/// <summary> /// Attacker issues one guess by picking an benign account at random and picking a password by weighted distribution /// </summary> public SimulatedLoginAttempt MaliciousLoginAttemptBreadthFirstAvoidMakingPopular(DateTime eventTimeUtc) { // Sometimes the attacker will miss and generate an invalid account name; bool invalidAccount = (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ProbabilityThatAttackerChoosesAnInvalidAccount); ulong breadthFirstAttemptCount; lock (_breadthFirstLock) { breadthFirstAttemptCount = _breadthFirstAttemptCounter; if (!invalidAccount) { _breadthFirstAttemptCounter++; } } // Start with the most common password and walk through all the accounts, // then move on to the next most common password. int passwordIndex = (int)((breadthFirstAttemptCount / (ulong)_experimentalConfiguration.MaxAttackerGuessesPerPassword)) % _simPasswords.OrderedListOfMostCommonPasswords.Count; int accountIndex = (int)(breadthFirstAttemptCount % (ulong)_simAccounts.BenignAccounts.Count); string mistake = invalidAccount ? "BadAccount" : ""; SimulatedUserAccount targetBenignAccount = invalidAccount ? null : _simAccounts.BenignAccounts[accountIndex]; string password = _simPasswords.OrderedListOfMostCommonPasswords[passwordIndex]; //SimulationTest _simulationtest = new SimulationTest(); return(new SimulatedLoginAttempt(targetBenignAccount, password, true, true, _ipPool.GetRandomMaliciousIp(), StrongRandomNumberGenerator.Get64Bits().ToString(), mistake, eventTimeUtc)); }
//public DecayingDouble AllFailures(TimeSpan halfLife) => AccountFailures.Add(halfLife, PasswordFailures); //public DecayingDouble AccountFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => AccountFailures.Subtract(halfLife, AccountFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithoutTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypo); //public DecayingDouble PasswordFailuresSubsetWithoutEitherFrequentPasswordOrTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypoAndFrequentPassword); /// <summary> /// This analysis will examine the client IP's previous failed attempts to login to this account /// to determine if any failed attempts were due to typos. /// </summary> /// <param name="account">The account that the client is currently trying to login to.</param> /// <param name="whenUtc"></param> /// <param name="correctPassword">The correct password for this account. (We can only know it because /// the client must have provided the correct one this loginAttempt.)</param> /// <returns></returns> public void AdjustBlockingScoreForPastTyposTreatedAsFullFailures( Simulator simulator, SimulatedUserAccount account, DateTime whenUtc, string correctPassword) { SimLoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos = RecentPotentialTypos.MostRecentFirst.ToArray(); foreach (SimLoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos) { if (account == null || potentialTypo.UsernameOrAccountId != account.UsernameOrAccountId) { continue; } // Use an edit distance calculation to determine if it was a likely typo bool likelyTypo = EditDistance.Calculate(potentialTypo.Password, correctPassword) <= simulator._experimentalConfiguration.BlockingOptions.MaxEditDistanceConsideredATypo; TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; DecayingDouble value = new DecayingDouble(1d, potentialTypo.WhenUtc); // Add this to the list of changed attempts if (potentialTypo.WasPasswordFrequent) { PasswordFailuresNoTypoFrequentPassword.SubtractInPlace(halfLife, value); PasswordFailuresTypoFrequentPassword.AddInPlace(halfLife, value); } RecentPotentialTypos.Remove(potentialTypo); } }
/// <summary> /// Attacker login with correct accounts he has, trying to fool our service into thinking his IP is benign /// </summary> /// <returns></returns> public SimulatedLoginAttempt MaliciousAttemptToSantiizeIpViaAValidLogin(IPAddress ipAddressToSanitizeThroughLogin) { SimulatedUserAccount simAccount = _simAccounts.GetMaliciousAccountAtRandomUniform(); return(new SimulatedLoginAttempt(simAccount, simAccount.Password, true, false, ipAddressToSanitizeThroughLogin, StrongRandomNumberGenerator.Get64Bits().ToString(), "", DateTime.UtcNow)); }
/// <summary> /// Attacker issues one guess by picking an benign account at random and picking a password by weighted distribution /// </summary> public SimulatedLoginAttempt MaliciousLoginAttemptWeighted(DateTime eventTimeUtc) { SimulatedUserAccount targetBenignAccount = (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ProbabilityThatAttackerChoosesAnInvalidAccount) ? null : _simAccounts.GetBenignAccountAtRandomUniform(); return(new SimulatedLoginAttempt( targetBenignAccount, _simPasswords.GetPasswordFromWeightedDistribution(), true, true, _ipPool.GetRandomMaliciousIp(), StrongRandomNumberGenerator.Get64Bits().ToString(), "", eventTimeUtc)); }
public SimulatedLoginAttempt(SimulatedUserAccount account, string password, bool isFromAttacker, bool isGuess, IPAddress clientAddress, string cookieProvidedByBrowser, string mistakeType, DateTime eventTimeUtc ) { SimAccount = account; UserNameOrAccountId = account != null ? account.UsernameOrAccountId : StrongRandomNumberGenerator.Get64Bits().ToString(); IsPasswordValid = account != null && account.Password == password; AddressOfClientInitiatingRequest = clientAddress; TimeOfAttemptUtc = eventTimeUtc; CookieProvidedByBrowser = cookieProvidedByBrowser; Password = password; IsFromAttacker = isFromAttacker; IsGuess = isGuess; MistakeType = mistakeType; }
/// <summary> /// Create accounts, generating passwords, primary IP /// </summary> #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public async Task GenerateAsync(ExperimentalConfiguration experimentalConfiguration, #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously //IUserAccountContextFactory accountContextFactory, CancellationToken cancellationToken = default(CancellationToken)) { _logger.WriteStatus("Creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); MemoryUserAccountController userAccountController = new MemoryUserAccountController();; ConcurrentBag <SimulatedUserAccount> benignSimulatedAccountBag = new ConcurrentBag <SimulatedUserAccount>(); // // Create benign accounts in parallel Parallel.For(0, (int)experimentalConfiguration.NumberOfBenignAccounts, (index) => { if (index > 0 && index % 10000 == 0) { _logger.WriteStatus("Created {0:N0} benign accounts", index); } SimulatedUserAccount userAccount = new SimulatedUserAccount() { UsernameOrAccountId = "user_" + index.ToString(), Password = _simPasswords.GetPasswordFromWeightedDistribution() }; userAccount.ClientAddresses.Add(_ipPool.GetNewRandomBenignIp()); userAccount.Cookies.Add(StrongRandomNumberGenerator.Get64Bits().ToString()); benignSimulatedAccountBag.Add(userAccount); double inverseFrequency = Distributions.GetLogNormal(0, 1); if (inverseFrequency < 0.01d) { inverseFrequency = 0.01d; } if (inverseFrequency > 50d) { inverseFrequency = 50d; } double frequency = 1 / inverseFrequency; lock (BenignAccountSelector) { BenignAccountSelector.AddItem(userAccount, frequency); } }); BenignAccounts = benignSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); // // Right after creating benign accounts we create IPs and accounts controlled by the attacker. // (We create the attacker IPs here, and not earlier, because we need to have the benign IPs generated in order to create overlap) _logger.WriteStatus("Creating attacker IPs"); _ipPool.GenerateAttackersIps(); _logger.WriteStatus("Creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); ConcurrentBag <SimulatedUserAccount> maliciousSimulatedAccountBag = new ConcurrentBag <SimulatedUserAccount>(); // // Create accounts in parallel Parallel.For(0, (int)experimentalConfiguration.NumberOfAttackerControlledAccounts, (index) => { SimulatedUserAccount userAccount = new SimulatedUserAccount() { UsernameOrAccountId = "attacker_" + index.ToString(), Password = _simPasswords.GetPasswordFromWeightedDistribution(), }; userAccount.ClientAddresses.Add(_ipPool.GetRandomMaliciousIp()); maliciousSimulatedAccountBag.Add(userAccount); }); AttackerAccounts = maliciousSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); // // Now create full UserAccount records for each simulated account and store them into the account context Parallel.ForEach(BenignAccounts.Union(AttackerAccounts), (simAccount, loopState) => { //if (loopState. % 10000 == 0) // _logger.WriteStatus("Created account {0:N0}", index); simAccount.CreditHalfLife = experimentalConfiguration.BlockingOptions.AccountCreditLimitHalfLife; simAccount.CreditLimit = experimentalConfiguration.BlockingOptions.AccountCreditLimit; foreach (string cookie in simAccount.Cookies) { userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBeforeAsync( simAccount, LoginAttempt.HashCookie(cookie), cancellationToken); } }); _logger.WriteStatus("Finished creating user accounts for each simluated account record"); }
//public DecayingDouble AllFailures(TimeSpan halfLife) => AccountFailures.Add(halfLife, PasswordFailures); //public DecayingDouble AccountFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => AccountFailures.Subtract(halfLife, AccountFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithInfrequentPassword(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithFrequentPassword); //public DecayingDouble PasswordFailuresSubsetWithoutTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypo); //public DecayingDouble PasswordFailuresSubsetWithoutEitherFrequentPasswordOrTypo(TimeSpan halfLife) => PasswordFailures.Subtract(halfLife, PasswordFailuresSubsetWithTypoAndFrequentPassword); /// <summary> /// This analysis will examine the client IP's previous failed attempts to login to this account /// to determine if any failed attempts were due to typos. /// </summary> /// <param name="account">The account that the client is currently trying to login to.</param> /// <param name="whenUtc"></param> /// <param name="correctPassword">The correct password for this account. (We can only know it because /// the client must have provided the correct one this loginAttempt.)</param> /// <returns></returns> public void AdjustBlockingScoreForPastTyposTreatedAsFullFailures( Simulator simulator, SimulatedUserAccount account, DateTime whenUtc, string correctPassword) { SimLoginAttemptSummaryForTypoAnalysis[] recentPotentialTypos = RecentPotentialTypos.MostRecentFirst.ToArray(); foreach (SimLoginAttemptSummaryForTypoAnalysis potentialTypo in recentPotentialTypos) { if (account == null || potentialTypo.UsernameOrAccountId != account.UsernameOrAccountId) continue; // Use an edit distance calculation to determine if it was a likely typo bool likelyTypo = EditDistance.Calculate(potentialTypo.Password, correctPassword) <= simulator._experimentalConfiguration.BlockingOptions.MaxEditDistanceConsideredATypo; TimeSpan halfLife = simulator._experimentalConfiguration.BlockingOptions.BlockScoreHalfLife; DecayingDouble value = new DecayingDouble(1d, potentialTypo.WhenUtc); // Add this to the list of changed attempts if (potentialTypo.WasPasswordFrequent) { PasswordFailuresNoTypoFrequentPassword.SubtractInPlace(halfLife, value); PasswordFailuresTypoFrequentPassword.AddInPlace(halfLife, value); } RecentPotentialTypos.Remove(potentialTypo); } }
/// <summary> /// Get a benign login attempt to simulate /// </summary> /// <returns></returns> public SimulatedLoginAttempt BenignLoginAttempt(DateTime eventTimeUtc) { // If there is a benign login attempt already scheduled to occur by now, // send it instaed lock (ScheduledBenignAttempts) { if (ScheduledBenignAttempts.Count > 0 && ScheduledBenignAttempts.First().TimeOfAttemptUtc < eventTimeUtc) { SimulatedLoginAttempt result = ScheduledBenignAttempts.First(); ScheduledBenignAttempts.Remove(result); return(result); } } string mistake = ""; //1. Pick a user at random SimulatedUserAccount account = _simAccounts.BenignAccountSelector.GetItemByWeightedRandom(); //2. Deal with cookies string cookie; // Add a new cookie if there are no cookies, or with if we haven't reached the max number of cookies and lose a roll of the dice if (account.Cookies.Count == 0 || (account.Cookies.Count < _experimentalConfiguration.MaxCookiesPerUserAccount && StrongRandomNumberGenerator.GetFraction() > _experimentalConfiguration.ChanceOfCoookieReUse)) { // We'll use the decimal represenation of a 64-bit unsigned integer as our cookie cookie = StrongRandomNumberGenerator.Get64Bits().ToString(); account.Cookies.Add(cookie); } else { // Use one of the user's existing cookies selected at random cookie = account.Cookies.ToArray()[(int)StrongRandomNumberGenerator.Get32Bits(account.Cookies.Count)]; } //Console.WriteLine("The user currently has " + account.Cookies.Count + " cookies. Using: " + cookie); //3. Choose an IP address for the login // 1/3 of times login with the primary IP address, otherwise, choose an IP randomly from the benign IP pool IPAddress clientIp; if (account.ClientAddresses.Count == 0 || (account.ClientAddresses.Count < _experimentalConfiguration.MaxIpPerUserAccount && StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfIpReUse)) { // Use a new IP for the user account.ClientAddresses.Add(clientIp = _ipPool.GetNewRandomBenignIp()); } else { // Use one of the user's existing IP Addresses selected at random clientIp = account.ClientAddresses.ToArray()[(int)StrongRandomNumberGenerator.Get32Bits(account.ClientAddresses.Count)]; } string password = account.Password; // // Add benign failures // An automated client begins a string of login attempts using an old (stale) password if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfLongRepeatOfStalePassword) { // To cause this client to be out of date, we'll change the password here. string newPassword = _simPasswords.GetPasswordFromWeightedDistribution(); _userAccountController.SetPassword(account, newPassword, account.Password); mistake += "StalePassword"; // Schedule all the future failed attempts a fixed distance aparat lock (ScheduledBenignAttempts) { double additionalMistakes = 0; DateTime currentTimeUtc = eventTimeUtc; for (additionalMistakes = 1; additionalMistakes < _experimentalConfiguration.LengthOfLongRepeatOfOldPassword; additionalMistakes++) { currentTimeUtc = currentTimeUtc.AddSeconds(_experimentalConfiguration.MinutesBetweenLongRepeatOfOldPassword); ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, mistake, currentTimeUtc)); } for (uint correctLogins = 1; correctLogins < _experimentalConfiguration.LengthOfLongRepeatOfOldPassword; correctLogins++) { currentTimeUtc = currentTimeUtc.AddSeconds(_experimentalConfiguration.MinutesBetweenLongRepeatOfOldPassword); ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, newPassword, false, false, clientIp, cookie, mistake, currentTimeUtc)); } } } // The benign user may mistype her password causing a typo if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfBenignPasswordTypo) { mistake += "Typo"; // Typos tend to come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatTypo) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, AddTypoToPassword(password), false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds(_experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after the string of typos ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (1 + additionalMistakes)))); } // Put the typo into the password for the first typo failure, to be returned by this function. password = AddTypoToPassword(password); } // The benign user may mistakenly use a password for another of her accounts, which we draw from same distribution // we used to generate user account passwords if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfAccidentallyUsingAnotherAccountPassword) { mistake += "WrongPassword"; // Choices of the wrong account password may come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatUseOfPasswordFromAnotherAccount) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, _simPasswords.GetPasswordFromWeightedDistribution(), false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds(_experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after mistakes ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (additionalMistakes + 1)))); } // Make the current request have the wrong password password = _simPasswords.GetPasswordFromWeightedDistribution(); } // The benign user may mistype her account name, and land on someone else's account name if (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfBenignAccountNameTypoResultingInAValidUserName) { mistake += "WrongAccountName"; // Choices of the wrong account password may come in clusters, and are hopefully followed by a correct login // Add additional typos to the schedule of future benign attempts and then a submission of the correct password lock (ScheduledBenignAttempts) { double additionalMistakes = 0; while (StrongRandomNumberGenerator.GetFraction() < _experimentalConfiguration.ChanceOfRepeatWrongAccountName) { ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( _simAccounts.GetBenignAccountAtRandomUniform(), password, false, false, clientIp, cookie, mistake, eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * ++additionalMistakes))); } // Add a correct login after mistakes ScheduledBenignAttempts.Add(new SimulatedLoginAttempt( account, password, false, false, clientIp, cookie, "", eventTimeUtc.AddSeconds( _experimentalConfiguration.DelayBetweenRepeatBenignErrorsInSeconds * (additionalMistakes + 1)))); // Make the current request have the wrong account name account = _simAccounts.GetBenignAccountAtRandomUniform(); } } return(new SimulatedLoginAttempt(account, password, false, false, clientIp, cookie, mistake, eventTimeUtc)); }
/// <summary> /// Create accounts, generating passwords, primary IP /// </summary> #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public async Task GenerateAsync(ExperimentalConfiguration experimentalConfiguration, #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously //IUserAccountContextFactory accountContextFactory, CancellationToken cancellationToken = default(CancellationToken)) { _logger.WriteStatus("Creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); MemoryUserAccountController userAccountController = new MemoryUserAccountController();; ConcurrentBag<SimulatedUserAccount> benignSimulatedAccountBag = new ConcurrentBag<SimulatedUserAccount>(); // // Create benign accounts in parallel Parallel.For(0, (int) experimentalConfiguration.NumberOfBenignAccounts, (index) => { if (index > 0 && index % 10000 == 0) _logger.WriteStatus("Created {0:N0} benign accounts", index); SimulatedUserAccount userAccount = new SimulatedUserAccount() { UsernameOrAccountId = "user_" + index.ToString(), Password = _simPasswords.GetPasswordFromWeightedDistribution() }; userAccount.ClientAddresses.Add(_ipPool.GetNewRandomBenignIp()); userAccount.Cookies.Add(StrongRandomNumberGenerator.Get64Bits().ToString()); benignSimulatedAccountBag.Add(userAccount); double inverseFrequency = Distributions.GetLogNormal(0, 1); if (inverseFrequency < 0.01d) inverseFrequency = 0.01d; if (inverseFrequency > 50d) inverseFrequency = 50d; double frequency = 1 / inverseFrequency; lock (BenignAccountSelector) { BenignAccountSelector.AddItem(userAccount, frequency); } }); BenignAccounts = benignSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); // // Right after creating benign accounts we create IPs and accounts controlled by the attacker. // (We create the attacker IPs here, and not earlier, because we need to have the benign IPs generated in order to create overlap) _logger.WriteStatus("Creating attacker IPs"); _ipPool.GenerateAttackersIps(); _logger.WriteStatus("Creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); ConcurrentBag<SimulatedUserAccount> maliciousSimulatedAccountBag = new ConcurrentBag<SimulatedUserAccount>(); // // Create accounts in parallel Parallel.For(0, (int) experimentalConfiguration.NumberOfAttackerControlledAccounts, (index) => { SimulatedUserAccount userAccount = new SimulatedUserAccount() { UsernameOrAccountId = "attacker_" + index.ToString(), Password = _simPasswords.GetPasswordFromWeightedDistribution(), }; userAccount.ClientAddresses.Add(_ipPool.GetRandomMaliciousIp()); maliciousSimulatedAccountBag.Add(userAccount); }); AttackerAccounts = maliciousSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); // // Now create full UserAccount records for each simulated account and store them into the account context Parallel.ForEach(BenignAccounts.Union(AttackerAccounts), (simAccount, loopState) => { //if (loopState. % 10000 == 0) // _logger.WriteStatus("Created account {0:N0}", index); simAccount.CreditHalfLife = experimentalConfiguration.BlockingOptions.AccountCreditLimitHalfLife; simAccount.CreditLimit = experimentalConfiguration.BlockingOptions.AccountCreditLimit; foreach (string cookie in simAccount.Cookies) userAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBeforeAsync( simAccount, LoginAttempt.HashCookie(cookie), cancellationToken); }); _logger.WriteStatus("Finished creating user accounts for each simluated account record"); }
/// <summary> /// Create accounts, generating passwords, primary IP /// </summary> public void Generate(ExperimentalConfiguration experimentalConfiguration) { SimulatedUserAccountController simUserAccountController = new SimulatedUserAccountController(); _logger.WriteStatus("Creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); ConcurrentBag <SimulatedUserAccount> benignSimulatedAccountBag = new ConcurrentBag <SimulatedUserAccount>(); // // Create benign accounts in parallel Parallel.For(0, (int)experimentalConfiguration.NumberOfBenignAccounts, (index) => { if (index > 0 && index % 10000 == 0) { _logger.WriteStatus("Created {0:N0} benign accounts", index); } SimulatedUserAccount userAccount = simUserAccountController.Create( "user_" + index.ToString(), _simPasswords.GetPasswordFromWeightedDistribution() ); userAccount.ClientAddresses.Add(_ipPool.GetNewRandomBenignIp()); string initialCookie = StrongRandomNumberGenerator.Get64Bits().ToString(); userAccount.Cookies.Add(initialCookie); userAccount.HashesOfCookiesOfClientsThatHaveSuccessfullyLoggedIntoThisAccount[initialCookie] = true; benignSimulatedAccountBag.Add(userAccount); double inverseFrequency = Distributions.GetLogNormal(0, 1); if (inverseFrequency < 0.01d) { inverseFrequency = 0.01d; } if (inverseFrequency > 50d) { inverseFrequency = 50d; } double frequency = 1 / inverseFrequency; lock (BenignAccountSelector) { BenignAccountSelector.AddItem(userAccount, frequency); } }); BenignAccounts = benignSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} benign accounts", experimentalConfiguration.NumberOfBenignAccounts); // // Right after creating benign accounts we create IPs and accounts controlled by the attacker. // (We create the attacker IPs here, and not earlier, because we need to have the benign IPs generated in order to create overlap) _logger.WriteStatus("Creating attacker IPs"); _ipPool.GenerateAttackersIps(); _logger.WriteStatus("Creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); ConcurrentBag <SimulatedUserAccount> maliciousSimulatedAccountBag = new ConcurrentBag <SimulatedUserAccount>(); // // Create accounts in parallel Parallel.For(0, (int)experimentalConfiguration.NumberOfAttackerControlledAccounts, (index) => { SimulatedUserAccount userAccount = simUserAccountController.Create( "attacker_" + index.ToString(), _simPasswords.GetPasswordFromWeightedDistribution()); userAccount.ClientAddresses.Add(_ipPool.GetRandomMaliciousIp()); maliciousSimulatedAccountBag.Add(userAccount); }); AttackerAccounts = maliciousSimulatedAccountBag.ToList(); _logger.WriteStatus("Finished creating {0:N0} attacker accounts", experimentalConfiguration.NumberOfAttackerControlledAccounts); // // Now create full UserAccount records for each simulated account and store them into the account context Parallel.ForEach(BenignAccounts.Union(AttackerAccounts), (simAccount, loopState) => { //if (loopState. % 10000 == 0) // _logger.WriteStatus("Created account {0:N0}", index); simAccount.CreditHalfLife = experimentalConfiguration.BlockingOptions.AccountCreditLimitHalfLife; simAccount.CreditLimit = experimentalConfiguration.BlockingOptions.AccountCreditLimit; foreach (string cookie in simAccount.Cookies) { simUserAccountController.HasClientWithThisHashedCookieSuccessfullyLoggedInBefore( simAccount, LoginAttempt.HashCookie(cookie)); } }); _logger.WriteStatus("Finished creating user accounts for each simluated account record"); }