보안 공부하는 꼬깔이

- Replace

 

 

Replace 문제를 클릭해 다운받자!

 

[ Exeinfo Pe ]

패킹여부 : Not packed

제작언어 : C++

 

 

문제파일인 Replace.exe 파일을 실행하면 위 사진과 같은 사용자가 입력가능한 텍스트 박스와 그 값을 넘겨주는 버튼이 존재하고 Wrong 이라는 문구가 출력되어있다.

텍스트 박스는 오직 숫자만을 입력하게끔 되어있다.

 

임의의 숫자를 입력하고 Check 버튼을 클릭하면 작동이 중지되었다는 문구가 출력되고 프로그램이 종료된다.

현재 PC의 문제라고 생각되어 다른 PC에서 테스트 해보았는데 똑같은 현상이 나타났다.

이런 현상부터 문제의 시작이 아닌가하여 디버깅을 해보니 문제의 의도였다!

왜 이런 오류가 뜨는지 디버깅을 통해 알아보도록 하자

 

프로그램을 실행시키고 임의의 숫자를 입력 후 0040466F 주소를 보면 0x90(NOP)를 EAX에 저장하는데 여기서 문제가 발생한다.

 

현재 EAX에는 "601605CC" 라는 주소가 저장되어 있는데 해당 주소는 접근이 불가능한 주소이다.

접근이 불가능한 주소에 0x90 이라는 데이터를 저장하려고 시도하니 에러가 출력되는 것이다.

프로그램이 오류를 출력하고 뻗어버리는 이유를 알았으니 본격적인 분석을 시작해보도록 하자 .

 

프로그램 시작시 사용자가 임의의 숫자를 입력할 수 있는 부분까지 정상적으로 동작하는 것을 생각해보면 해당 기능을 수행하는 함수 역시 정상 동작을 한다는 것이다. 그렇다면 해당 기능을 하는 함수를 기준으로 분석을 해보도록 하자.

현재 프로그램에서 사용하고있는 함수 목록을 출력해주는 기능을 사용하자. (Ctrl + N)

위 사진을 보면 GetDigItemInt 함수가 보일것이다. 해당 함수는 입력된 정수형 데이터를 얻는 함수이다.

아무래도 DialogBoxParamA 함수가 사용자 입력 창을 띄워주고 사용자가 값을 입력하면 GetDigltemlnt 함수가 입력된 정수형 데이터를 가져오는 구조인듯 하다.

 

GetDigltmelnt 함수의 CALL 부분이 위치한 0040105A주소에 브레이크 포인트(F2)를 걸고 실행시켜보자.

 

해당 함수는 사용자가 입력한 숫자를 16진수로 변환해 EAX에 저장하는 모양이다.

위 사진을 보면 내가 입력한 "1234"를 "0x4D2"로 변환해 저장한 것을 알 수 있다.

함수 호출이 끝나면 EAX에 저장된 값을 4084D0 주소에 저장한다. 그 다음 0040466F 주소를 CALL 하는데 이 주소는 처음에 봤던 접근이 불가능한 주소에 NOP 주소를 저장시키려고 시도하여 오류가 출력되던 부분이다.

0040466F 주소를 CALL 해서 어떤 과정을 진행하는지 확인하기 위해 해당 주소로 진입(F7)해보자

0040466F 주소에 진입하면 위 사진의 코드가 출력되는데 0040466F 부분을 보면 0040467A 주소를 CALL 한다.

0040467A 주소는 어떤 과정을 진행하는지 살펴보기위해 주소로 진입해보자.

어샘 코드를 분석해보면 0x619060EB의 값을 406016 주소에 넣고 00404689 주소를 호출하는데 이 주소는 04084D0 주소의 DWORD 값을 1만큼 증가시키는 동작을 수행한다. 04084D0 에는 처음에 내가 입력한 "1234" 가 16진수로 변환되 저장된 값인 "0x4D2" 이 저장되있으며 1만큼 증가해 04084D0에는 "0x4D3" 이 저장된다.

그리고 Retn을 하는데 00404684 주소에서 00404689 주소를 CALL 했기때문에 00404689 주소로 돌아가 4084D0에 저장되어 있는 "0x4D3" 에 한번더 1만큼 증가시켜준다. 그러므로 4084D0에 저장되는 값은 "0x4D4" 가 되며 다시 RETN을 수행하게 된다.

두 번째로 리턴되는 주소는 0X404674 이다.

 

404674 주소의 코드를 확인하면 4084D0 주소에 601605C7을 저장한다.  이렇게 되면 4084D0 주소에 저장되어 있는 "0x4D4"      에 "601605C7" 가 합해진 "60160A9B" 이 주소가 사용자 값이 위치한 주소가 되는것이다.

그리고 00404684 주소를 보면 아까 4084D0 주소에 저장된 값에 2를 더해준 00404689 주소를 호출하는것을 확인할 수 있는데 이렇게 되면 다시한번 4084D0 주소에 저장된 값에 2를 더해 "60160A9D" 주소가 4084D0 주소에 저장 되게 된다.

하지만 전과 달리 RETN 되는 위치는 CALL STACK 상의 0x40106A 주소로 리턴된다.

0040106A 주소에서는 EAX를 초기화 하고 404690 주소로 분기한다.

 

분기한 404690 주소를 보면 사용자 입력값이 저장된 주소를 저장하고 있는 4084D0 주소의 값을 DWORD 크기 만큼 EAX에 넣는다.

그리고 4084D0에 2를 INC 시켜주는 주소인 00404689를 한번 더 호출한다. 그래서 4084D0에 저장된 주소는 "60160A9F" 가 된다.

404689 주소로 인해 40469F 주소로 리턴하면 0xC39000C6 값을 40466F 주소에 넣은 후 40466F 주소를 호출한다.

40466F 주소는 이전에 접근 불가능한 주소에 NOP를 저장하려해 문제가 발생한 영역이다. 처음 디버깅에서는 분명 문제가 없었는데 현재는 이러한 오류가 발생했다. 이유는 40466F 의 코드가 40469F 주소의 코드에 의해 변경되었고 EAX에 4084D0 주소의 값을 넣는 코드를 실행 하는 과정에서 해당 코드를 주소로 삼아서 0x90(NOP)를 넣으려 하니 처음에 봤던 오류가 발생하게 되는 것이였다.

만약에 EAX에 정상적인 주소가 들어있는 상태라면 그 다음은 어떤동작을 수행할까?

아마 그 주소에 NOP를 생성할 것이다.

 

그리고 464672 주소의 RETN 명령어가 실행되는데 위 사진의 스택상태를 보면 리턴할 주소는 4046AE 주소이다.

 

4046AE 주소를 확인하면 정상적인 주소가 들어있다고 가정한 EAX에 1만큼 증가를 시키며 그 주소가 EAX에 저장될 것이다.

그리고 40466F 주소를 호출하는데 해당 주소로 인해 또 다시 0x90(NOP)를 저장하는 코드로 넘어가게 되며 NOP를 입력후 4046B4주소로 RETN 하게 된다.

4046B4 주소에서는 40466F 주소에 0x6E8을 저장한다.

그리고 4046BE 주소에서 POP EAX가 실행되여 남아 있던 RETN 주소인 40469F 주소가 정리되고 4046C4 주소의 분기문으로 인해 401071 주소로 분기한다.

 

분기한 401071 주소를 보면 401084 주소로 분기 시킨다. 그리고 40107E 주소를 확인하면 SetDigitemTextA 함수가 사용되며 401073 주소에는 Correct! 라는 성공을 의미하는 문구가 존재한다.

이렇게 되면 현재 이 함수는 다이얼로그 박스에 표시된 Wrong 를 Correct로 변경시켜주는 역할을 한다는 것을 알 수 있다.

그런데 401071 주소의 분기문으로 인해 "Correct!" 구문을 출력해주는 setDlgItemTextA 함수를 지나치게 된다. 결론적으로 401071주소의 코드를 우회할 방법을 강구하면 문제를 해결할 수 있을 것이다.

어떤 방법을 이용하면 우회가 가능할까 한참 고민하다가 401071 주소의 코드가 "EB 11" 2byte 크기의 코드라는 것을 확인했고 동시에 0x90(NOP) 값을 두 번 저장했던것이 생각났다. 그 0X90(NOP) 코드가 401071,401072 주소에 입력된다면 기존에 있던 분기문코드는 덮어 씌워지게 되고 SetDIGitemTextA 함수가 실행되어 "Correct!" 문구가 출력될 것이다.

[ 정리 ]

처음에 접근이 불가능한 주소에 0x90(NOP)를 저장하려해 오류가 발생한 부분은 40466F 주소에서 EAX 에 401071 주소가 저장된다면 해당 주소의 2Byte 코드가 0x90(NOP)가 되는데, EAX는 사용자가 입력했던 값이 저장되는 주소인 4084D0 주소의 값에 해당함으로 코드상의 연산을 끝내고 난 후 값이 401071 주소가 저장되는 어떠한 수를 넣으면 분기문의 코드가 NOP로 덮어 씌워 질것이고 문제가 풀릴것이다.

계산식 : FFFFFFFF - 601605CB(601605C7+inc+inc+inc+inc) + 401071 = "정답(16진수로 계산하고 10진수로 AUTH창에 넘겨야 함)"

 

 

Replace Clear!