Skip to content

Commit

Permalink
180703
Browse files Browse the repository at this point in the history
180703
  • Loading branch information
joizel committed Jul 3, 2018
1 parent 91fbe2d commit f05b80e
Show file tree
Hide file tree
Showing 3 changed files with 365 additions and 0 deletions.
267 changes: 267 additions & 0 deletions docs/reversing/windows/[2018_VolgaCTF] [REV] Crack Me.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
=====================================================================
[2018_VolgaCTF] [REV] Crack Me
=====================================================================

문제 내용
=====================================================================

Just do it.


문제 풀이
=====================================================================

파일 다운로드 시 실행 파일 Crackme.exe와 암호화 텍스트 Crackme.txt가 주어진다.
Crackme.exe 실행 시 다음과 같은 화면이 출력된다.

.. code-block:: console
Incorrect arguments
Usage: CrackMe.exe <input_file> <output_file> <user_pass_phrase> <mode>
ilspy로 디컴파일한 뒤, 출력되는 문자열을 검색하면 다음 코드 부분을 확인할 수 있다.

.. code-block:: c#
// CrackMe.Program
private static void Main(string[] args)
{
if (args.Length != 4)
{
Console.WriteLine("Incorrect arguments");
Console.WriteLine("Usage: CrackMe.exe <input_file> <output_file> <user_pass_phrase> <mode>");
return;
}
string text = args[0];
string outputFile = args[1];
string userPassword = args[2];
string a = args[3];
if (!File.Exists(text))
{
Console.WriteLine("Input file doesn't exist");
return;
}
CryptoOperation cryptoOperation = new CryptoOperation();
cryptoOperation.FileName = text;
cryptoOperation.UserPassword = userPassword;
if (a == "encrypt")
{
Program.Encrypt(cryptoOperation, outputFile);
return;
}
if (!(a == "decrypt"))
{
Console.WriteLine("Incorrect mode");
Console.WriteLine("Use encrypt or decrypt");
return;
}
Program.Decrypt(cryptoOperation, outputFile);
}
mode 인자값에 따라 암호화 복호화가 가능하며, 주어진 CrackMe.txt파일을 복호화하는게 이번 문제에 핵심으로 보인다.

.. code-block:: c#
// CrackMe.CryptoOperation
public byte[] DecryptFile()
{
byte[] result = null;
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.Key = this.UserKey;
aesCryptoServiceProvider.IV = this.IV;
try
{
ICryptoTransform transform = aesCryptoServiceProvider.CreateDecryptor(aesCryptoServiceProvider.Key, aesCryptoServiceProvider.IV);
FileInfo fileInfo = new FileInfo(this.FileName);
using (MemoryStream memoryStream = new MemoryStream(this.ProcessingBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
{
using (BinaryReader binaryReader = new BinaryReader(cryptoStream))
{
result = binaryReader.ReadBytes((int)fileInfo.Length);
}
}
}
}
catch
{
Console.WriteLine("Failed to decrypt file {0}: wrong password.", this.FileName);
}
}
return result;
}
aes로 암복호화를 하고 있으며, Userkey값을 키값으로 쓰고 있다.
UserKey값은 3번째 인자값을 다음과 같이 변행하여 적용한다.

.. code-block:: c#
// CrackMe.CryptoOperation
public string UserPassword
{
set
{
this.KeyLength = 16;
byte[] userKey = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(value));
this.UserKey = this.CombineKeys(userKey);
}
}
// CrackMe.CryptoOperation
private byte[] CombineKeys(byte[] UserKey)
{
AppSettings appSettings = new AppSettings();
byte[] expr_16 = Encoding.UTF8.GetBytes(appSettings.DefaultKey);
long num = BitConverter.ToInt64(expr_16, 0);
long num2 = BitConverter.ToInt64(expr_16, 8);
long num3 = BitConverter.ToInt64(UserKey, 0);
long num4 = BitConverter.ToInt64(UserKey, 8);
long num5 = num ^ num3;
long num6 = num2 ^ num4;
long num7 = (~num & num3) | (~num3 & num);
long num8 = (~num2 & num4) | (~num4 & num2);
int num9 = BitConverter.ToInt32(BitConverter.GetBytes(num5), 0);
int num10 = BitConverter.ToInt32(BitConverter.GetBytes(num5), 4);
int num11 = BitConverter.ToInt32(BitConverter.GetBytes(num6), 0);
int num12 = BitConverter.ToInt32(BitConverter.GetBytes(num6), 4);
num9 >>= 2;
num10 >>= 2;
num9 <<= 1;
num10 <<= 1;
num12 = num9 << 1;
num11 >>= 2;
num11 = num9 << 1;
num12 >>= 2;
if (~(num9 & num12) == (~num9 | ~num12))
{
num11 = num10;
if (~(num9 & num12) == (~num9 | ~num12))
{
num10 = num12;
}
else
{
num12 = num10;
}
num9 = ~num12;
}
else
{
num11 = num9;
if (~(~num7) == num5 && ~(~num8) == num6)
{
num10 = num12;
}
else
{
num12 = num10;
}
num9 = ~num10;
}
num9 = ~num9;
byte[] bytes = BitConverter.GetBytes(num9);
byte[] bytes2 = BitConverter.GetBytes(num10);
byte[] bytes3 = BitConverter.GetBytes(num11);
byte[] bytes4 = BitConverter.GetBytes(num12);
byte[] array = new byte[16];
for (int i = 0; i < 4; i++)
{
array[i] = bytes[i];
array[i + 4] = bytes2[i];
array[i + 8] = bytes3[i];
array[i + 12] = bytes4[i];
}
return array;
}
위 코드는 보기엔 복잡한 계산식이지만 트릭이 있다.
계산식 중 "num ^ num3"은 "~num & num3) | (~num3 & num)"과 같으며, "num2 ^ num4"도 역시 "(~num2 & num4) | (~num4 & num2)"과 같다.
그리고 if 조건문이 존재하는데 "(~(num9 & num12) == (~num9 | ~num12))"은 어떤 값이든 항상 참으로 else 문이 필요없다.
그리고 if 조건문 안에 추가 if 조건문이 존재하는데 "(~(num9 & num12) == (~num9 | ~num12))"은 어떤 값이든 항상 거짓으로 else문으로 가게된다.
해당 내용에 맞게 위 코드는 다음과 같이 정리할 수 있다.


.. code-block:: c#
// CrackMe.CryptoOperation
private byte[] CombineKeys(byte[] UserKey)
{
AppSettings appSettings = new AppSettings();
byte[] expr_16 = Encoding.UTF8.GetBytes(appSettings.DefaultKey);
long num = BitConverter.ToInt64(expr_16, 0);
long num2 = BitConverter.ToInt64(expr_16, 8);
long num3 = BitConverter.ToInt64(UserKey, 0);
long num4 = BitConverter.ToInt64(UserKey, 8);
long num5 = num ^ num3;
long num6 = num2 ^ num4;
long num7 = num ^ num3;
long num8 = num2 ^ num4;
int num9 = BitConverter.ToInt32(BitConverter.GetBytes(num5), 0);
int num10 = BitConverter.ToInt32(BitConverter.GetBytes(num5), 4);
int num11 = BitConverter.ToInt32(BitConverter.GetBytes(num6), 0);
int num12 = BitConverter.ToInt32(BitConverter.GetBytes(num6), 4);
num9 >>= 2;
num10 >>= 2;
num9 <<= 1;
num10 <<= 1;
num12 = num9 << 1;
num11 >>= 2;
num11 = num9 << 1;
num12 >>= 2;
num11 = num10;
num12 = num10;
num9 = ~num12;
num9 = ~num9;
byte[] bytes = BitConverter.GetBytes(num9); # num9 = num12
byte[] bytes2 = BitConverter.GetBytes(num10); # num10 = (BitConverter.ToInt32(BitConverter.GetBytes(BitConverter.ToInt64(expr_16, 0) ^ BitConverter.ToInt64(UserKey, 0)), 4)>>2) << 1
byte[] bytes3 = BitConverter.GetBytes(num11); # num11 = num10
byte[] bytes4 = BitConverter.GetBytes(num12); # num12 = num10
byte[] array = new byte[16];
for (int i = 0; i < 4; i++)
{
array[i] = bytes[i];
array[i + 4] = bytes2[i];
array[i + 8] = bytes3[i];
array[i + 12] = bytes4[i];
}
return array;
}
결국 리턴값 array에 들어갈 값은 4바이트가 연속되는 값들이 들어가게 된다.

.. code-block:: text
ex>
31323334 31323334 31323334 31323334
41424344 41424344 41424344 41424344
해당 키값을 획득하기 위해 브루트포싱 진행
- 오른쪽으로 2번 시프트 연산 왼쪽으로 1번 시프트 연산
- 오른쪽 1bit는 0
- 왼쪽 1bit는 0

.. code-block:: python
from Crypto.Cipher import AES
import struct
f = open("CrackMe.txt","rb")
data = f.read()
f.close()
iv = data[:16]
data = data[16:]
for i in range(0x10000000,0x80000000,2):
key = struct.pack('>I',i)
aes = AES.new(key*4, AES.MODE_CBC, iv)
dec = aes.decrypt(data)
if "Volga" in str(dec):
print(key)
print(dec)
break
68 changes: 68 additions & 0 deletions docs/reversing/windows/[2018_Xiomara] [REV] Fortune Jack.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
=====================================================================
[2018_Xiomara] [REV] Fortune Jack
=====================================================================

문제 내용
=====================================================================

If your smartphone gets connected to a VPN, you feel like you won a lucky draw.

Lucky_Drawer.exe

문제 풀이
=====================================================================

실행 시 로또같은 화면이 나오며, Generate 버튼 클릭 시 가운데 숫자가 변경되며 "Sorry You are Not So Lucky :( OH!" 메시지 출력
ilspy로 디컴파일한 뒤, 출력되는 문자열을 검색하면 다음 코드 부분을 확인할 수 있음.

.. code-block:: c#
// XiomaraChallenge.Form1
private void checkflag(int key)
{
StringBuilder stringBuilder = new StringBuilder();
string str = "xiomara{";
string text = "þæþÖîûìèýÖðæüÖíàíÖàýÖ³\u00a0";
string str2 = "}";
string text2 = "DB2C17E69713C8604A91AA7A51CBA041";
for (int i = 0; i < text.Length; i++)
{
stringBuilder.Append((char)((int)text[i] ^ key));
}
string text3 = stringBuilder.ToString();
string text4 = Form1.CreateMD5(text3);
bool flag = text4.Equals(text2);
if (flag)
{
this.label3.Text = str + text3 + str2;
this.label3.Visible = true;
}
bool flag2 = text4 != text2;
if (flag2)
{
this.label3.Text = "Sorry You are Not So Lucky :( OH!";
this.label3.Visible = true;
}
}
- 해당 문자열이 출력되지 않으려면 flag2가 false여야 함
- flag2가 false가 되기 위해서는 text4와 text2가 같아야 함.
- text4와 text2가 같기 위해서는 stringBuilder값을 md5한 값이 "DB2C17E69713C8604A91AA7A51CBA041"와 같아야 함.
- md5값을 확인하기 위해서는 임의값 입력을 통한 브루트포싱을 진행

.. code-block:: python
#-*- coding: utf-8 -*-
from hashlib import md5
arr = u"þæþÖîûìèýÖðæüÖíàíÖàýÖ³\u00a0"
md5hash = "db2c17e69713c8604a91aa7a51cba041"
for key in range(256):
flag = ''
for b in arr:
flag += chr(key ^ ord(b))
if md5hash == md5(flag.decode("utf-8","ignore").encode('utf-8')).hexdigest():
print("xiomara{" + flag + "}")
30 changes: 30 additions & 0 deletions docs/reversing/windows/[2018_sharifctf 8] [REV] crack me.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=====================================================================
[2018_SharifCTF 8] [REV] Crack me!
=====================================================================

문제 내용
=====================================================================

Find the password.


동작 화면
=====================================================================

```sh
$ ./crackme
Enter Password:
aaaaaa
Try again
```

문제 풀이
=====================================================================

- 패스워드 값이 참이 아닐 경우, "Try again" 메시지 출력. 해당 출력이 발생되는 함수 부분을 디버깅을 통해 확인: sub_402140에서 해당 메시지가 출력됨
- sub_402140이 받는 인자값 확인: v53
- sub_401e70에서 v53값이 저장됨 (-1|0)
- -1일 경우 "Try again"
- 0일 경우 "Correct:Flag is MD5 Of Password"
- 0이 출력되기 위해서는 Dst값이 whynxt과 일치해야함. Dst값은 입력값이 암호화된 값

0 comments on commit f05b80e

Please sign in to comment.