mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-06 17:10:13 +00:00
Implement enhanced support for SteamParental
- Detect if SteamParental is active - Verify if the code supplied by user is valid - If the code is not valid or not supplied, generate it automatically (through local bruteforcing, pending further speed enhancements) It's really sad seeing how this is possible, Valve seriously, you couldn't dedicate one single request in the backend to verify that? You could easily implement rate-limiting on invalid attempts and make parental more secure, but with the current implementation it's a security joke.
This commit is contained in:
@@ -20,15 +20,27 @@
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using CryptSharp.Utility;
|
||||
using SteamKit2;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
public static class ArchiCryptoHelper {
|
||||
private static byte[] EncryptionKey = Encoding.UTF8.GetBytes(nameof(ArchiSteamFarm));
|
||||
|
||||
internal static string BruteforceSteamParentalCode(byte[] passwordHash, byte[] salt, bool derivedKey = true) {
|
||||
if ((passwordHash == null) || (salt == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return derivedKey ? BruteforceSteamParentalCodeDerived(passwordHash, salt) : BruteforceSteamParentalCodePbkdf2(passwordHash, salt);
|
||||
}
|
||||
|
||||
internal static string Decrypt(ECryptoMethod cryptoMethod, string encrypted) {
|
||||
if (!Enum.IsDefined(typeof(ECryptoMethod), cryptoMethod) || string.IsNullOrEmpty(encrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(cryptoMethod) + " || " + nameof(encrypted));
|
||||
@@ -81,6 +93,76 @@ namespace ArchiSteamFarm {
|
||||
EncryptionKey = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
|
||||
private static string BruteforceSteamParentalCodeDerived(byte[] passwordHash, byte[] salt) {
|
||||
if ((passwordHash == null) || (salt == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] password = new byte[4];
|
||||
|
||||
for (char a = '0'; a <= '9'; a++) {
|
||||
password[0] = (byte) a;
|
||||
|
||||
for (char b = '0'; b <= '9'; b++) {
|
||||
password[1] = (byte) b;
|
||||
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
password[2] = (byte) c;
|
||||
|
||||
for (char d = '0'; d <= '9'; d++) {
|
||||
password[3] = (byte) d;
|
||||
|
||||
byte[] passwordHashTry = SCrypt.ComputeDerivedKey(password, salt, 8192, 8, 1, null, passwordHash.Length);
|
||||
|
||||
if (passwordHashTry.SequenceEqual(passwordHash)) {
|
||||
return Encoding.UTF8.GetString(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string BruteforceSteamParentalCodePbkdf2(byte[] passwordHash, byte[] salt) {
|
||||
if ((passwordHash == null) || (salt == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(passwordHash) + " || " + nameof(salt));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] password = new byte[4];
|
||||
|
||||
using (KeyedHashAlgorithm hmacAlgorithm = KeyedHashAlgorithm.Create()) {
|
||||
for (char a = '0'; a <= '9'; a++) {
|
||||
password[0] = (byte) a;
|
||||
|
||||
for (char b = '0'; b <= '9'; b++) {
|
||||
password[1] = (byte) b;
|
||||
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
password[2] = (byte) c;
|
||||
|
||||
for (char d = '0'; d <= '9'; d++) {
|
||||
password[3] = (byte) d;
|
||||
|
||||
byte[] passwordHashTry = Pbkdf2.ComputeDerivedKey(hmacAlgorithm, salt, 10000, passwordHash.Length);
|
||||
|
||||
if (passwordHashTry.SequenceEqual(passwordHash)) {
|
||||
return Encoding.UTF8.GetString(password);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string DecryptAES(string encrypted) {
|
||||
if (string.IsNullOrEmpty(encrypted)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(encrypted));
|
||||
|
||||
@@ -29,6 +29,7 @@ using System.Threading.Tasks;
|
||||
using ArchiSteamFarm.CMsgs;
|
||||
using ArchiSteamFarm.Localization;
|
||||
using ArchiSteamFarm.NLog;
|
||||
using CryptSharp.Utility;
|
||||
using JetBrains.Annotations;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
@@ -43,6 +44,7 @@ namespace ArchiSteamFarm {
|
||||
private readonly SteamUnifiedMessages.UnifiedService<IClanChatRooms> UnifiedClanChatRoomsService;
|
||||
private readonly SteamUnifiedMessages.UnifiedService<IEcon> UnifiedEconService;
|
||||
private readonly SteamUnifiedMessages.UnifiedService<IFriendMessages> UnifiedFriendMessagesService;
|
||||
private readonly SteamUnifiedMessages.UnifiedService<IParental> UnifiedParentalService;
|
||||
private readonly SteamUnifiedMessages.UnifiedService<IPlayer> UnifiedPlayerService;
|
||||
|
||||
internal DateTime LastPacketReceived { get; private set; }
|
||||
@@ -57,6 +59,7 @@ namespace ArchiSteamFarm {
|
||||
UnifiedClanChatRoomsService = steamUnifiedMessages.CreateService<IClanChatRooms>();
|
||||
UnifiedEconService = steamUnifiedMessages.CreateService<IEcon>();
|
||||
UnifiedFriendMessagesService = steamUnifiedMessages.CreateService<IFriendMessages>();
|
||||
UnifiedParentalService = steamUnifiedMessages.CreateService<IParental>();
|
||||
UnifiedPlayerService = steamUnifiedMessages.CreateService<IPlayer>();
|
||||
}
|
||||
|
||||
@@ -629,6 +632,86 @@ namespace ArchiSteamFarm {
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
internal async Task<(bool IsSteamParentalEnabled, string SteamParentalCode)?> ValidateSteamParental(string steamParentalCode = null) {
|
||||
if (!Client.IsConnected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CParental_GetParentalSettings_Request request = new CParental_GetParentalSettings_Request { steamid = Client.SteamID };
|
||||
|
||||
SteamUnifiedMessages.ServiceMethodResponse response;
|
||||
|
||||
try {
|
||||
response = await UnifiedParentalService.SendMessage(x => x.GetParentalSettings(request));
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericWarningException(e);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
ArchiLogger.LogNullError(nameof(response));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.Result != EResult.OK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CParental_GetParentalSettings_Response body = response.GetDeserializedResponse<CParental_GetParentalSettings_Response>();
|
||||
|
||||
if (!body.settings.is_enabled) {
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
bool derivedKey;
|
||||
|
||||
switch (body.settings.passwordhashtype) {
|
||||
case 4:
|
||||
derivedKey = false;
|
||||
|
||||
break;
|
||||
case 6:
|
||||
derivedKey = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(body.settings.passwordhashtype), body.settings.passwordhashtype));
|
||||
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
if ((steamParentalCode != null) && (steamParentalCode.Length > 4)) {
|
||||
byte i = 0;
|
||||
byte[] password = new byte[steamParentalCode.Length];
|
||||
|
||||
foreach (char character in steamParentalCode) {
|
||||
if ((character < '0') || (character > '9')) {
|
||||
break;
|
||||
}
|
||||
|
||||
password[i++] = (byte) character;
|
||||
}
|
||||
|
||||
if (i >= steamParentalCode.Length) {
|
||||
byte[] passwordHash = derivedKey ? SCrypt.ComputeDerivedKey(password, body.settings.salt, 8192, 8, 1, null, body.settings.passwordhash.Length) : SCrypt.GetEffectivePbkdf2Salt(password, body.settings.salt, 8192, 8, 1, null);
|
||||
|
||||
if (passwordHash.SequenceEqual(body.settings.passwordhash)) {
|
||||
return (true, steamParentalCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.PleaseWait);
|
||||
|
||||
steamParentalCode = ArchiCryptoHelper.BruteforceSteamParentalCode(body.settings.passwordhash, body.settings.salt, derivedKey);
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.Done);
|
||||
|
||||
return (true, steamParentalCode);
|
||||
}
|
||||
|
||||
private void HandleItemAnnouncements(IPacketMsg packetMsg) {
|
||||
if (packetMsg == null) {
|
||||
ArchiLogger.LogNullError(nameof(packetMsg));
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="4.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="CryptSharpStandard" Version="1.0.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.10" />
|
||||
<PackageReference Include="Humanizer" Version="2.6.2" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
|
||||
|
||||
@@ -2417,16 +2417,24 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(BotConfig.SteamParentalCode) && (BotConfig.SteamParentalCode.Length != 4)) {
|
||||
string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false);
|
||||
(bool isSteamParentalEnabled, string steamParentalCode)? steamParental = await ArchiHandler.ValidateSteamParental(BotConfig.SteamParentalCode).ConfigureAwait(false);
|
||||
|
||||
if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != 4)) {
|
||||
Stop();
|
||||
if (steamParental?.isSteamParentalEnabled == true) {
|
||||
if (!string.IsNullOrEmpty(steamParental.Value.steamParentalCode)) {
|
||||
if (BotConfig.SteamParentalCode != steamParental.Value.steamParentalCode) {
|
||||
SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParental.Value.steamParentalCode);
|
||||
}
|
||||
} else {
|
||||
string steamParentalCode = await Logging.GetUserInput(ASF.EUserInputType.SteamParentalCode, BotName).ConfigureAwait(false);
|
||||
|
||||
break;
|
||||
if (string.IsNullOrEmpty(steamParentalCode) || (steamParentalCode.Length != 4)) {
|
||||
Stop();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode);
|
||||
}
|
||||
|
||||
SetUserInput(ASF.EUserInputType.SteamParentalCode, steamParentalCode);
|
||||
}
|
||||
|
||||
ArchiWebHandler.OnVanityURLChanged(callback.VanityURL);
|
||||
|
||||
Reference in New Issue
Block a user