HDCON 2009 예선 Stage 0

2010/06/11 00:24

제7회 해킹방어대회, HDCON 2010 본선이 오늘 있었습니다. 온라인 중계와 트위터 중계로 소식을 전해들었는데, 정말 재미있어 보이더군요. 예선에서 stage 3, 즉 4번 문제를 조금 더 빨리 풀었으면 저희도 본선 나갈 수 있었는데 참 아쉬웠습니다. 포렌식에 대한 준비가 미흡했던 게 원인이었죠.

그래서 작년의 해킹방어대회의 예선 문제부터 차근차근 정리해보려 합니다. 작년에는 선배들 옆에서 전 구경만 했었기 때문에 문제들을 새로 푸는거나 다름 없네요.

Question

웹 프로그래머 강산에씨는 웹 해킹으로 부터 자유를 선언하며
강력한 보안성을 주장하는 웹 2.0 트랜드로 홈페이지를 제작했다.
이 새로운 솔루션은 과연 안전할까?

로그인 패스워드가 1단계의 답이다.

ID: stage1 , PASSWORD: Welcome_to_the_hdcon!

Analysis

첫 번째 문제부터 흥미롭습니다. HDCON 2010 예선 문제들도 꽤 흥미로웠는데, 되짚어보니 작년도 그랬었네요. 주어진 ID 와 PASSWORD 로 문제로 접속하면, 웹페이지가 반갑게 인사합니다.

사용자 삽입 이미지

사실, 이 페이지는 플래시로 구성되었습니다. HDConKnowWhatYouDidLastGame.swf 라는 플래시 파일을 로드했더라구요. Account 와 Password 에 아무런 값을 입력하고 Enter 를 누르면, 경고창이 나타납니다. 아무래도 정확한 Account 와 Password 를 입력하면, 문제가 풀릴 것 같습니다. 문제에서도 로그인 패스워드가 답이라고 했으니까요.

Solution

플래시가 등장한 이 순간부터 이 문제는 웹해킹 문제가 아니라, 리버스 엔지니어링 문제로 변합니다. swf 파일은 디컴파일이 가능하거든요. swf 파일을 디컴파일할 수 있는 툴은 여러 가지가 있는데, 그 중에서 Sothink SWF Decompiler 가 가장 뛰어납니다. 이 문제를 풀기 위해서 다른 툴들도 이용해 봤지만, Sothink SWF Decompiler 만큼 디컴파일을 잘 해주는 툴은 없는 것 같습니다.

사용자 삽입 이미지

swf 파일을 다운로드하여서 Sothink SWF Decompiler 로 열면, 여러 가지 파일들이 보입니다. 그 중에서 메인 클래스인 HDConKnowWhatYouDidLastGame 을 디컴파일합니다. 그러면 소스 레벨로 디컴파일러가 코드를 출력합니다.

메인 클래스 소스 중에서 우선적으로 봐야 할 부분은 역시 문자열이겠죠. 특히, Enter 을 눌렀을 때 나타나는 경고창의문자열들이 도움이 될 것입니다. 윈도우즈 PE 파일을 리버싱할 때처럼요. 소스를 쭈욱 쭉 내려가다 보면, 다음과 같은 코드를 볼 수 있습니다.

private function showMsg() : void
{
var _loc_1:int;
if (isAdmin.length > 8 || isAdmin.length < 4)
{
Alert.show("계정의 유효길이는 4자에서 8자까지입니다.");
}
else if (isPasswd.length > 32 || isPasswd.length < 10)
{
Alert.show("비밀번호의 유효길이는 10자에서 32자까지입니다.");
}
else if (isAdmin.text == "admin")
{
_loc_1 = loveBBoy(isPasswd.text);
if (_loc_1 == 1)
{
Alert.show(" 축하합니다! \n 당신이 입력한 비밀번호가 이번문제의 정답입니다.");
}
else if (_loc_1 == 9)
{
Alert.show("비밀번호가 일치하지 않습니다.");
}
else
{
Alert.show("도데체 뭘 입력한거죠?");
}// end else if
}
else
{
Alert.show("죄송합니다...\n당신의 Account는 " + isAdmin.text + "이 아닙니다만...");
}// end else if
return;
}// end function


코드에서 친절히 Account 가 "admin" 이라고 알려주고 있습니다. 그리고 Password 를 loveBBoy() 함수로 확인을 하네요.

이번에는 HDConKnowWhatYouDidLastGame 클래스 안에서 loveBBoy() 함수를 찾습니다.

private function loveBBoy(scriptRecursionLimit:String) : int
{
var _loc_2:* = new Base64Encoder();
_loc_2.encode(scriptRecursionLimit);
if (_loc_2.toString() == MeninBlack())
{
return 1;
}// end if
return 9;
}// end function


loveBBoy() 함수에서는 입력받은 문자열을 base64 인코딩을 하여서 MeninBlack() 함수의 반환값와 비교를 하고 있습니다. 그렇다면, MeninBlack() 에서 반환되는 문자열은 base64 인코딩이 된 문자열임을 추측할 수 있습니다. 즉, MeninBlack() 에서 반환되는 문자열을 base64 디코딩을 하면 정확한 Password 를 얻을 수 있겠네요.

다시 HDConKnowWhatYouDidLastGame 클래스 안에서 MeninBlack() 함수를 찾으면 되겠죠?

private function MeninBlack() : String
{
var _loc_1:String;
var _loc_2:String;
var _loc_3:String;
var _loc_4:String;
var _loc_5:String;
var _loc_6:String;
_loc_1 = _loc_2 + _loc_3 + _loc_4 + _loc_5 + _loc_6;
return _loc_1;
}// end function


정말 당황스러운 코드입니다. 결국은 여러 문자열들을 결합한 _loc_1 을 반환한다는 것인데, 그 문자열들이 무엇인지를 알 수 없네요. 여기서 각 문자열들을 알아보기 위해서는 Sothink SWF Decompiler 의 P Code 를 볼 수 있는 기능을 이용합니다. P Code 는 가상머신에서 실행할 수 있는 코드라네요. 자바로 따지면, 바이트 코드라고 볼 수 있을 것 같습니다. 다음은 MeninBlack() 함수의 P Code 입니다.

private function MeninBlack() : String
{
_as3_debugfile "C:\Documents and Settings\mario\My Documents\Flex Builder 3\HDConKnowWhatYouDidLastGame\src;;HDConKnowWhatYouDidLastGame.mxml"
_as3_debugline line number: 67
_as3_getlocal <0>
_as3_pushscope
_as3_pushnull
_as3_coerce_s
_as3_setlocal <1>
_as3_debug 1, 5089, 0, 68
_as3_debug 1, 5090, 1, 70
_as3_debug 1, 5091, 2, 71
_as3_debug 1, 5092, 3, 72
_as3_debug 1, 5093, 4, 73
_as3_debug 1, 5094, 5, 74
_as3_pushstring "VGhlX0Jh"
_as3_debugline line number: 70
_as3_coerce_s
_as3_setlocal <2>
_as3_pushstring "bGxlcmlu"
_as3_debugline line number: 71
_as3_coerce_s
_as3_setlocal <3>
_as3_pushstring "YV93aG9fbG92"
_as3_debugline line number: 72
_as3_coerce_s
_as3_setlocal <4>
_as3_pushstring "ZXNfYV9CQm95IQ"
_as3_debugline line number: 73
_as3_coerce_s
_as3_setlocal <5>
_as3_pushstring "=="
_as3_debugline line number: 74
_as3_coerce_s
_as3_setlocal <6>
_as3_debugline line number: 76
_as3_getlocal <2>
_as3_getlocal <3>
_as3_add
_as3_getlocal <4>
_as3_add
_as3_getlocal <5>
_as3_add
_as3_getlocal <6>
_as3_add
_as3_coerce_s
_as3_setlocal <1>
_as3_debugline line number: 78
_as3_getlocal <1>
_as3_returnvalue
}// end function


_as3_pushstring 명령어로 메모리에 올리는 문자열들이 보입니다. 저 문자열들을 모두 결합하면, 하나의 base64 형태의 문자열이 완성되지요.

VGhlX0JhbGxlcmluYV93aG9fbG92ZXNfYV9CQm95IQ==

이것을 base64 디코딩을 하면, 정확한 Password 를 얻을 수 있습니다.

The_Ballerina_who_loves_a_BBoy!

다음 그림은 인증샷!

사용자 삽입 이미지

크리에이티브 커먼즈 라이센스
Creative Commons License

6l4ck3y3 #2. 지피지기, 백전불패/리버스 엔지니어링 , , ,

Trackback Address:http://hisjournal.net/blog/trackback/323
[로그인][오픈아이디란?]