[Wargame.kr] Crypto Crackme Basic 풀이 (398p)
Wargame/wargame.kr

[Wargame.kr] Crypto Crackme Basic 풀이 (398p)

풀이

Simple Reverse Engineering.
Can you Reversing for C# Application?

패스워드를 찾아보세요 xD (닷넷 프레임워크 2.0이 필요합니다.)

find the password xD (you need .net framework 2.0)


C#이라니.. dotPeek을 이용해 프로그램을 열어 소스코드를 확인해봤다.

프로그램의 흐름을 따라가면서 코드를 이해해보자.

using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;

namespace my_sharp_prac
{
  internal class Program
  {
    private static string myEncrypt(string strKey, string name)
    {
      DESCryptoServiceProvider cryptoServiceProvider = new DESCryptoServiceProvider();
      cryptoServiceProvider.Mode = CipherMode.ECB;
      cryptoServiceProvider.Padding = PaddingMode.PKCS7;
      byte[] bytes1 = Encoding.ASCII.GetBytes(Program.mPadding(name));
      cryptoServiceProvider.Key = bytes1;
      cryptoServiceProvider.IV = bytes1;
      MemoryStream memoryStream = new MemoryStream();
      CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, cryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write);
      byte[] bytes2 = Encoding.UTF8.GetBytes(strKey.ToCharArray());
      cryptoStream.Write(bytes2, 0, bytes2.Length);
      cryptoStream.FlushFinalBlock();
      return Convert.ToBase64String(memoryStream.ToArray());
    }

    private static string mPadding(string s)
    {
      int length = s.Length;
      if (length == 8)
        return s;
      if (length > 8)
        return s.Substring(length - 8);
      for (int index = 0; index < 8 - length; ++index)
        s += "*";
      return s;
    }

    private static bool myCmp(string s1, string s2)
    {
      return s1.Length == s2.Length && !(s1 != s2);
    }

    private static void Main(string[] args)
    {
      Console.Write("Input your name : ");
      string name = Console.ReadLine();
      Console.Write("Password : ");
      string s1 = Program.myEncrypt(Console.ReadLine(), name);
      if (name == "BluSH4G" && Program.myCmp(s1, Program.getps(name)))
        Console.WriteLine("\n::Congratulation xD ::\n");
      else
        Console.WriteLine("\n:: WTF AUTH FAILED ::\n");
    }

    public static string getps(string name)
    {
      WebRequest webRequest = WebRequest.Create("http://wargame.kr:8084/prob/28/ps.php?n=" + name);
      webRequest.Credentials = CredentialCache.DefaultCredentials;
      HttpWebResponse response = (HttpWebResponse) webRequest.GetResponse();
      Stream responseStream = response.GetResponseStream();
      StreamReader streamReader = new StreamReader(responseStream);
      string end = streamReader.ReadToEnd();
      streamReader.Close();
      responseStream.Close();
      response.Close();
      return end;
    }
  }
}

먼저 Main 부분이다.

 private static void Main(string[] args)
 {
   Console.Write("Input your name : ");
   string name = Console.ReadLine();
   Console.Write("Password : ");
   string s1 = Program.myEncrypt(Console.ReadLine(), name);
   if (name == "BluSH4G" && Program.myCmp(s1, Program.getps(name)))
   	Console.WriteLine("\n::Congratulation xD ::\n");
   else
   	Console.WriteLine("\n:: WTF AUTH FAILED ::\n");
 }

name과 password 값을 입력받는다. 단, password 값은 name과 함께 myEncrypt 함수의 매개 변수로 전달된다.

그 후, if문에서 name이 BluSH4G이고, myCmp(s1, getps(name))이 True면, flag를 출력한다.

먼저 myEncrypt 함수를 봐보자.


 private static string myEncrypt(string strKey, string name)
 {
   DESCryptoServiceProvider cryptoServiceProvider = new DESCryptoServiceProvider();
   cryptoServiceProvider.Mode = CipherMode.ECB;
   cryptoServiceProvider.Padding = PaddingMode.PKCS7;
   byte[] bytes1 = Encoding.ASCII.GetBytes(Program.mPadding(name));
   cryptoServiceProvider.Key = bytes1;
   cryptoServiceProvider.IV = bytes1;
   MemoryStream memoryStream = new MemoryStream();
   CryptoStream cryptoStream = new CryptoStream((Stream) memoryStream, cryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Write);
   byte[] bytes2 = Encoding.UTF8.GetBytes(strKey.ToCharArray());
   cryptoStream.Write(bytes2, 0, bytes2.Length);
   cryptoStream.FlushFinalBlock();
   return Convert.ToBase64String(memoryStream.ToArray());
 }

password 값을 strKey로 받고, name 값을 그대로 name으로 받는다.

암호화 방식으로는 DES 알고리즘, ECB 방식, 패딩 방식은 PKCS7(몰라도 푸는건 지장 없음)을 선택한다.

mPadding(name)을 실행하고 bytes1로 넘겨주는데.. myEncrypt 설명이 끝나고 mPadding 함수를 보도록 하자.

DES Key 값을 strKey를 사용하는 것이 아닌, 아까 mPadding 함수에서 리턴된 name 값(bytes1)을 이용한다.

그리고 memoryStream을 사용하는데 이건 솔직히 왜 사용하는지 모르겠다.

결국, 최종적으로 Base64로 Convert한 값을 리턴 값으로 넘겨준다.


이제 bytes1로 넘어가기 전, 들리는 mPadding 함수이다.

 private static string mPadding(string s)
 {
   int length = s.Length;
   if (length == 8)
 	  return s;
   if (length > 8)
   	return s.Substring(length - 8);
   for (int index = 0; index < 8 - length; ++index)
   	s += "*";
   return s;
 }

문제를 풀다가 알게된 사실인데, DES 알고리즘에서는 Key 값이 무조건 8자리여야 정상적으로 동작한다고 한다.

그래서, 만약 8자리가 되지 않는다면, 그만큼을 *을 사용해 패딩해준다.

하지만, 이 문제에서는 name 값이 무조건 BluSH4G여야 하기 때문에, 최종 값은 BluSH4G*이다.


이제 if문에서 비교할 때 쓰이는, getps 함수이다.

public static string getps(string name)
{
  WebRequest webRequest = WebRequest.Create("http://wargame.kr:8084/prob/28/ps.php?n=" + name);
  webRequest.Credentials = CredentialCache.DefaultCredentials;
  HttpWebResponse response = (HttpWebResponse) webRequest.GetResponse();
  Stream responseStream = response.GetResponseStream();
  StreamReader streamReader = new StreamReader(responseStream);
  string end = streamReader.ReadToEnd();
  streamReader.Close();
  responseStream.Close();
  response.Close();
  return end;
}

해당 함수가 문제 풀이하는데 중요하다(?).

해당 url에 해당하는 WebRequest를 생성하여 출력된 값을 최종적으로 가져온다.

URL에 들어가면 다음과 같은 값을 준다. (참고로, name에 해당하는 값은 BluSH4G이다.)

tcgPiyWBb/n2ttVFykxEdcsnbM1R6sTXzUWRDrEipvYfQMWMHHVKgEqmGadhBLVA

이제 myEncrypt에서 넘어온 값과, getps에서 넘어온 값을 가지고, myCmp 함수로 넘어간다.

 private static bool myCmp(string s1, string s2)
 {
	 return s1.Length == s2.Length && !(s1 != s2);
 }

해당 함수에서는 s1, s2의 길이가 같고, 심지어는 문자열까지 같아야한다.

여기서 리턴된 값을 갖고 Main의 if문 분기를 결정한다.


코드는 이렇게나 길지만.. 코드를 잘 이해했다면 문제 푸는데에는 시간이 얼마 걸리지 않는다.

다음 URL로 이동하고, 다음과 같은 값을 넣고 실행해보자. 

https://www.tools4noobs.com/online_tools/decrypt/

 

Online decrypt tool - Online tools

Supported algorithms Algorithms supported: Cast-128, Gost, Rijndael-128, Twofish, Arcfour, Cast-256, Loki97, Rijndael-192, Saferplus, Wake, Blowfish-compat, Des, Rijndael-256, Serpent, Xtea, Blowfish, Enigma, Rc2, Tripledes. Modes supported: CBC, CFB, CTR,

www.tools4noobs.com