Difficulty
easy
Categories
rev
Description

Feel like you have been transported back in time by looking at this lovely .NET WinForms application.

Note: This challenge only runs on windows.

Author
NoRelect
Attachments
dino-configurator.tar.gz

Solution

It’s a .NET reversing challenge.

Using ILSpy, we can quickly export the code:

using System;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using DinoConfigurator.Properties;

namespace DinoConfigurator
{
	public class Form1 : Form
	{
		private IContainer components;
		private Label lblUsername;
		private TextBox txtUsername;
		private TextBox txtPassword;
		private Label label1;
		private Button btnLogin;
		public Form1()
		{
			InitializeComponent();
		}
		private void Form1_Load(object sender, EventArgs e)
		{
		}
		private void btnLogin_Click(object sender, EventArgs e)
		{
			string text = ((Control)txtUsername).get_Text();
			string text2 = ((Control)txtPassword).get_Text();
			((Control)btnLogin).set_Enabled(false);
			((Control)txtUsername).set_Enabled(false);
			((Control)txtPassword).set_Enabled(false);
			if (Verifier.ValidateUser(text, text2))
			{
				byte[] cipher = new byte[48]
				{
					36, 88, 62, 118, 95, 27, 184, 109, 197, 203,
					174, 99, 161, 147, 15, 218, 124, 44, 246, 37,
					66, 31, 189, 75, 235, 13, 198, 243, 64, 85,
					39, 120, 214, 3, 107, 52, 129, 90, 167, 204,
					251, 229, 178, 219, 181, 209, 30, 1
				};
				string text3 = CryptoUtils.Decrypt("908194288470cb5d", cipher, text2);
				MessageBox.Show("In reality, I have created this login prompt to hide that I haven't yet fully implemented the app.\nTake this flag in the meantime:\n\n" + text3, "Under construction", (MessageBoxButtons)0);
			}
			else
			{
				MessageBox.Show("Invalid username/password. Try again.", "Error", (MessageBoxButtons)0);
			}
			((Control)btnLogin).set_Enabled(true);
			((Control)txtUsername).set_Enabled(true);
			((Control)txtPassword).set_Enabled(true);
		}
		protected override void Dispose(bool disposing)
		{
			if (disposing && components != null)
			{
				components.Dispose();
			}
			((Form)this).Dispose(disposing);
		}
		private void InitializeComponent()
		{
			lblUsername = new Label();
			txtUsername = new TextBox();
			txtPassword = new TextBox();
			label1 = new Label();
			btnLogin = new Button();
			((Control)this).SuspendLayout();
			((Control)lblUsername).set_AutoSize(true);
			((Control)lblUsername).set_Location(new Point(12, 9));
			((Control)lblUsername).set_Name("lblUsername");
			((Control)lblUsername).set_Size(new Size(58, 13));
			((Control)lblUsername).set_TabIndex(0);
			((Control)lblUsername).set_Text("Username:");
			((Control)txtUsername).set_Location(new Point(15, 25));
			((Control)txtUsername).set_Name("txtUsername");
			((Control)txtUsername).set_Size(new Size(200, 20));
			((Control)txtUsername).set_TabIndex(1);
			((Control)txtPassword).set_Location(new Point(15, 64));
			((Control)txtPassword).set_Name("txtPassword");
			((Control)txtPassword).set_Size(new Size(200, 20));
			((Control)txtPassword).set_TabIndex(3);
			txtPassword.set_UseSystemPasswordChar(true);
			((Control)label1).set_AutoSize(true);
			((Control)label1).set_Location(new Point(12, 48));
			((Control)label1).set_Name("label1");
			((Control)label1).set_Size(new Size(56, 13));
			((Control)label1).set_TabIndex(2);
			((Control)label1).set_Text("Password:");
			((Control)btnLogin).set_Location(new Point(140, 90));
			((Control)btnLogin).set_Name("btnLogin");
			((Control)btnLogin).set_Size(new Size(75, 23));
			((Control)btnLogin).set_TabIndex(4);
			((Control)btnLogin).set_Text("Login");
			((ButtonBase)btnLogin).set_UseVisualStyleBackColor(true);
			((Control)btnLogin).add_Click((EventHandler)btnLogin_Click);
			((ContainerControl)this).set_AutoScaleDimensions(new SizeF(6f, 13f));
			((ContainerControl)this).set_AutoScaleMode((AutoScaleMode)1);
			((Control)this).set_BackgroundImage((Image)Resources.matteo_discardi_tdA2J1uJHew_unsplash);
			((Control)this).set_BackgroundImageLayout((ImageLayout)4);
			((Form)this).set_ClientSize(new Size(800, 450));
			((Control)this).get_Controls().Add((Control)(object)btnLogin);
			((Control)this).get_Controls().Add((Control)(object)txtPassword);
			((Control)this).get_Controls().Add((Control)(object)label1);
			((Control)this).get_Controls().Add((Control)(object)txtUsername);
			((Control)this).get_Controls().Add((Control)(object)lblUsername);
			((Control)this).set_Name("Form1");
			((Form)this).set_ShowIcon(false);
			((Control)this).set_Text("Dino Configurator");
			((Form)this).add_Load((EventHandler)Form1_Load);
			((Control)this).ResumeLayout(false);
			((Control)this).PerformLayout();
		}
	}
	internal static class Program
	{
		[STAThread]
		private static void Main()
		{
			Application.EnableVisualStyles();
			Application.SetCompatibleTextRenderingDefault(false);
			Application.Run((Form)(object)new Form1());
		}
	}
	internal class CryptoUtils
	{
		internal static bool VerifySecret(int seed, string input, byte[] secret)
		{
			if (input.Length != secret.Length)
			{
				return false;
			}
			Bitmap matteo_discardi_tdA2J1uJHew_unsplash = Resources.matteo_discardi_tdA2J1uJHew_unsplash;
			Random random = new Random(seed);
			bool result = true;
			for (int i = 0; i < secret.Length; i++)
			{
				Color pixel = Resources.matteo_discardi_tdA2J1uJHew_unsplash.GetPixel(random.Next(matteo_discardi_tdA2J1uJHew_unsplash.Width), random.Next(matteo_discardi_tdA2J1uJHew_unsplash.Height));
				byte b = (byte)(pixel.R ^ pixel.G ^ pixel.B ^ pixel.A ^ random.Next(255));
				if (((byte)input[i] ^ b) != secret[i])
				{
					result = false;
				}
			}
			return result;
		}

		internal static string Decrypt(string iv, byte[] cipher, string key)
		{
			Aes aes = Aes.Create();
			aes.Mode = CipherMode.CBC;
			aes.IV = Encoding.UTF8.GetBytes(iv);
			aes.Key = Encoding.UTF8.GetBytes(key);
			ICryptoTransform cryptoTransform = aes.CreateDecryptor();
			return Encoding.UTF8.GetString(cryptoTransform.TransformFinalBlock(cipher, 0, cipher.Length));
		}
	}
	internal class Random
	{
		private const uint Multiplier = 1664525u;
		private const uint Increment = 1013904223u;
		private uint _state;
		public Random(int seed)
		{
			_state = (uint)seed;
		}
		public Random(uint seed)
		{
			_state = seed;
		}
		public uint NextUInt()
		{
			_state = 1664525 * _state + 1013904223;
			return _state;
		}
		public int Next(int maxValue)
		{
			if (maxValue <= 0)
			{
				throw new ArgumentOutOfRangeException("maxValue", "maxValue must be positive.");
			}
			return (int)(NextUInt() % (uint)maxValue);
		}
		public double NextDouble()
		{
			return (double)NextUInt() / 4294967296.0;
		}
	}
	internal class Verifier
	{
		internal static bool ValidateUser(string username, string password)
		{
			if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
			{
				return false;
			}
			if (!CryptoUtils.VerifySecret(1337, username, new byte[32]
			{
				185, 118, 150, 153, 92, 18, 192, 188, 152, 183,
				200, 123, 254, 68, 171, 145, 173, 83, 187, 49,
				199, 190, 40, 112, 204, 0, 24, 101, 120, 103,
				118, 166
			}))
			{
				return false;
			}
			if (!CryptoUtils.VerifySecret(42, password, new byte[32]
			{
				139, 85, 46, 194, 113, 17, 172, 175, 217, 83,
				168, 234, 216, 15, 194, 44, 195, 7, 132, 251,
				87, 55, 176, 102, 229, 212, 142, 175, 33, 61,
				137, 55
			}))
			{
				return false;
			}
			return true;
		}
	}
}
namespace DinoConfigurator.Properties
{
	[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
	[DebuggerNonUserCode]
	[CompilerGenerated]
	internal class Resources
	{
		private static ResourceManager resourceMan;
		private static CultureInfo resourceCulture;
		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static ResourceManager ResourceManager
		{
			get
			{
				if (resourceMan == null)
				{
					resourceMan = new ResourceManager("DinoConfigurator.Properties.Resources", typeof(Resources).Assembly);
				}
				return resourceMan;
			}
		}

		[EditorBrowsable(EditorBrowsableState.Advanced)]
		internal static CultureInfo Culture
		{
			get
			{
				return resourceCulture;
			}
			set
			{
				resourceCulture = value;
			}
		}

		internal static Bitmap matteo_discardi_tdA2J1uJHew_unsplash => (Bitmap)ResourceManager.GetObject("matteo-discardi-tdA2J1uJHew-unsplash", resourceCulture);

		internal Resources()
		{
		}
	}
	[CompilerGenerated]
	[GeneratedCode("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
	internal sealed class Settings : ApplicationSettingsBase
	{
		private static Settings defaultInstance = (Settings)(object)SettingsBase.Synchronized((SettingsBase)(object)new Settings());
		public static Settings Default => defaultInstance;
	}
}

We can see that the program loads a Bitmap as a key. When I tried to reimplement this using the .NET core runtime, I got errors, so I had to pivot to using Mono to write my script.

Mostly, the challenge is just extracting the PNG file and copy the program functions together. I rewrote the VerifySecret to a getSecret method and simply loaded the PNG from the current directory:

using System;
using System.Drawing;
using System.Security.Cryptography;
using System.Text;
using System.Drawing;

public class Random
{
	private const uint Multiplier = 1664525u;

	private const uint Increment = 1013904223u;

	private uint _state;

	public Random(int seed) {
		_state = (uint)seed;
	}

	public Random(uint seed) {
		_state = seed;
	}

	public uint NextUInt() {
		_state = 1664525 * _state + 1013904223;
		return _state;
	}

	public int Next(int maxValue) {
		return (int)(NextUInt() % (uint)maxValue);
	}

	public double NextDouble() {
		return (double)NextUInt() / 4294967296.0;
	}
}


public class Program {
    // opposite of VerifySecret
    static byte[] getSecret(int seed, byte[] secret) {
        Bitmap matteo_discardi_tdA2J1uJHew_unsplash = new Bitmap("./00000018.png");
        Random random = new Random(seed);
        byte[] input = new byte[secret.Length];;
        bool result = true;
        for (int i = 0; i < secret.Length; i++) {
            Color pixel = matteo_discardi_tdA2J1uJHew_unsplash.GetPixel(random.Next(matteo_discardi_tdA2J1uJHew_unsplash.Width), random.Next(matteo_discardi_tdA2J1uJHew_unsplash.Height));
            byte b = (byte)(pixel.R ^ pixel.G ^ pixel.B ^ pixel.A ^ random.Next(255));
            input[i] = (byte)(secret[i] ^ b);
        }
        return input;
    }

    static string Decrypt(string iv, byte[] cipher, string key) {
            Aes aes = Aes.Create();
            aes.Mode = CipherMode.CBC;
            aes.IV = Encoding.UTF8.GetBytes(iv);
            aes.Key = Encoding.UTF8.GetBytes(key);
            ICryptoTransform cryptoTransform = aes.CreateDecryptor();
            return Encoding.UTF8.GetString(cryptoTransform.TransformFinalBlock(cipher, 0, cipher.Length));
    }

    static int Main() {
        byte[] cipher = new byte[48]
                {
                    36, 88, 62, 118, 95, 27, 184, 109, 197, 203,
                    174, 99, 161, 147, 15, 218, 124, 44, 246, 37,
                    66, 31, 189, 75, 235, 13, 198, 243, 64, 85,
                    39, 120, 214, 3, 107, 52, 129, 90, 167, 204,
                    251, 229, 178, 219, 181, 209, 30, 1
                };
        byte[] flag = getSecret(42, new byte[32]
                {
                    139, 85, 46, 194, 113, 17, 172, 175, 217, 83,
                    168, 234, 216, 15, 194, 44, 195, 7, 132, 251,
                    87, 55, 176, 102, 229, 212, 142, 175, 33, 61,
                    137, 55
                });
        string dec = Decrypt("908194288470cb5d", cipher,  System.Text.Encoding.Default.GetString(flag) );
        Console.WriteLine(dec);

        return 0;
    }
}
mcs Program.cs -pkg:dotnet
./Program.exe

Flag:

dach2026{3asy_d0tn3t_r3v_bcdc578b18b946e5}