public void TestParseRC4CryptoAPIFilePassRecord() { byte[] rc4CryptoApiFilePassRecord = new byte[] { 0x2F, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x43, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x67, 0x00, 0x72, 0x00, 0x61, 0x00, 0x70, 0x00, 0x68, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x76, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x98, 0xC3, 0xA1, 0xA0, 0x9A, 0xC8, 0x92, 0x3D, 0x7A, 0x5D, 0x03, 0x30, 0x34, 0xE8, 0x67, 0x64, 0xA8, 0xA9, 0x29, 0xF0, 0xA6, 0xA8, 0xB6, 0xA1, 0x1F, 0x8C, 0x6F, 0x27, 0xB6, 0xA8, 0x82, 0xEB, 0x14, 0x00, 0x00, 0x00, 0xE8, 0xAF, 0x8B, 0x67, 0x4E, 0x3F, 0xA7, 0xA1, 0x39, 0x9D, 0x9F, 0x2D, 0xDC, 0xB5, 0x1A, 0x33, 0x5D, 0x8A, 0xB2, 0x69 }; VirtualStreamReader vsr = new VirtualStreamReader(new MemoryStream(rc4CryptoApiFilePassRecord)); FilePass filePassRecord = new FilePass(vsr, (RecordType)vsr.ReadUInt16(), vsr.ReadUInt16()); byte[] filePassBytes = filePassRecord.GetBytes(); Assert.AreEqual(rc4CryptoApiFilePassRecord.Length, filePassBytes.Length); Assert.IsFalse(filePassRecord.encryptionHeader.fDocProps); Assert.IsFalse(filePassRecord.encryptionHeader.fCryptoAPI); Assert.IsTrue(filePassRecord.encryptionHeader.fExternal); Assert.IsTrue(filePassRecord.encryptionHeader.fAES); for (int offset = 0; offset < rc4CryptoApiFilePassRecord.Length; offset += 1) { Assert.AreEqual(rc4CryptoApiFilePassRecord[offset], filePassBytes[offset]); } }
public FilePass CreateFilePassFromPassword(string password) { ushort key = CreateXorKey_Method1(password); ushort verify = CreatePasswordVerifier_Method1(password); return(FilePass.CreateXORObfuscationFilePass(key, verify)); }
public void GenerateFilePassBytesForPasswords() { XorObfuscation xorObfuscation = new XorObfuscation(); byte[] xorArray = xorObfuscation.CreateXorArray_Method1(XorObfuscation.DefaultPassword); string xorString = BitConverter.ToString(xorArray).Replace("-", " "); Console.WriteLine(xorString); string[] passwords = new[] { "000", "111", "222", "333", "444", "555", "5 5 5", "666", "777", "7 7 7", "888", "999", "123", "321", "987", "876", "543", "321", "135", "246", "357", "468", "579", "1234", "2020", "2021", "password", "password1", "infected", "infected2" }; foreach (var password in passwords) { Console.WriteLine(string.Format("FilePass Bytes for {0}", password)); FilePass fp = xorObfuscation.CreateFilePassFromPassword(password); Console.WriteLine(fp.ToHexDumpString(16, false, true)); } }
/// <summary> /// Dumps information about BIFF records that are relevant for analysis. Defaults to sheet, label, and formula data. /// </summary> /// <param name="path">Path to the XLS file to dump</param> /// <param name="dumpAll">Dump all BIFF records, not the most commonly used by maldocs</param> /// <param name="showAttrInfo">Explicitly display PtgAttr information in Formula strings. Defaults to False.</param> /// <param name="dumpHexBytes">Dump the byte content of each BIFF record in addition to its content summary.</param> /// <param name="password">XOR Obfuscation decryption password to try. Defaults to VelvetSweatshop if FilePass record is found.</param> /// <param name="disableDecryption">Use this flag in order to skip decryption of the file before dumping.</param> public static void Dump(FileInfo path, bool dumpAll = false, bool showAttrInfo = false, bool dumpHexBytes = false, string password = "******", bool disableDecryption = false) { if (path == null) { Console.WriteLine("path argument must be specified in Dump mode. Run dump -h for usage instructions."); return; } if (path.Exists == false) { Console.WriteLine("path file does not exist."); return; } WorkbookStream wbs = new WorkbookStream(path.FullName); if (wbs.HasPasswordToOpen() && !disableDecryption) { FilePass fpRecord = wbs.GetAllRecordsByType <FilePass>().First(); if (fpRecord.wEncryptionType == 0 && fpRecord.xorObfuscationKey != 0) { XorObfuscation xorObfuscation = new XorObfuscation(); Console.WriteLine("FilePass record found - attempting to decrypt with password " + password); try { wbs = xorObfuscation.DecryptWorkbookStream(wbs, password); } catch (ArgumentException argEx) { Console.WriteLine("Password " + password + " does not match the verifier value of the document FilePass. Try a different password."); return; } } else if (fpRecord.wEncryptionType == 1 && fpRecord.vMajor > 1) { Console.WriteLine("FilePass record for CryptoAPI Found - Currently Unsupported."); string verifierSalt = BitConverter.ToString(fpRecord.encryptionVerifier.Salt).Replace("-", ""); string verifier = BitConverter.ToString(fpRecord.encryptionVerifier.EncryptedVerifier).Replace("-", ""); string verifierHash = BitConverter.ToString(fpRecord.encryptionVerifier.EncryptedVerifierHash).Replace("-", ""); Console.WriteLine("Salt is: " + verifierSalt); Console.WriteLine("Vrfy is: " + verifier); Console.WriteLine("vHsh is: " + verifierHash); Console.WriteLine("Algo is: " + string.Format("{0:x8}", fpRecord.encryptionHeader.AlgID)); } else if (fpRecord.wEncryptionType == 1 && fpRecord.vMajor == 1) { Console.WriteLine("FilePass record for RC4 Binary Document Encryption Found - Currently Unsupported."); } } int numBytesToDump = 0; if (dumpHexBytes) { numBytesToDump = 0x1000; } if (dumpAll) { List <BiffRecord> records; WorkbookStream fullStream = new WorkbookStream(PtgHelper.UpdateGlobalsStreamReferences(wbs.Records)); records = fullStream.Records; foreach (var record in records) { Console.WriteLine(record.ToHexDumpString(numBytesToDump, showAttrInfo)); } } else { string dumpString = RecordHelper.GetRelevantRecordDumpString(wbs, dumpHexBytes, showAttrInfo); Console.WriteLine(dumpString); } }
public byte[] TransformWorkbookBytes(byte[] bytes, ObfuscationMode mode, string password = XorObfuscation.DefaultPassword) { VirtualStreamReader vsr = new VirtualStreamReader(new MemoryStream(bytes)); MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); while (vsr.BaseStream.Position < vsr.BaseStream.Length) { BiffHeader bh; bh.id = (RecordType)vsr.ReadUInt16(); //Handle case where RecordId is empty if (bh.id == 0) { break; } bh.length = vsr.ReadUInt16(); //Taken from https://social.msdn.microsoft.com/Forums/en-US/3dadbed3-0e68-4f11-8b43-3a2328d9ebd5/xls-xor-data-transformation-method-1 byte XorArrayIndex = (byte)((vsr.BaseStream.Position + bh.length) % 16); //We remove the FilePass Record for the decrypted document if (mode == ObfuscationMode.Decrypt && bh.id == RecordType.FilePass) { //Skip the remaining FilePass bytes ushort encryptionMode = vsr.ReadUInt16(); if (encryptionMode != 0) { throw new NotImplementedException("FilePass EncryptionMode of " + encryptionMode + " is unsupported."); } ushort key = vsr.ReadUInt16(); ushort verify = vsr.ReadUInt16(); ushort passwordVerify = CreatePasswordVerifier_Method1(password); if (verify != passwordVerify) { throw new ArgumentException( "Incorrect decryption password. Try bruteforcing the password with another tool."); } continue; } ; bw.Write(Convert.ToUInt16(bh.id)); bw.Write(Convert.ToUInt16(bh.length)); //If we're encrypting, then use the byte writer for our current position rather than the read stream if (mode == ObfuscationMode.Encrypt) { XorArrayIndex = (byte)((bw.BaseStream.Position + bh.length) % 16); } //Nothing to decrypt for 0 length if (bh.length == 0) { continue; } switch (bh.id) { case RecordType.BOF: case RecordType.FilePass: case RecordType.UsrExcl: case RecordType.FileLock: case RecordType.InterfaceHdr: case RecordType.RRDInfo: case RecordType.RRDHead: byte[] recordBytes = vsr.ReadBytes(bh.length); bw.Write(recordBytes); //If this is the first BOF record, we inject the appropriate FilePass record if (mode == ObfuscationMode.Encrypt && bh.id == RecordType.BOF && vsr.BaseStream.Position == (bh.length + 4)) { ushort key = CreateXorKey_Method1(password); ushort verify = CreatePasswordVerifier_Method1(password); FilePass filePass = new FilePass(key, verify); byte[] filePassBytes = filePass.GetBytes(); bw.Write(filePassBytes); } continue; case RecordType.BoundSheet8: //Special Case - don't encrypt/decrypt the lbPlyPos Field uint lbPlyPos = vsr.ReadUInt32(); //For encryption we need to adjust this by the added FilePass record length //Decryption we auto-fix afterwards, but encrypted entries don't auto-fix well // if (mode == ObfuscationMode.Encrypt) // { // lbPlyPos += 10; // } bw.Write(lbPlyPos); //Since we are skipping lbPlyPos, we need to update the XorArrayIndex offset as well XorArrayIndex = (byte)((XorArrayIndex + 4) % 16); byte[] remainingBytes = vsr.ReadBytes(bh.length - 4); if (mode == ObfuscationMode.Decrypt) { byte[] decryptedBytes = DecryptData_Method1(password, remainingBytes, XorArrayIndex); bw.Write(decryptedBytes); } else { byte[] encryptedBytes = EncryptData_Method1(password, remainingBytes, XorArrayIndex); bw.Write(encryptedBytes); } continue; default: byte[] preTransformBytes = vsr.ReadBytes(bh.length); if (mode == ObfuscationMode.Decrypt) { byte[] decBytes = DecryptData_Method1(password, preTransformBytes, XorArrayIndex); bw.Write(decBytes); } else { byte[] encBytes = EncryptData_Method1(password, preTransformBytes, XorArrayIndex); bw.Write(encBytes); } continue; } } return(bw.GetBytesWritten()); }