Cracking SmarterMail hashes

This week I encountered a password hash I hadnt seen in a while. Base64 with a twist. SmarterTools.com has a mail server called SmarterMail written in all .net. It stores its passwords in xml files in what looks to be base64, but not quite.

Unlike most companies, I assume SmarterTools doesn’t know how to encrypt or obfuscate its binaries, instead, they store all of their code in a managed assembly DLL and call it with an exe. That said, reversing it was rather simple since I only needed a decent decompiler and grep to find its hashing algorithm. And here it is:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace TicketCounter
{
public class CryptographyHelper
{
protected internal byte[] salt = new byte[4]
{
(byte) 155,
(byte) 26,
(byte) 93,
(byte) 86
};
protected SymmetricAlgorithm Coder;
protected byte[] Key;
protected byte[] IV;
protected int Method;
protected CryptographyHelper()
{
}
public CryptographyHelper(int methodIn)
{
this.Method = methodIn;
if (this.Method == 0)
this.Coder = (SymmetricAlgorithm) DES.Create();
else
this.Coder = (SymmetricAlgorithm) RC2.Create();
}
public void SetKey(string key)
{
int cb = this.Method != 0 ? 5 : 8;
PasswordDeriveBytes passwordDeriveBytes = new PasswordDeriveBytes(key, this.salt);
this.Key = passwordDeriveBytes.GetBytes(cb);
this.IV = passwordDeriveBytes.GetBytes(cb);
}
public void SetKey(ref Random rnd)
{
int length = this.Method != 0 ? 5 : 8;
this.Key = new byte[length];
this.IV = new byte[length];
rnd.NextBytes(this.Key);
rnd.NextBytes(this.IV);
}
public void SetKey(byte[] keyIn, byte[] ivIn)
{
int length = this.Method != 0 ? 5 : 8;
this.Key = new byte[length];
this.IV = new byte[length];
Array.Copy((Array) keyIn, 0, (Array) this.Key, 0, length);
Array.Copy((Array) ivIn, 0, (Array) this.IV, 0, length);
}
public string EncodeToBase64(string val)
{
if (val.Length == 0)
return val;
else
return Convert.ToBase64String(this.Encode(Encoding.UTF8.GetBytes(val)));
}
public string DecodeFromBase64(string val)
{
if (val.Length == 0)
return val;
else
return Encoding.UTF8.GetString(this.Decode(Convert.FromBase64String(val)));
}
public static string EncodeToBase64(int method, int key, string val)
{
if (val.Length == 0)
return val;
CryptographyHelper cryptographyHelper = new CryptographyHelper(method);
Random rnd = new Random(key);
cryptographyHelper.SetKey(ref rnd);
return cryptographyHelper.EncodeToBase64(val);
}
public static string DecodeFromBase64(int method, int key, string val)
{
if (val.Length == 0)
return val;
CryptographyHelper cryptographyHelper = new CryptographyHelper(method);
Random rnd = new Random(key);
cryptographyHelper.SetKey(ref rnd);
return cryptographyHelper.DecodeFromBase64(val);
}
public static string EncodeToBase64(int method, string key, string val)
{
if (val.Length == 0)
return val;
CryptographyHelper cryptographyHelper = new CryptographyHelper(method);
cryptographyHelper.SetKey(key);
return cryptographyHelper.EncodeToBase64(val);
}
public static string DecodeFromBase64(int method, string key, string val)
{
if (val.Length == 0)
return val;
CryptographyHelper cryptographyHelper = new CryptographyHelper(method);
cryptographyHelper.SetKey(key);
return cryptographyHelper.DecodeFromBase64(val);
}
public byte[] Encode(byte[] buf)
{
return this.PassThrough(buf, this.Coder.CreateEncryptor(this.Key, this.IV));
}
public byte[] Decode(byte[] buf)
{
return this.PassThrough(buf, this.Coder.CreateDecryptor(this.Key, this.IV));
}
protected byte[] PassThrough(byte[] buf, ICryptoTransform transformation)
{
MemoryStream memoryStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, transformation, CryptoStreamMode.Write);
cryptoStream.Write(buf, 0, buf.Length);
cryptoStream.FlushFinalBlock();
memoryStream.Seek(0L, SeekOrigin.Begin);
byte[] buffer = new byte[memoryStream.Length];
memoryStream.Read(buffer, 0, (int) memoryStream.Length);
cryptoStream.Close();
memoryStream.Close();
return buffer;
}
}
}

See the key?  No you don’t because the key was elsewhere in the app – specifically in the config area that deals with description:


//namespace SmarterMail.Config
public class SystemAdminLogin
{
public static string PasswordKey = 03a8ur98qhfa9h; //  assume quotes
public string ID { get; set; }
public string Username { get; set; }
public List<IpAccess> IpAccessRestrictions { get; private set; }
public bool IsPrimaryAdmin { get; set; }
public bool EnableIpAccessRestriction { get; set; }
public DateTime DateCreated { get; set; }
public string Description { get; set; }
public string Password
{
get
{
return CryptographyHelper.
DecodeFromBase64(0,SystemAdminLogin.PasswordKey, this.get_EncryptedPassword());
}
set
{
this.set_EncryptedPassword(CryptographyHelper.EncodeToBase64(0,SystemAdminLogin.PasswordKey, value));
}
}static SystemAdminLogin()
{
}public SystemAdminLogin()
{
this.IpAccessRestrictions = new List<IpAccess>();
this.DateCreated = DateTime.Now;
this.ID = string.Empty;
}public void ToXml(XmlWriter writer)
{
writer.WriteStartElement(“SystemAdmin”);
writer.WriteElementString(“ID”, this.ID);
writer.WriteElementString(“Username”, this.Username);
// ISSUE: reference to a compiler-generated method
writer.WriteElementString(“Password”, this.get_EncryptedPassword());
writer.WriteElementString(“EnableIpAccessRestriction”,
this.EnableIpAccessRestriction.ToString());
writer.WriteElementString(“DateCreated”,
this.DateCreated.ToString((IFormatProvider)
CultureInfo.InvariantCulture));
writer.WriteElementString(“Description”, this.Description);
if (this.IpAccessRestrictions.Count > 0)
{
writer.WriteStartElement(“IpAccess”);
foreach (IpAccess ipAccess in this.IpAccessRestrictions)
{
writer.WriteStartElement(“Allowed”);
writer.WriteElementString(“Description”, ipAccess.Description);
writer.WriteElementString(“StartIp”, ipAccess.StartIP);
if (System_ExtensionMethods7BCA73B06BAB478aA3AC6AC60979BA25.HasValue(ipAccess.EndIP))
writer.WriteElementString(“EndIp”, ipAccess.EndIP);
writer.WriteElementString(“Type”, ((int) ipAccess.Type).ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
public void FromXml(XmlReader reader)
{
string str = string.Empty;
label_27:
while (reader.Read())
{
int num = (int) reader.MoveToContent();
if (reader.NodeType == XmlNodeType.EndElement &&
reader.Name.ToLowerInvariant() == “systemadmin”)
break;
if (reader.NodeType == XmlNodeType.Element &&
reader.Name.ToLowerInvariant() == “ipaccess”)
{
IpAccess ipAccess = new IpAccess();
bool flag = false;
while (true)
{
do
{
if (reader.Read() && (reader.NodeType !=
XmlNodeType.EndElement || !(reader.Name.ToLowerInvariant() ==
“ipaccess”)))
{
if (reader.NodeType == XmlNodeType.EndElement &&
reader.Name.ToLowerInvariant() == “allowed” && flag)
{
this.IpAccessRestrictions.Add(ipAccess);
flag = false;
ipAccess = new IpAccess();
}
if (reader.NodeType == XmlNodeType.Element)
goto label_8;
}
else
goto label_27;
}
while (reader.NodeType != XmlNodeType.Text);
goto label_10;
label_8:
str = reader.Name.ToLowerInvariant();
continue;
label_10:
switch (str)
{
case “description”:
flag = true;
ipAccess.Description = reader.Value;
continue;
case “startip”:
flag = true;
ipAccess.StartIP = reader.Value;
continue;
case “endip”:
flag = true;
ipAccess.EndIP = reader.Value;
continue;
case “type”:
flag = true;
ipAccess.Type = (IPType) int.Parse(reader.Value);
continue;
default:
continue;
}
}
}
else if (reader.NodeType == XmlNodeType.Element)
str = reader.Name.ToLowerInvariant();
else if (reader.NodeType == XmlNodeType.Text)
{
switch (str)
{
case “id”:
this.ID = reader.Value;
continue;
case “enableipaccessrestriction”:
this.EnableIpAccessRestriction = bool.Parse(reader.Value);
continue;
case “username”:
this.Username = reader.Value;
continue;
case “password”:
// ISSUE: reference to a compiler-generated method
this.set_EncryptedPassword(reader.Value);
continue;
case “datecreated”:
this.DateCreated = DateTime.Parse(reader.Value,
(IFormatProvider) CultureInfo.InvariantCulture);
continue;
case “description”:
this.Description = reader.Value;
continue;
default:
continue;
}
}
}
}
}

That’s what we’re going to use to decrypt the binary.

I borrowed their code to make my own little decrypter app kind of like how a cracker makes a keygen – same principle really, except in this case, they include a function for decrypting so I don’t need much:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace TicketCounter
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static string PasswordKey = “03a8ur98qhfa9h”;
private void btnDecode_Click(object sender, EventArgs e)
{
string enc = this.tbEnc.Text;
this.tbDec.Text = DecodeFromBase64(0, PasswordKey, enc);
}
public static string DecodeFromBase64(int method, string key, string val)
{
if (val.Length == 0)
return val;
CryptographyHelper cryptographyHelper = new CryptographyHelper(method);
cryptographyHelper.SetKey(key);
return cryptographyHelper.DecodeFromBase64(val);
}
}
}

Create the object and use it. Keep it simple. The project includes the class I ripped in the project.

And there you have it. Next time you take control of a Smarter Mail server, you can now decrypt the passwords with ease.

For those of you who are lazy, you can download the project here.

Happy hacking 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *