Created
July 1, 2025 08:00
-
-
Save Myrkie/05359014e9db1092bf26951f75be1755 to your computer and use it in GitHub Desktop.
Csharp implementation for decrypting and encrypting FTPRush RushSite.xml passwords
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Text; | |
using Org.BouncyCastle.Crypto.Parameters; | |
using Org.BouncyCastle.Crypto.Digests; | |
using Org.BouncyCastle.Crypto.Engines; | |
namespace ExtractRushPass | |
{ | |
public static class FtpRushCrypto | |
{ | |
private static readonly byte[] Secret = "th3m3ugaysshit9"u8.ToArray(); | |
private static readonly byte[] IvInit = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; | |
// Derived keys and IVs - computed once at class init | |
private static readonly byte[] Key; // 32 bytes | |
private static readonly byte[] Iv; // 8 bytes | |
private static readonly byte[] IvShort; // 8 bytes | |
static FtpRushCrypto() | |
{ | |
var ripemd256 = new RipeMD256Digest(); | |
ripemd256.BlockUpdate(Secret, 0, Secret.Length); | |
Key = new byte[32]; | |
ripemd256.DoFinal(Key, 0); | |
var blowfishEngine = new BlowfishEngine(); | |
blowfishEngine.Init(true, new KeyParameter(Key)); | |
Iv = new byte[8]; | |
blowfishEngine.ProcessBlock(IvInit, 0, Iv, 0); | |
IvShort = new byte[8]; | |
blowfishEngine.ProcessBlock(Iv, 0, IvShort, 0); | |
} | |
/// <summary> | |
/// Encrypts plainPassword to FTPRush encrypted hex password string. | |
/// Returns uppercase hex string. | |
/// </summary> | |
public static string EncryptPassword(string plainPassword) | |
{ | |
ArgumentNullException.ThrowIfNull(plainPassword); | |
byte[] passBytes = Encoding.ASCII.GetBytes(plainPassword); | |
int ctLength = passBytes.Length; | |
switch (ctLength) | |
{ | |
case 0: | |
return ""; | |
case < 8: | |
{ | |
// Short password XOR with IvShort | |
byte[] ct = new byte[ctLength]; | |
for (int i = 0; i < ctLength; i++) | |
ct[i] = (byte)(passBytes[i] ^ IvShort[i]); | |
return Convert.ToHexString(ct); | |
} | |
default: | |
{ | |
// Long password encryption with custom CBC | |
int fullBlocksLen = ctLength - ctLength % 8; | |
int modulus = ctLength % 8; | |
byte[] cbcIv = new byte[8]; | |
Array.Copy(Iv, cbcIv, 8); | |
var blowfishEngine = new BlowfishEngine(); | |
blowfishEngine.Init(true, new KeyParameter(Key)); | |
byte[] ct = new byte[ctLength]; | |
// Encrypt full blocks in custom CBC mode | |
for (int blockIndex = 0; blockIndex < fullBlocksLen; blockIndex += 8) | |
{ | |
byte[] ivOld = new byte[8]; | |
Array.Copy(cbcIv, ivOld, 8); | |
for (int i = 0; i < 8; i++) | |
{ | |
cbcIv[i] ^= passBytes[blockIndex + i]; | |
} | |
blowfishEngine.ProcessBlock(cbcIv, 0, ct, blockIndex); | |
for (int i = 0; i < 8; i++) | |
{ | |
cbcIv[i] = (byte)(ivOld[i] ^ ct[blockIndex + i]); | |
} | |
} | |
if (modulus <= 0) return Convert.ToHexString(ct); | |
{ | |
byte[] ivModulus = new byte[8]; | |
blowfishEngine.ProcessBlock(cbcIv, 0, ivModulus, 0); | |
// XOR the last partial bytes with ivModulus | |
for (int i = 0; i < modulus; i++) | |
{ | |
ct[fullBlocksLen + i] = (byte)(passBytes[fullBlocksLen + i] ^ ivModulus[i]); | |
} | |
} | |
return Convert.ToHexString(ct); | |
} | |
} | |
} | |
/// <summary> | |
/// Decrypts an FTPRush encrypted hex password string. | |
/// Returns plaintext string. | |
/// </summary> | |
public static string DecryptPassword(string encryptedHex) | |
{ | |
ArgumentNullException.ThrowIfNull(encryptedHex); | |
if (encryptedHex.Length % 2 != 0) | |
throw new ArgumentException("Invalid encrypted hex string length."); | |
int ctLength = encryptedHex.Length / 2; | |
byte[] ptBytes = Convert.FromHexString(encryptedHex); | |
switch (ctLength) | |
{ | |
case 0: | |
return ""; | |
case < 8: | |
{ | |
// Short password XOR with IvShort | |
byte[] pt = new byte[ctLength]; | |
for (int i = 0; i < ctLength; i++) | |
pt[i] = (byte)(ptBytes[i] ^ IvShort[i]); | |
return Encoding.ASCII.GetString(pt); | |
} | |
default: | |
{ | |
// Long password decryption with custom CBC | |
int fullBlocksLen = ctLength - ctLength % 8; | |
int modulus = ctLength % 8; | |
byte[] cbcIv = new byte[8]; | |
Array.Copy(Iv, cbcIv, 8); | |
var blowfishEngine = new BlowfishEngine(); | |
blowfishEngine.Init(false, new KeyParameter(Key)); | |
byte[] pt = new byte[ctLength]; | |
// Decrypt full blocks with custom CBC | |
for (int blockIndex = 0; blockIndex < fullBlocksLen; blockIndex += 8) | |
{ | |
byte[] tmp = new byte[8]; | |
blowfishEngine.ProcessBlock(ptBytes, blockIndex, tmp, 0); | |
for (int i = 0; i < 8; i++) | |
{ | |
pt[blockIndex + i] = (byte)(tmp[i] ^ cbcIv[i]); | |
} | |
for (int i = 0; i < 8; i++) | |
{ | |
cbcIv[i] ^= ptBytes[blockIndex + i]; | |
} | |
} | |
if (modulus <= 0) return Encoding.ASCII.GetString(pt); | |
{ | |
byte[] ivModulus = new byte[8]; | |
blowfishEngine.Init(true, new KeyParameter(Key)); | |
blowfishEngine.ProcessBlock(cbcIv, 0, ivModulus, 0); | |
// XOR the last partial bytes with ivModulus | |
for (int i = 0; i < modulus; i++) | |
{ | |
pt[fullBlocksLen + i] = (byte)(ptBytes[fullBlocksLen + i] ^ ivModulus[i]); | |
} | |
} | |
return Encoding.ASCII.GetString(pt); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment