워게임/CodeEngn

[CodeEngn] Advance RCE L07

name2965 2022. 5. 3. 02:05
728x90

 

 

 

문제는 다음과 같습니다.

 

일단 문제파일을 exeinfope로 한번 까보면

 

 

 

 

이렇게 나오는데, 우선 32비트기반 실행파일이고 .NET 기반의 C# 프로그램인것을 확인해볼수 있습니다.

하.... 저는 C#에 대해서 잘 모르기 때문에 다음 문제로 넘어갈까 하다가 한번 도전해보고자 시작하게 되었습니다.

 

정확한 분석을 위해서 virtualbox를 이용해서 만든 windows 7 32bit 환경에서 계속해서 분석하겠습니다.

 

우선 프로그램을 실행시켜보면

 

 

 

 

다음과 같이 텍스트박스 2개와 버튼 2개로 이루어진 프로그램이고 

1번째 텍스트박스에 HMX0101이라는 문자열이 있는것을 확인할수 있습니다.

 

 

문제에서는 name이 CodeEngn이고 serial이 28BF522F-A5BE61D1-XXXXXXXX 와 같은 형식일때 

뒤에 8자리 부분을 알아내야한다고 나와있습니다.

 

일단 C#을 분석할수있는 도구인 dnspy를 통해서 까봐야합니다.

 

 

 

 

 

많은 시간을 들이고 엄청난 검색을 통해서 분석해본 결과

 

name과 serial을 입력받으면 자릿수 검증을 한후

맞으면 시리얼은 각 마디를 제외한 숫자 8자리부분을 문자열에서 16진수의 원래형태로 변환한뒤

3개의 변수를 선언할때 같이쓰입니다.

그 후, CodeEngn이라는 문자열에서 문자 하나하나를 아스키코드에 따라서 16진수로 변환한후 배열형태로 선언합니다.

 

그리고 CodeEngn 문자열을 GetHashCode()라는 함수에 사용해서 그에대한 반환값으로 hashcode라는 변수를 만듭니다.

 

그 이외에는 선언한 변수들과 기타 등등의 값들을 이용해서 엄청나게 복잡한 연산과정을 거친후

특정 조건을 만족하는 경우에만 성공 메세지를 띄우는것으로 확인됩니다.

 

일단 분석을 해본 결과, 최소한 저의 머리로는 모든 연산과정을 자세하게 분석하지 못하기 때문에

어떻게 해야할지 고민하다가, 제가 그나마 할줄아는 C언어로 이 프로그램에서 시리얼번호를 생성하는

알고리즘을 그대로 가지고 온다음 C#과 C언어 간의 구조차이가 있기 때문에 그러한 요인들을 고려해서

시리얼 번호가 나올수있는 모든 경우의수를 하나하나 검사해보는 브루트 포스 알고리즘을 짜야할것같습니다.

 

 

여기서 문제가 되었던것은 hashCode 였는데, GetHashCode()라는 함수는 같은 문자열을 통해서 함수값을 받더라도

32bit와 64bit 간의 차이가 있고, CodeEngn은 32bit를 기준으로 나온 시리얼번호를 유효하게 따지기 때문에

브루트포스 알고리즘을 짜기 위해서는 CodeEngn 문자열에 대한 hashCode값만 따로 빼오는 작업을 해야합니다.

 

저는 close 버튼을 눌렀을때 메세지에서 hashCode값이 같이 출력되도록 프로그램을 수정해서 확인했습니다.

 

 

 

 

이런식으로 button2_Click 메소드를 수정한후 프로그램을 실행해서 hashCode는 사용되는 문자열에 따라서 값이 달라지기 때문에 문제에서 제시한대로 Name을 CodeEngn으로 바꾼후 close를 누르면

 

 

 

 

다음과 같이 hashCode가 출력됩니다. 이 값을 16진수로 바꾸면 0x9E1E73D5 입니다.

 

이 값을 사용해서 브루트포스 알고리즘을 짜봤습니다.

 

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

unsigned __int8 CodeEngn[] = { 0x43,0x6F,0x64,0x65,0x45,0x6E,0x67,0x6E };
unsigned int ewrrr[] = {1082200817, 440077137, 693619197, 456293661, 2743929585,718661953};

bool vxzzz(unsigned int rwerqw[], unsigned int kgtsdfs[], unsigned int pgdsfa, unsigned int fsfsdf)
{
	pgdsfa ^= fsfsdf;
	unsigned int num = pgdsfa % 57 - 1;
	unsigned int num2 = rwerqw[0];
	unsigned int num3 = rwerqw[1];
	unsigned int num4 = num;
	unsigned int num5 = pgdsfa << (int)((unsigned __int8)(97 ^ num + 68));
	if (num == 0)
		return false;
	while (num-- > 0)
	{
		unsigned int num6 = num4 / 16;
		unsigned int num7 = num2 << (int)((unsigned __int8)(num4 / 8));
		unsigned int num8 = num2 >> (int)(3 + (unsigned __int8)num6);
		unsigned int num9 = num4 / 4 + 3;
		unsigned int num10 = num9;
		num9 = kgtsdfs[(int)((unsigned int*)((num5 >> (int)((unsigned __int8)num9)) % 4))];
		unsigned int num11 = num5 + num9;
		num3 -= ((num7 ^ num8) + num2 ^ num11) - num;
		num5 -= pgdsfa;
		num3 -= num;
		num7 = num3 << (int)((unsigned __int8)(num10 + 1) ^ 8);
		num8 = num3 >> (int)((unsigned __int8)(num4 / 2 - num10 + 23) ^ 25);
		if (num == num4)
			num3 ^= num;
		if (num == num4 / 2 + (num10 ^ 27))
			num10 = (num7 ^ num8) + (num3 ^ num);
		else
			num10 = (num7 ^ num8) + num3;
		num2 -= (num10 ^ num5 + kgtsdfs[(int)((unsigned int*)(num5 & 3))]);
	}
	rwerqw[0] = (num2 ^ 4);
	rwerqw[1] = (num3 ^ 7);
	rwerqw[2] = (rwerqw[1] ^ (unsigned int)((unsigned __int8)((num4 + 1) / 3 - 4)));
	rwerqw[3] = (rwerqw[0] ^ (unsigned int)((unsigned __int8)(num4 - 21 + 1 ^ 8)));
	rwerqw[0] = (rwerqw[0] ^ kgtsdfs[4]);
	rwerqw[1] = (rwerqw[1] ^ kgtsdfs[5]);
	return true;
}

unsigned int qwerty(unsigned __int8 text[])
{
	unsigned int num = 3131961357;
	unsigned int tqepq[256];
	unsigned int num2 = 0;
	int tqepq_length = 256;
	while ((unsigned long)num2 < (unsigned long)((long)tqepq_length))
	{
		unsigned int num3 = num2;
		for (int i = 8; i > 0; i--)
		{
			if ((num3 & 1) == 1)
				num3 = (num3 >> 1 ^ num);
			else
				num3 = num3 >> (int)((unsigned __int8)(num / num));
		}
		tqepq[(int)((unsigned int*)num2)] = num3;
		num2 += 1;
	}
	num = 0;
	num = ~num;
	for (int i = 0; i < 8; i++)
	{
		unsigned __int8 b = (unsigned __int8)((num & 255) ^ (unsigned int)text[i]);
		num = (num >> 8 ^ tqepq[(int)b]);
	}
	return ~num;
}

int main()
{
	unsigned int yreee[4];
	unsigned int num = 0x28BF522F;
	unsigned int num2 = 0xA5BE61D1;
	unsigned int num4 = qwerty(CodeEngn);
	unsigned int hashCode = 0x9E1E73D5;
	for (int i = 0; i <= 0xFFFFFFFF; i++)
	{
		unsigned int num3 = i;
		printf("%X\n", num3);
		yreee[0] = num;
		yreee[1] = num2;
		yreee[2] = num;
		yreee[3] = num2;
		num3 ^= hashCode;
		if (vxzzz(yreee, ewrrr, 2415796773, num3) && (int)yreee[2] == (int)hashCode && (int)yreee[3] == (int)num4)
		{
			printf("시리얼값: 28BF522F-A5BE61D1-%X\n", i);
			break;
		}
	}
	return 0;
}

 

 

하지만 이 프로그램을 실행시켜보면 다음과 같이 연산이 됐다가 안됐다가 하는 식으로 굉장히 뚝뚝 끊기는 

현상이 발생합니다.

 

 

 

 

분명 연산 로직을 그대로 가져와서 브루트포스 알고리즘을 만들었는데 도데체 왜 안되는것인지 모르겠어서 

연산 로직을 자세히 분석해봤는데 

 

 

 

 

이 부분을 보면, xor연산을 거친후 pgdsfa변수가 57과 나머지 연산을 한후 -1을 한 값을 num변수에 선언합니다.

그 후에는 num 변수가 0인지 아닌지 확인한후 num 변수를 통해서 반복문을 돌립니다.

 

여기서 문제가 발생하는데, num 변수는 unsigned int 자료형으로 선언되었기 때문에,

pgdsfa 와 57이 나머지 연산을 했을때 만약 나누어 떨어지는 상황이라면 결과적으로 num변수의 값은

-1이 되기때문에 분기문에서도 걸리지않고 그대로 반복문으로 들어가게됩니다.

 

그렇게 되면 unsigned int형 변수가 -1이 되었으므로 언더플로우가 일어나게 되어서 0xFFFFFFFF라는 어마무시한 값이 됩니다. 의도한 부분인지는 모르겠지만 이 부분으로인해서 프로그램의 연산로직을 그대로 가져다 쓰는것으로는

제대로된 브루트포스 알고리즘을 짤수 없게됩니다.

쓸데없는 연산으로인해서 브루트포싱이 제대로 되지 않고있고, 어차피 의도대로라면 0이하일때는 틀린값이 될것이므로

분기문의 조건을 수정해줍니다.

 

 

 

 

다음과 같이 0이하인 값이라면 false를 리턴하도록 수정했습니다.

참고로 int형으로 형변환을 한후에 비교를 하는 이유는, unsigned int 형인 상태로 비교연산을 하게되면 

-1일 경우에는 0xFFFFFFFF만큼 값이 언더플로우가 일어나므로, 0이하라고 판단되지 않으므로 

int형으로 형변환시켜서 제대로된 값으로 인식되도록 해야합니다.

 

이렇게 고친후 최종적인 코드는 다음과 같습니다.

 

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

unsigned __int8 CodeEngn[] = { 0x43,0x6F,0x64,0x65,0x45,0x6E,0x67,0x6E };
unsigned int ewrrr[] = {1082200817, 440077137, 693619197, 456293661, 2743929585,718661953};

bool vxzzz(unsigned int rwerqw[], unsigned int kgtsdfs[], unsigned int pgdsfa, unsigned int fsfsdf)
{
	pgdsfa ^= fsfsdf;
	unsigned int num = pgdsfa % 57 - 1;
	unsigned int num2 = rwerqw[0];
	unsigned int num3 = rwerqw[1];
	unsigned int num4 = num;
	unsigned int num5 = pgdsfa << (int)((unsigned __int8)(97 ^ num + 68));
	if ((int)num <= 0)
		return false;
	while (num-- > 0)
	{
		unsigned int num6 = num4 / 16;
		unsigned int num7 = num2 << (int)((unsigned __int8)(num4 / 8));
		unsigned int num8 = num2 >> (int)(3 + (unsigned __int8)num6);
		unsigned int num9 = num4 / 4 + 3;
		unsigned int num10 = num9;
		num9 = kgtsdfs[(int)((unsigned int*)((num5 >> (int)((unsigned __int8)num9)) % 4))];
		unsigned int num11 = num5 + num9;
		num3 -= ((num7 ^ num8) + num2 ^ num11) - num;
		num5 -= pgdsfa;
		num3 -= num;
		num7 = num3 << (int)((unsigned __int8)(num10 + 1) ^ 8);
		num8 = num3 >> (int)((unsigned __int8)(num4 / 2 - num10 + 23) ^ 25);
		if (num == num4)
			num3 ^= num;
		if (num == num4 / 2 + (num10 ^ 27))
			num10 = (num7 ^ num8) + (num3 ^ num);
		else
			num10 = (num7 ^ num8) + num3;
		num2 -= (num10 ^ num5 + kgtsdfs[(int)((unsigned int*)(num5 & 3))]);
	}
	rwerqw[0] = (num2 ^ 4);
	rwerqw[1] = (num3 ^ 7);
	rwerqw[2] = (rwerqw[1] ^ (unsigned int)((unsigned __int8)((num4 + 1) / 3 - 4)));
	rwerqw[3] = (rwerqw[0] ^ (unsigned int)((unsigned __int8)(num4 - 21 + 1 ^ 8)));
	rwerqw[0] = (rwerqw[0] ^ kgtsdfs[4]);
	rwerqw[1] = (rwerqw[1] ^ kgtsdfs[5]);
	return true;
}

unsigned int qwerty(unsigned __int8 text[])
{
	unsigned int num = 3131961357;
	unsigned int tqepq[256];
	unsigned int num2 = 0;
	int tqepq_length = 256;
	while ((unsigned long)num2 < (unsigned long)((long)tqepq_length))
	{
		unsigned int num3 = num2;
		for (int i = 8; i > 0; i--)
		{
			if ((num3 & 1) == 1)
				num3 = (num3 >> 1 ^ num);
			else
				num3 = num3 >> (int)((unsigned __int8)(num / num));
		}
		tqepq[(int)((unsigned int*)num2)] = num3;
		num2 += 1;
	}
	num = 0;
	num = ~num;
	for (int i = 0; i < 8; i++)
	{
		unsigned __int8 b = (unsigned __int8)((num & 255) ^ (unsigned int)text[i]);
		num = (num >> 8 ^ tqepq[(int)b]);
	}
	return ~num;
}

int main()
{
	unsigned int yreee[4];
	unsigned int num = 0x28BF522F;
	unsigned int num2 = 0xA5BE61D1;
	unsigned int num4 = qwerty(CodeEngn);
	unsigned int hashCode = 0x9E1E73D5;
	for (int i = 0; i <= 0xFFFFFFFF; i++)
	{
		unsigned int num3 = i;
		printf("%X\n", num3);
		yreee[0] = num;
		yreee[1] = num2;
		yreee[2] = num;
		yreee[3] = num2;
		num3 ^= hashCode;
		if (vxzzz(yreee, ewrrr, 2415796773, num3) && (int)yreee[2] == (int)hashCode && (int)yreee[3] == (int)num4)
		{
			printf("시리얼값: 28BF522F-A5BE61D1-%X\n", i);
			break;
		}
	}
	return 0;
}

 

 

이 프로그램을 실행시키면 다음과 같이 브루트포싱을 통해서 시리얼 번호가 나오는것을 확인할수 있습니다.

 

 

 

 

 

이 값이 나오는데 굉장히 오랜시간이 걸렸습니다. 값이 조금만 더 컸더라면 정말 일주일 정도는 걸리지 않았을까 생각해봅니다.

 

마지막으로 이 값을 프로그램에 입력해보면

 

 

 

 

 

다음과 같이 풀리는것을 확인할수 있습니다.

 

 

728x90

'워게임 > CodeEngn' 카테고리의 다른 글

[CodeEngn] Basic RCE L05  (0) 2022.05.08
[CodeEngn] Basic RCE L04  (0) 2022.05.04
[CodeEngn] Basic RCE L03  (0) 2022.05.04
[CodeEngn] Basic RCE L02 공부  (0) 2022.04.14
[CodeEngn] Basic RCE L01 공부  (0) 2022.04.14