Dans ce challenge, on nous fourni une application Windows Phone (qui est un paquet Silverlight). Un paquet Silverlight est une archive au format ZIP avec l’extension .XAP. Décompressez cette archive devrait vous donnez une multitude de fichier, ainsi qu'un appelé ndh_stay-alive.dll.
Ce fichier est une librairie programmé en .NET. Ouvrez le avec un désassembleur .NET, pour pouvoir lire le code. À partir du moment ou cette assembly n'est pas protégé, ce challenge va être trivial. J'ai utilisé SAE (Simple Assembly Explorer) pour extraire le code, je l'ai nettoyé et voici ce que je trouve:
using Microsoft.Phone.Controls; using System; using System.Diagnostics; using System.IO; using System.Security.Cryptography; using System.Text; using System.Windows; using System.Windows.Controls; namespace ndh_stay_alive { public class MainPage : PhoneApplicationPage { private string hashA223UU8DzZDZD = "+5jeyeCv5aejnwa2dd5L7LvIjM4nDFyhhLY+Nj5Rh3wdZFL8Mi3hltRicAalWPuLRQMk91pag4dfWfnj7nDiJB13grBCyFMf2pJwP8b2BQ5"; internal Grid LayoutRoot; internal Grid ContentPanel; internal TextBlock appTitle; internal Image ndh_logo; internal PasswordBox ndhPassword; internal Button ndhPasswordLogin; private bool _contentLoaded; public MainPage() { InitializeComponent(); } private void ndhPasswordLogin_Click(object sender, RoutedEventArgs e) { string text = this.ndhPassword.get_Password(); if (text.Length < 0) { text = cryptPassword(text); if (string.Equals(text, trustedHash())) { base.get_NavigationService().Navigate(new Uri("/LoginError.xaml", UriKind.Relative)); return; } MessageBox.Show("Login failed, try again.", "Error", 0); } } private string trustedHashPart1() { return "/8hd3Z1ds8a1StSQkXxNyjZd2mTQPpTR4zmyf9FzK4Y4XUfzw1hUP3qj+"; } private string trustedHashPart2() { string text = "+R2kbpJn0tqCMmUMZzFwQFNU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3QHpy6M2kY"; return text.Replace(text, ""); } private string trustedHashPart3() { return "+R2kbpJn0tqCMmUNZzFwQFMU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3QHpy6M2kY"; } private string trustedHash() { string text = "dGyKgNjQmXdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PV5ZqJtz1E7EBzqaXxTxDOE9V5ZqiPArIoY9Rl//8RalprV5ZqBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br"; text = text.Replace("V5Zq", "w"); return this.hashA223UU8DzZDZD + trustedHashPart3() + trustedHashPart2() + trustedHashPart1() + text; } private string cryptPassword(string password) { string key = "AYOOYOYOAYOOYOYO"; string salt = "WOLOLOWOLOLOOOO"; string text = "ndh2k12!"; string key2 = null; string text2 = null; for (int i = 0; i < password.Length; i++) { string text3 = password[i].ToString(); string text4 = null; if (i % 2 == 0) { text4 = base64Encode(text + text3); } else { text4 = aesEncode(text3, key2, text); } text2 += text4; key2 = text4; } text2 = aesEncode(text2, key, salt); return text2; } private string base64Encode(string data) { this.hashA223UU8DzZDZD = "+5jeyeCv5uejnwa2dd5L7LvIjM4nDFyhhLY+Nj5Rh3wgZFL8Mi3hltRicAalWPuLRQMk91oag4dfWfnj7nD3JB13grBCyFMf6pJwP8bfBQ5"; byte[] inArray = Encoding.UTF8.GetBytes(data); return Convert.ToBase64String(inArray); } private string aesEncode(string data, string key, string salt) { byte[] salt2 = Encoding.UTF8.GetBytes(salt); Aes aes = new AesManaged(); Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(key, salt2); aes.Key = rfc2898DeriveBytes.GetBytes(16); aes.IV = aes.Key; MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write); byte[] bytes = Encoding.UTF8.GetBytes(data); cryptoStream.Write(bytes, 0, bytes.Length); cryptoStream.FlushFinalBlock(); return Convert.ToBase64String(memoryStream.ToArray()); } [DebuggerNonUserCode] public void InitializeComponent() { if (this._contentLoaded) { return; } this._contentLoaded = true; Application.LoadComponent(this, new Uri("/ndh_stay-alive;component/MainPage.xaml", UriKind.Relative)); this.LayoutRoot = (Grid)base.FindName("LayoutRoot"); this.ContentPanel = (Grid)base.FindName("ContentPanel"); this.appTitle = (TextBlock)base.FindName("appTitle"); this.ndh_logo = (Image)base.FindName("ndh_logo"); this.ndhPassword = (PasswordBox)base.FindName("ndhPassword"); this.ndhPasswordLogin = (Button)base.FindName("ndhPasswordLogin"); } } }
Nous allons le nettoyé un peu plus et supprimé tous le code inutile pour avoir une vue claire du code. La fonction ndhPasswordLogin_Click est une fonction évenementielle, elle est appelé quand un utilisateur appui sur le bouton Login.
Cette fonction appelle une fonction de cryptage et compare ce résultat avec une chaîne chiffré par cette même fonction (on imagine bien). Cette chaîne n'est pas donné directement, le programme utilise plusieurs fonctions pour la récupéré. Récupérons la… !
La fonction base64Encode() remplace la variable hashA223UU8DzZDZD avec une nouvelle valeur:
this.hashA223UU8DzZDZD = "+5jeyeCv5uejnwa2dd5L7LvIjM4nDFyhhLY+Nj5Rh3wgZFL8Mi3hltRicAalWPuLRQMk91oag4dfWfnj7nD3JB13grBCyFMf6pJwP8bfBQ5";
Quand la fonction trustedHash() est appelé, le programme retourne cette valeur ainsi que 4 autres.
La variable text contient:
string text = "dGyKgNjQmXdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PV5ZqJtz1E7EBzqaXxTxDOE9V5ZqiPArIoY9Rl//8RalprV5ZqBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br"; text = text.Replace("V5Zq", "w");
dGyKgNjQmXdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PwJtz1E7EBzqaXxTxDOE9wiPArIoY9Rl//8RalprwBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br
La fonction trustedHashPart3() retourne:
+R2kbpJn0tqCMmUNZzFwQFMU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3QHpy6M2kY
La fonction trustedHashPart2() ne retourne rien:
return text.Replace(text, ""); // null
Et enfin, trustedHashPart1() retourne:
/8hd3Z1ds8a1StSQkXxNyjZd2mTQPpTR4zmyf9FzK4Y4XUfzw1hUP3qj+
La chaîne correct est ainsi une concaténation de c'est 5 valeurs dans l'ordre suivant:
hashA223UU8DzZDZD + trustedHashPart3() + trustedHashPart2() + trustedHashPart1() + text</font> La chaîne est donc: <file C# source.cs>+5jeyeCv5uejnwa2dd5L7LvIjM4nDFyhhLY+Nj5Rh3wgZFL8Mi3hltRicAalWPuLRQMk91oag4dfWfnj7nD3JB13grBCyFMf6pJwP8bfBQ5+R2kbpJn0tqCMmUNZzFwQFMU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3QHpy6M2kY/8hd3Z1ds8a1StSQkXxNyjZd2mTQPpTR4zmyf9FzK4Y4XUfzw1hUP3qj+dGyKgNjQmXdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PwJtz1E7EBzqaXxTxDOE9wiPArIoY9Rl//8RalprwBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br
Si nous la décryptons, nous avons le flag. J'ai supprimé le code qui était inutile et l'ai modifié pour qu'il puisse fonctionner dans un programme C#.NET type console.
using System; using System.Diagnostics; using System.IO; using System.Security.Cryptography; using System.Text; namespace ndh_stay_alive { public class Program { public static string sTrustedPassword = "+5jeyeCv5uejnwa2dd5L7LvIjM4nDFyhhLY+Nj5Rh3wgZFL8Mi3hltRicAalWPuLRQMk91oag4dfWfnj7nD3JB13grBCyFMf6pJwP8bfBQ5+R2kbpJn0tqCMmUNZzFwQFMU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3QHpy6M2kY/8hd3Z1ds8a1StSQkXxNyjZd2mTQPpTR4zmyf9FzK4Y4XUfzw1hUP3qj+dGyKgNjQmXdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PwJtz1E7EBzqaXxTxDOE9wiPArIoY9Rl//8RalprwBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br"; public static void Main(string[] args) { login(Console.ReadLine()); } public static void login(string sPassword) { string sCrypted = null; if (sPassword.Length > 0) { sCrypted = cryptPassword(sPassword); if (sCrypted == sTrustedPassword) { Console.WriteLine("Done!"); } Console.WriteLine("Failed!"); } } public static string cryptPassword(string password) { string key = "AYOOYOYOAYOOYOYO"; string salt = "WOLOLOWOLOLOOOO"; string text = "ndh2k12!"; string key2 = null; string text2 = null; for (int i = 0; i < password.Length; i++) { string text3 = password[i].ToString(); string text4 = null; if (i % 2 == 0) { text4 = base64Encode(text + text3); } else { text4 = aesEncode(text3, key2, text); } text2 += text4; key2 = text4; } text2 = aesEncode(text2, key, salt); return text2; } public static string base64Encode(string data) { byte[] inArray = Encoding.UTF8.GetBytes(data); return Convert.ToBase64String(inArray); } public static string aesEncode(string data, string key, string salt) { byte[] salt2 = Encoding.UTF8.GetBytes(salt); Aes aes = new AesManaged(); Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(key, salt2); aes.Key = rfc2898DeriveBytes.GetBytes(16); aes.IV = aes.Key; MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write); byte[] bytes = Encoding.UTF8.GetBytes(data); cryptoStream.Write(bytes, 0, bytes.Length); cryptoStream.FlushFinalBlock(); return Convert.ToBase64String(memoryStream.ToArray()); } } }
Nous devons écrire la fonction inverse de cryptPassword() (donc une fonction de décryptage). Pour cela, nous devons écrire les fonction aesDecode() et base64Decode():
public static string aesDecode(string data, string key, string salt) { byte[] salt2 = Encoding.UTF8.GetBytes(salt); Aes aes = new AesManaged(); Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(key, salt2); aes.Key = rfc2898DeriveBytes.GetBytes(16); aes.IV = aes.Key; MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write); byte[] bytes = Convert.FromBase64String(data); cryptoStream.Write(bytes, 0, bytes.Length); cryptoStream.FlushFinalBlock(); return Encoding.UTF8.GetString(memoryStream.ToArray()); } public static string base64Decode(string data) { return Encoding.UTF8.GetString(Convert.FromBase64String(data)); }
Et maintenant, au tour de la fonction decryptPassword() elle même:
public static string decryptPassword(string password) { string key = "AYOOYOYOAYOOYOYO"; string salt = "WOLOLOWOLOLOOOO"; string text = "ndh2k12!"; string key2 = null; string text2 = null; string decode = null; bool first = true; decode = aesDecode(password, key, salt); for (int i = 0; i < decode.Length; i += 12) { string text4 = null; if (first) { text4 = base64Decode(decode.Substring(i, 12)).Substring(text.Length, 1); } else { text4 = aesDecode(decode.Substring(i, 24), key2, text); i += 12; } text2 += text4; key2 = base64Encode(text + text4); first = !first; } return text2; }
Essayons la !
public static void login(string sPassword) { string sCrypted = null; if (sPassword.Length > 0) { sCrypted = cryptPassword(sPassword); Console.WriteLine("Crypted: " + sCrypted); Console.WriteLine("Decrypted: " + decryptPassword(sCrypted)); Console.Write("Result: "); if (sCrypted == sTrustedPassword) { Console.WriteLine("Done!"); } Console.WriteLine("Failed!"); } }
Xartrick Crypted: vZX3k8Y4T0V0ti0rLwtChRXShHSOlgJD7gxtm5J0Z/uXhRYX21SDpS9xuuqTJPs1HHxTww2rptZW mA2DgWFRrs8GODtiwbhVrSfbCcuIyeG9nkPVOlv2UAvCBiDDWeSWeeV7DnL339dt0gkhlehDWNLEpbFD/D9zZ xinyDhzOf7PA+wCYlkZTMPi3cIsGAWymSAalixVRkX80xDFulA4ZQ== Decrypted: Xartrick Result: Failed!
La fonction fonctionne à merveille, nous avons plus qu'à décrypté la variable qui contient le bon mot de passe pour avoir le flag !
hzv-will-never-die
Xartrick, 2012.