38
ActiveX COM Methods Analysis(함수분석법) 저자: AmesianX ([email protected]) 출처: 파워해커(POWERHACKER.NET) 본 문서는 “제2회 POC2007 국제해킹/보안컨퍼런스” 개최시에 “해킹과 보안”이라는 해커잡지 창간호에 개재된 내 용입니다. 사전에 잡지발표 후 인터넷에 문서공개 허락을 받았으므로 이 문서의 무단배포를 허락하는 바 입니다.

ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

ActiveX COM Methods Analysis(함수분석법)

저자: AmesianX ([email protected])

출처: 파워해커(POWERHACKER.NET)

본 문서는 “제2회 POC2007 국제해킹/보안컨퍼런스” 개최시에 “해킹과 보안”이라는 해커잡지 창간호에 개재된 내

용입니다. 사전에 잡지발표 후 인터넷에 문서공개 허락을 받았으므로 이 문서의 무단배포를 허락하는 바 입니다.

Page 2: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

오늘날 우리들은 해커이자 동시에 현 보안업계에 종사하고있으며 대부분 PT 나 컨설팅을

하거나 보안업계 담당자내지는 필자같이 PT 를 하다가 개발자로 온 경우가 있을것이다. 그

동안 여러취약점들을 기계처럼 찾아서 리포트를 해왔지만 사실 그 의미를 깊이있게 고찰하

고 되새김을하려는 시도는 주변에서 많이 볼 수 없었다. 그 점은 필자가 항상 안타까워하는

부분이었다. 깊이있게 고찰해야 할 내용들은 많지만 연구해서 높은 벽을 허물고 이루어내려

는 움직임이 없었다는 것이다. 항상 공부만하려고 하는 모습일뿐 연구하는 문화는 소수의

하드스터디(?!) 해커들뿐이었다. 아마 주변에서 해커를 양산하는 기관에대한 안좋은 이미지

도 사실 이러한 고찰에 대해서 깊이다루고있지 않을것이라는 선입견 때문일 것이다. 바다로

가고싶은가? 아니면 이렇게 길들여져 기계처럼 해킹을하고 공부를하며 대충 취약점발견해놓

고 분석에대한 고찰도없이 제출하고 끝내버릴 것인가? 이러한 고민은 필자뿐만이 아니라 이

분야에 발을 디딘 모든분들의 끝낼수 없는 고민이라 생각한다.

지금부터 필자가 설명하게될 내용은 우리가 인터넷을 할때 끈덕지게 달라붙어있는 ActiveX

에대한 재미난 연구결과이다. 이 빌어먹을 ActiveX 에대해서 깊이 고민을하기 시작한것은

2005 년도쯤 A3 라는 보안컨설팅 회사에서 근무하던 중반부터였다. 그 당시 국내의 한 해

커에의해 금융사들에 불어닥친 ActiveX 취약점 광풍때문에 순간적으로 매우많은 연구를 진

행해야만 했다. 그 덕분에 COM 에대해서는 프로그래밍적으로 새로운 고찰을 많이하여 이

제는 거의 모든것이 정립되었다. 그러나 필자가 해결하지 못한채 귀찮아서 그냥 넘어갔던

것이 있었다. 분석방법에대한 것인데 뒤에서 그 문제를 해결하는 것을 보일 것이다. 필자가

ActiveX 취약점을 찾는데 있어서 가장 기본적인 스크립트 작성법을 배우게 된 것은 사실 스

스로 알게 된 것은 아니었는데 국내해커가 금융권해킹을 시연하면서 보여준 익스플로잇

(Exploit)을 역 분석 하면서 그 원리를 알게 되었다. (그 해커는 공격 스크립트를 두 세 단계

로 Encryption(인크립션) 시켜놔서 풀어내는 것에만 1시간이 걸렸다. -_-) 그 이후로 ActiveX

를 공격하는 스크립트를 작성할 수 있게 되었고 여러가지 ActiveX 를 대상으로 취약점을 발

견하였으나 개인적으로 BOF(버퍼오버플로우) 공격까지만 완성짓고 더 이상의 취약점을 찾아

내는 것에는 흥미를 잃었다. 그 이유는 일단, 취약점이 너무 많아서 희소성이 없다는 것, 그

리고 서비스팩2 가 나오면서 자동으로 ActiveX 가 작동하지 않고 경고가 뜬다는 점, 마지막

으로는 대부분의 사람들이 ActiveX 를 간단한 스크립트 몇 줄로 공격하면서 분석적인 고찰

없이 무분별하게 흡수한다는 사실을 인식했기 때문이다. 그러한 결과로 ActiveX 공격 자체

는 취약점에 대한 분석관점이라기보다 팁(Tip)으로 전락해 버렸다. 더이상 취약점 리포팅이

란 의미 자체가 없어진지는 이미 오래전이다. (지금도 찾아보면 메이져(Major)급 회사에서도

취약점이 수두룩하다.) 그러나 A3 를 그만두기 직전까지는 PT 를 하더라도 기계화된 방식의

ActiveX 취약점 찾기는 어쩔수 없이 계속할 수 밖에 없었는데 그때부터 느끼기 시작한 것이

바로 남들보다 보고서를 잘 써보자였다. 맨날 거기서 거기인 다 빗비름한 ActiveX 취약점

보고서를 보는 담당자들은 얼마나 지루할까라는 좀 엉뚱한 생각에서 보고서를 보고 좀 놀라

보라는 재미난 발상을 꿈꿨던 것이다. 그렇지만 이 계획은 PT 에 몸담고 있을 시절 계획에

Page 3: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

옮기지 못하였다. 그 이유는 본인이 너무 COM 에 대해서 무지했기 때문이다. 그래서 회사

를 그만두고부터 COM 에 대해서만 집중적으로 공부하여 COM 을 다양하게 접근하는 방법

을 알아냈다. 그리고 현재는 이 문서를 쓰는 시점에서 COM 에 대해서는 남들이 잘 알지

못하는 기술도 확보하고있다.(그 기술은 차후에 발표예정)

지금 이 잡지를 통해서 ActiveX 분석 접근법 한가지와 바이너리 조작 방법을 설명할 것이

다. ActiveX 분석 접근법 같은 경우는 필자가 국내와 해외에서 얻을 수 있을만한 정보는 거

의 다 뒤져보았어도 찾을 수 없었던 방법이다. 물론, 개발자들이 자신들의 필요에 의해서 이

론으로 제시한 것들과 간략한 코드들은 존재하고 있고 그 정보들로 인하여 도움을 받은 것

이 사실이다. 하지만 지금처럼 해킹을 위한 목적으로 접근법을 설명한 곳은 아마 그 어느

곳을 뒤져도 전혀 나오지 않을 것이다. 만약 찾게 된다면 개인적으로 연락을 해주길 바란다.

만일, 필자가 한번도 본적이 없었던 정보라면 감사의 술을 한턱 쏠 것이다.(이 문서가 작성

되었던 시점에서는 발견하지 못하였다가, 최근의 IDA Pro 를 만든 제작사인 DataRescue 에

서 2002 년도 버전의 COM 헬퍼를 찾게되었다. 이 문서에서 기술하고 있는 내용과 일치하

고 있었으나 아직 성능확인을 해보지 못하였다. – 필자주: 필자가 문서기고를 완료한 시점에

다시 2007 년도 COM 헬퍼로 버전업이 되었으며 작동까지 확인하였음. 굿!! 작동!! – )

ActiveX 함수(Method) 분석방법

필자가 설명하려고 하는 ActiveX 분석 접근법은 가장 기본적인 것에 있다. 바로 설명으로

들어가보자.

[그림 1] ActiveX 를 생성하고 COM 모듈내부의 UpdateModules 함수호출

참고로 위에 보이는 컨트롤은 XecureWeb 이라는 PKI 관련 컨트롤이다. 위에서는 해당

UpdateModules 라는 함수호출을 예로 들고 있지만 취약점이 있다고 오산하지 말기 바란다.

그냥 단순히 예를 들고 있는 것일 뿐이며 실제로 취약점이 존재해서 보여주는 것이 아니다.

테클걸고 싶은 사람이 있으면 당장 이 잡지를 덮길 바란다.

우리는 위의 그림에서 보는 것처럼 HTML 스크립트를 사용해서 BOF 취약점이 존재하는지

Page 4: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

공격테스트를 하는 것에 매우 익숙할 것이다. 또한 굳이 HTML 스크립트가 아니라고 하더라

도 AXMAN 이나 AXFUZZ 또는 iDefense 라는 보안회사에서 개발한 COMRaider 라는

Fuzzer(퍼져:자동공격툴)를 사용하기도 한다. 그런데 여기서 한가지 질문을 해보게된다. 스크

립트를 쓰지않고 자동화 공격도구인 Fuzzer 조차도 쓰지않은 상태에서 바이너리 리버싱 만

으로 ActiveX 취약점을 찾아내는 것은 못하는 걸까? 이러한 생각을 한번쯤 해보았을 것이라

고 생각한다. 간단히 말하면 다음의 그림에서 보이는 UpdateModules 함수를 스크립트로 작

성해서 테스트하지 않고 그 함수를 직접 어셈블리로 뜯어보는 생각을 했었을 거란 의미이다.

하지만 ActiveX 모듈은 COM 이기 때문에 IDA(Interactive Disassembler) 에서 함수하나 찾는

데만도 많은 시간을 허비하게 될 것이다. 이유는 다음에서부터 나타난다.

[그림 2] COM DLL 만이 타입라이브러리(TypeLibrary) 라 불리는 프로토타입을 갖음

[그림 3] 좌측의 COM DLL 과 우측의 일반 DLL 의 Export 방식차이

위의 그림에서 보는 것과 같이 좌측의 그림은 COM DLL 을 Dependency Walker 유틸리티로

본화면이고 마찬가지로 우측의 그림은 일반 DLL 을 본 것이다. 좌측은 네개의 Export 된 함

Page 5: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

수가 존재하고 우측의 일반 DLL 은 여러 개의 함수들이 존재한다. 무엇이 다른지는 금방 알

수 있다. 좌측에서 보는 COM DLL 은 앞에서 XecureWeb 이라고 언급했던 ActiveX 모듈파

일이다. 그런데 왜 앞에서는 document.all.Xecure.UpdateModules 처럼 자바스크립트 형식으

로 UpdateModules 함수를 호출했음에도 불구하고 COM DLL 파일의 Export 목록에 없는 것

일까? 분명히 오른쪽의 일반 DLL 에는 개발자가 자기가 Export 시킬 함수들 목록과 주소가

있는데 COM DLL 에는 그런 것들이 없다. 만일 IDA(DataRescue 사의 Interactive

Disassembler) 를 이용해서 오른쪽의 일반 DLL 을 디스어셈블리 시킨다면 각각의 함수들마

다 EntryPoint(엔트리포인트)에 브레이크를 거는 것은 식은죽 먹기일 것이다. 하지만 좌측의

COM DLL 만 갖고 UpdateModules 함수의 진입지점에 브레이크 포인트를 걸으라고 주문한

다면 어떻게 할 것인가? 참고로 [그림2] 타입라이브러리(TypeLibrary)에서 함수(Method)의

주소 같은 정보는 전혀 알 수 없다. 그렇다면 인터넷 익스플로어에 HTML 페이지를 읽어 들

이면서 브레이크 포인트를 걸고 Tracing(트레이싱)을 시작할 것인가? 정확히 UpdateModules

함수의 주소를 찾는 다는 것은 매우 힘든 일이고 찾아낸다면 오히려 박수를 보내야 마땅하

다. 하지만 COM 의 구조를 이해하면 의외로 답은 쉬운 곳에 존재한다. COM DLL 은 타입라

이브러리(TypeLibrary)를 통해서 자신이 Export 한 함수들이 있다고 분명히 알려주고 있기

때문에, 다만 그 정보를 어떻게 접근하느냐가 앞으로의 설명에서 핵심적인 부분이 될 것이

다.

필자는 오류를 겪는 것부터 설명하는 것을 좋아하는데 지금의 설명방식도 그렇게 진행할

것이다. 오류과정에서 더 많은 것을 배울 수 있다는 생각 때문이다. 그럼 지금부터 필자의

연구과정에 있었던 접근의 잘못된 점을 지적해 보겠다. 일단, 필자의 아이디어는 VC++ 을

활용하는 것이었다. VC++ 을 활용한다는 것은 VC++ 에 ActiveX 같은 COM 을 Import(임포

트)하는 기능이 있기 때문에 이를 이용하겠단 것이다. 참고로 COM 을 Import 하는 기능은

델파이에도 있으며 대부분의 언어에 그 기능이 존재한다. 다음의 화면을 보고 설명한다.

Page 6: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 4] 새 프로젝트를 MFC(exe) 용으로 생성함

반복해서 얘기하지만 지금부터 보게되는 내용은 필자가 COM 에 존재하는 UpdateModules

함수의 진입점(EntryPoint) 을 찾기 위한 연구과정이며 첫 번째로 진행한 연구에서 겪은 오

류를 먼저 설명하는 것이다. 그리고 대다수의 개발자라면 ActiveX Import 기능을 이미 다 알

고 있을 것이므로 다음에서 보는 화면들이 낯설지 않을 것이다.

일단 위에서 기본 프로젝트를 생성하고 다음처럼 XecureWeb 컨트롤을 임포트 시키자.

[그림 5] XecureWeb 4.0 컨트롤을 임포트 시킴

Page 7: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 6] 자동으로 XecureWeb 클래스를 생성해줌

위와 같이 클래스를 생성하겠냐는 질문과 함께 OK 를 누르게 되면 자동으로 XecureWeb

C++ 클래스가 생성된다. 참고로 필자가 써본 다른 Win32 용 언어라고는 델파이 밖에 없지

만 이러한 Import 수행과정은 델파이에서도 똑같았다. 일단, 이렇게 XecureWeb 을 임포트

시켜서 C++ 클래스까지 얻어내면 그 다음부터는 HTML 스크립트로 작성했던 것과 똑같이

UpdateModules 를 호출 시킬 수 있다.(혹시, 몰랐던 사람을 위해서 한가지 조언을 할 필요

가 있다면 자바스크립트로 프로그래밍하는 것과 VC++ 로 프로그래밍 하는 것이 코드의 양

만 다를 뿐 COM 을 다루는 입장에서는 기본적으로 그 구조가 똑같다.)

Page 8: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 7] XecureWeb ActiveX Control 을 조종할 수 있는 클래스

생성된 프로젝트 파일들의 소스를 보게되면 xecctl40.cpp 와 xecctl40.h 등등 새로운 파일들

이 생겨있고 위에서 보는 것처럼 long 형 반환값을 갖는 CXecCtl40 이란 클래스의

UpdateModules 라는 함수가 있다. 그리고 이 함수는 LPCTSTR 이라는 타입의 url 이라는 인

자를 매개변수로 취한다. 그 안에 보면 InvokeHelper 라는 함수가 앞에서 타입라이브러리에

서 보았던 0x26 이라는 디스패치(Dispatch) 번호로 함수를 호출하고 있다. 그렇다면 이 함수

가 우리가 찾던 UpdateModules 함수의 엔트리 포인트인가? 절대로 그렇지 않다. 그 이유는

지금 보는 것은 컴파일도 되지 않은 소스일 뿐이다. 그리고 이 소스는 우리가 만든 프로그

램 내부에 있는 것이며 우리가 원하는 UpdateModules 함수는 XecureWeb 바이너리 안에있

다.(XecureWeb 이 인스턴스(Instance)로 생성되면 메모리(코드영역)에 올라가있음) 그 바이너

리 안에 있는 UpdateModules 라는 함수(국내 정서상 Method 라 안하고 함수라 하겠음) 본

체의 시작주소를 원하는 것이지 우리가 만든 소스에서 시작점을 찾아봐야 아무 소용없다.

그렇다면 InvokeHelper 라는 함수를 Tracing(추적)해야 할 필요가 있다고 느낄 것이다.

Page 9: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 8] InvokeHelper 에 브레이크 걸고 추적하기(디버깅)

앞에서 필자가 언급한 내용중에서 UpdateModules 함수의 실제 엔트리 포인트를 찾기 위해

서 인터넷 익스플로어를 열고 HTML 스크립트로 작성된 document.all.Xecure.UpdateModul

es 구문을 추적하면서 함수가 호출되는 것을 추적하는 것은 엄청난 뻘짓이라고 하였다. 그

렇지만 여기서는 상황이 달라졌다. 지금 우리는 인터넷 익스플로어에서 HTML 스크립트로

UpdateModules 함수를 호출하는 것과 똑같이 호출할 수 있게 되었기 때문이다. 앞서 필자

가 언급한 것처럼 인터넷 익스플로어에서 자바스크립트로 짜는 거나 VC++ 로 호출하는 거

나 COM 프로그래밍에 있어서 구조적으로는 똑같다고 하였다. 단지 자바스크립트로 이렇게

하는데는 한계가 있다는 것뿐이다. 자바스크립트는 포인터를 다룰 수 없으므로 지금 같은

추적은 힘들다. 다음의 그림에서 VC++ 로 UpdateModules 를 어떻게 호출하게 되는지 보

자.

[그림 9] xecctl40.h 헤더추가

Page 10: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

위와 같이 일단 Import 해서 생성된 XecureWeb 클래스의 헤더를 PowerHackerDlg.cpp 에

추가해줘야 한다. 그리고 다이얼로그 기반 MFC 프로젝트 생성시 이미 작성된 OK 버튼에

함수를 추가하여 다음과 같이 코딩을 한다.

[그림 10] UpdateModules 함수 호출하기

위와 같이 코딩을하고 UpdateModules 함수에 브레이크를 걸어놓는다. 그리고 디버깅을 시

작하면서 추적을 해보자. 다시한번 얘기하지만 쉽게 생각하고 접근했던 필자의 오류과정에

대해서 설명하고 있다. 아마 누구나 빠르게 접근하고자 한다면 이 오류를 범할 것이라고 판

단하였다.

[그림 11] 디버깅 시작을 하고 OK 버튼을 누르면 호출구문에서 브레이크 걸림

Page 11: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

위의 그림에서 보는 것처럼 UpdateModules 함수호출 구문의 시작에서 브레이크가 걸리게

된다. 이 구문을 앞에서 보았던 document.all.Xecure.UpdateModules 구문과 정확히 똑같다

고 생각하자. 무슨 의미인지는 이미 두번이나 언급하였다. VC++ 이나 자바스크립트나 COM

프로그래밍 구조상으로는 똑같다고 하였다. 그러나 자바스크립트로는 더 이상 추적이 안될

것이다. 그러나 우리는 여기서 F11 을 누르면 한 단계 더 안으로 진입하면서 내부의 호출까

지 추적해 들어갈 수 있다. 그럼 F11 을 눌러서 추적을 해보자.

[그림 12] 추적해서 들어가면 앞에서 보았던 XecureWeb 클래스 구현쪽으로 들어감

위의 화면에서 보는 것처럼 한단계 더 추적해서 들어갔을 경우에는 우리가 만든 프로그램에

서 임포트한 XecureWeb 클래스 쪽으로 진입한다는 것을 알 수 있다. 현재 브레이크 위치는

우리가 작성한 프로그램의 메모리이다. 그러므로 이 주소는 UpdateModules 함수의 엔트리

포인트가 아니다. 더 추적해서 COM 서버에 등록되어 메모리에 실제 로딩되어있는

XecureWeb 컨트롤의 메모리 공간으로 추적해 들어가야 한다. InvokeHelper 라는 함수로 더

트레이싱(Tracing) 해서 들어가야 할 필요가 있다.

[그림 13] InvokeHelper 함수 안에는 또 다른 InvokeHelperV 함수가 존재

Page 12: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

보시다시피 F11 로 한 단계 더 들어갔음에도 불구하고 그 안에 InvokeHelperV 라는 함수가

또 호출되고 있다. 그리고 소스가 나오는 것으로 봐서 우리는 아직도 우리의 프로그램을 벗

어나지 못하고 있다. 맨 위에 빨간 박스를 보라 MFC\SRC\WINOCC.CPP 라고 되어있다. 그

뜻은 아직도 MFC 클래스 소스라는 뜻이다. InvokeHelperV 까지 더 추적해 들어가자.

[그림 14] 내부에 또 InvokeHelperV 함수가 존재한다.

추적해 들어갔더니 또 InvokeHelperV 라는 함수가 존재한다. 인내심을 갖고 딱 한번만 더

추적해 들어가보자. 그럼 다음과 같은 부분을 보게 될 것이다.

Page 13: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 15] 실제 호출이 이루어지며 XecureWeb 메모리(코드영역) 속으로 빨려들어감

위에서 “make the call” 이라는 주석이 보일 것이다. 바로 실제로 호출을 하는 부분이다. 이

렇게 어지럽게 이리넘기고 저리넘기는 구조가 COM 의 일반적인 프로그래밍 방법이다. 이

것을 (Dispatch)디스패치 구조라고도 하고 Delegate(델리게이트) 구조라고도 한다. 이러한 구

조가 짜증나고 복잡해 보이지만 COM 이 일반 DLL 처럼 실제 함수주소를 Export 하지 않아

도 요청(Query)만으로 내부의 함수들을 연결해줄 수 있는 이유이기 때문에 필수적으로 이해

해야 한다. 어찌됐던 간에 일단 Invoke 함수 속으로 한번 더 추적해야 한다. 그러나 이제

MFC 소스는 보이지 않게 될 것이다. 그렇기 때문에 사실 함수내부로 들어가지도 않게 될

것이다. Invoke 함수내부로 들어가려면 디스어셈블리(Disassembly) 화면으로 바꿔놓고 F11

을 누르면서 추적해야만 가능하다.

Page 14: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 16] 디스어셈블리를 켜 놓은 상태에서만 F11 로 CALL 함수 안쪽으로 들어갈수 있음

위에서 빨간 박스 안을 보라. 0x5F58A35D 주소에 call dword ptr [edx+18h] 명령이 호출되

어야 메모리에 생성된 실제 XecureWeb 메모리(코드영역) 속으로 들어갈 수 있다. 마지막으

로 F11 을 눌러서 한번 더 추적을 들어가보자. 정말 UpdateModules 함수의 엔트리 포인트

가 나오게 될 것인가?

Page 15: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 17] XWEBCLT! 00ba0840 으로 컨텍스트(Context) 영역이 바뀌었음

자, 드디어 원하던 XecureWeb(XWebCLT.dll)의 메모리(코드영역) 공간 속으로 들어왔다. 그렇

다면 이 주소가 UpdateModules 함수의 엔트리 포인트인가? 테스트 할 수 있는 방법은 아

주 간단하다. 지금 눈에 보이는 OPCODE 들을 모두 적는다. 그리고 그 OPCODE 들을 갖고

XecureWeb 의 바이너리 파일인 XWebCLT.dll 을 열어서 위치를 찾은 다음 첫 부분을 CC 로

수정한다.(누군가 XWebCLT.dll 파일이 XecureWeb 의 ActiveX 인지 어떻게 아냐고 묻는다면

OLE VIEW 라는 유틸리티로 보면 경로가 나온다고 말해주고 싶다.) 그 다음 자바스크립트로

작성한 HTML 파일을 인터넷 익스플로어에서 열었을 때 0xCC 에 해당하는 INT 3 어셈블리

명령이 실행되면서 UpdateModules 함수의 진입점에 브레이크가 걸린다면 제대로 찾은 것

이다. 과연 그렇게 될 것인가?

[그림 18] 바이너리에서 찾은 위치에 0xCC 로 수정을 하면 브레이크가 걸릴까?

Page 16: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

지금까지 열심히 설명한 것이 허무할 수 도 있지만 COM 에 대한 이해를 하지 못한 독자가

있다면 분명히 이 접근오류에 빠지기 쉬울 것이다. 앞에서 분명히 언급했지만 지금 본 과정

은 오류과정이다. 왜 이렇게 보여준 것일까? 아마 누구나 스스로 연구할 때 지금 생각하는

과정이 가장 접하기 쉬운 오류일 것이라고 생각했기 때문이다. 어찌됐든 일단, 테스트의 마

무리를 짓기 위해서 이렇게 CC 까지 바이너리에서 수정한 뒤에 HTML 파일을 열어보라. 아

무런 반응도 없을 것이다. 아무것도 안 잡힐 것이다. 설령 디버거가 뜨면서 오류가 난다고

하더라도 그 위치는 UpdateModules 함수의 진입점이 절대 아니다. 그렇다면 앞에서 추적을

통해 XecureWeb 모듈 안쪽까지 따라들어갔는데 그 주소의 위치는 어디란 말인가? 그 답은

XecureWeb 이라는 ActiveX COM 모듈을 개발한 개발자가 작성한 Invoke 라는 함수 속으로

들어간 것이다. 즉, 앞에 디스어셈블리 그림에서 봤던 주소인 XWEBCLT! 00ba0840 위치는

XecureWeb 컨트롤의 Invoke 함수이다. 다른 말로 하면 디스패쳐(Dispatcher) 함수로 들어간

것이다. 이를 쉽게 풀이하면 XecureWeb COM 모듈 또한 외부에서 접근하려고 할 때 내부의

함수를 직접 호출하도록 하는게 아니라 어떤 함수를 호출하겠다는 요청이 있으면 자기가 관

리하고 있는 내부 함수를 스스로 골라서 호출하도록 하는 형식인 디스패쳐(Dispatcher) 형식

을 취하고 있다는 것이다. 직접 분석해 본다면 우리가 본 주소는 Invoke 함수라는 것을 쉽

게 알 수 있다. 필자가 이걸 금방 알게된 이유는 UpdateModules 함수가 아니라 BlockEnc

와 같은 함수를 호출하면서 추적해도 결국 도착위치가 XWEBCLT! 00ba0840 주소였기 때문

이다. 이러한 구조는 디스패쳐일 수 밖에 없으며 직접 IDA 로 분석해보니 Invoke 함수였다.

만일, 이 위치에서 UpdateModules 함수까지 찾아들어갈려고 한다면 지금 우리가 추적하면

서 겪었던 험난한 과정들을 다시 거슬러서 해당 함수까지 찾아 들어가야 한다. 그야말로 어

마어마한 쌩 노가다가 따로 없는 것이다. 즉, 우리의 고민인 UpdateModules 함수의 실제주

소를 찾기위해서 습관처럼 디버거로 추적하는 접근방식은 버리고 이론적으로 접근해야한다.

이때 COM 의 원리를 잘 알고 COM 의 구조를 이용하면 지금처럼 비참한 연구결과가 나오

지 않고 쉽게 우리가 원하는 UpdateModules 함수의 엔트리포인트를 찾을 수 있다.

이쯤에서 잠시 머리를 식히는 기분으로 다음의 게시판 내용을 읽어보자.

Page 17: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 19] 같은 문제에 대해서 연구할 사람을 찾기위해 올린 글 (답글이 한 개도 없다 -_-)

필자는 위와 같이 나름대로 생각해온 문제에 대하여 글을 올렸지만 아무도 답글을 달지않는

것을 보고 혼자서만 뻘짓을 하는가란 의문에 빠졌었다. 그러던 어느날 필자가 서버를 운영

하고 있는 파워해커(powerhacker.net)에서 홍텐(Hong10)님이 ActiveX 취약점을 찾는 법을 알

고 싶다고 하셨다. 대략 한 두 시간에 걸쳐 모든 설명을 해드렸고 홍텐님이 엄청난 속도로

파고드는 것을 보게되었다. 처음 파워해커 게시판에 나타났을 때부터 놀라울 정도의 집중력

을 발휘하는 모습에 감탄했는데 하루 이틀이 지나 홍텐(Hong10)님이 M모 게임사에 존재하

는 BOF 취약점을 발견하시게 되었다. 필자는 매우 놀랐지만 그 이유는 BOF 때문이 전혀 아

니었다. 일주일 전쯤 한게임(Hangame) ActiveX 익스플로잇(Exploit)을 공개했었던 필자한테는

그 취약점이 사실 그리 놀라울 만한 주제거리는 아니었기 때문이었다. 그런데도 필자가 홍

텐님에게 매우 놀랄 수 밖에 없었던 것은 바로 필자가 앞서 고민했던 문제에 대해서 질문을

했기 때문이었다. 홍텐님은 툴의 도움을 받고 싶지 않고 직접 바이너리에서 COM 메쏘드(헷

갈릴까봐 함수)에 해당하는 주소를 찾아 스스로 어셈블리를 분석하여 취약점 도출을 해내고

싶어하셨던 것이다. 세상천지에 같은 생각을 하고 있는 사람이 또 있었다니 매우 놀랄만한

Page 18: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

일이아닌가. 기존에 ActiveX 취약점을 찾아내고 분석하는 사람들이 이런 고민을 하는 것을

본적도 없고 질문, 의향 등등을 묻는 것은 물론이고 게시판에 먼저 아이디어를 제공했는데

도 불구하고 같이 연구하자는 사람조차 없었다. 그런데 질문을 받게되자 매우 기뻤던 것이

다. 하지만 아쉽게도 그때까지는 홍텐님께 아무런 답변을 해드릴 수 없었다. 연구가 끝나지

않았기 때문이었다. 그때부터 필자는 꾸준히 생각한 아이디어를 구체화 시키기로 하였다. 이

제 그 결과물을 얻어냈기 때문에 이러한 연구내용을 공유하려 한다.(IDA Plugin 형식으로 만

들려고 노력하였으나 단기간에 이 작업까지 하는 데는 상당한 어려움이 있었음. 어쩌면 기

술적 한계에 걸려 범용으로 만드는 것 자체가 불가능한 것일 수 도 있음.)

지금부터 보게 되는 내용은 필자가 얻어낸 해답이며 앞으로도 계속해서 꾸준히 진행하려고

하는 프로젝트와 같다. 굳이 이름을 지어야 한다면 Virtual Function Table Dumper Project

라고 부를 수 있다.

#include "stdafx.h"

#include <atlbase.h>

#include <iostream>

#include "ComString.h"

#import "C:\\Program Files\\SoftForum\\XecureWeb\\ActiveX\\XWebCLT.dll"

no_namespace named_guids raw_native_types

void dumpcode(unsigned char *buff, int len)

{

int i;

CCOMString Debug, OutDebug;

for(i=0;i<len;i++)

{

if(i%16==0)

{

Debug.Format("0x%08x ", &buff[i]);

OutDebug += Debug;

}

Debug.Format("%02x ", buff[i]);

OutDebug += Debug;

Page 19: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

if(i%16-15==0)

{

int j;

OutDebug += " ";

for(j=i-15;j<=i;j++)

{

if(isprint(buff[j]))

{

Debug.Format("%c", buff[j]);

OutDebug += Debug;

}

else

{

OutDebug += ".";

}

}

OutDebug += "\n";

}

}

if(i%16!=0)

{

int j;

int spaces=(len-i+16-i%16)*3+2;

for(j=0;j<spaces;j++)

{

OutDebug += " ";

}

for(j=i-i%16;j<len;j++)

{

if(isprint(buff[j]))

{

Debug.Format("%c", buff[j]);

OutDebug += Debug;

}

else

Page 20: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

{

OutDebug += ".";

}

}

}

OutDebug += "\n";

printf("%s\n", OutDebug.GetString());

}

void main(int argc, char *argv[])

{

USES_CONVERSION;

BSTR SearchFunc = NULL;

int SearchIndex = -1;

if(argc == 2)

{

if(isdigit(argv[1][0]))

{

SearchIndex = atoi(argv[1]);

SearchFunc = ::SysAllocString(L"Don't Find");

}

else

{

SearchFunc = ::SysAllocString(T2OLE(argv[1]));

}

}

printf("===========================================\n");

printf(" Programmed by AmesianX in powerhacker.net\n");

printf("===========================================\n");

printf(" USAGE: %s [FuncName or FuncIndex]\n", argv[0]);

printf(" Exam: %s UpdateModules\n", argv[0]);

printf(" Same: %s 40\n", argv[0]);

Page 21: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

printf("===========================================\n\n");

HRESULT hr = CoInitialize(NULL);

if (FAILED(hr))

{

printf("ERROR - Could not initialize COM library");

exit(1);

}

IXecCtl40 *pXecCtl40;

hr = CoCreateInstance(CLSID_XecCtl40, NULL, CLSCTX_INPROC_SERVER,

IID_IXecCtl40, (void **)&pXecCtl40);

if (FAILED(hr))

{

printf("ERROR - Could not create the XecCtl40.");

exit(1);

}

else

{

CCOMString szTemp;

szTemp.Format("0x%x", *pXecCtl40);

DWORD *lpTemp[100];

sscanf(szTemp.GetString(), "0x%x", lpTemp);

DWORD *lpVftbl = (DWORD *)*lpTemp;

printf("INSTANCE (this)pXecCtl40 = 0x%x\n", pXecCtl40);

printf("VIRTUAL FUNCTION TABLES (_vfptr)lpVftbl = 0x%x\n\n", lpVftbl);

CComPtr<ITypeLib> spTypeLib;

CComPtr<ITypeInfo> spTypeInfo;

CComQIPtr<IProvideClassInfo> spPCI = pXecCtl40;

if( spPCI )

{

CComPtr<ITypeInfo> pClassInfo;

Page 22: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

hr = spPCI->GetClassInfo(&pClassInfo);

if( SUCCEEDED(hr) )

{

BSTR szClassName;

hr = pClassInfo->GetDocumentation(-1, &szClassName, 0, 0, 0);

printf("CLASS = %S\n", szClassName);

if( SUCCEEDED(hr) )

{

UINT Reserved = 0;

hr = pClassInfo->GetContainingTypeLib(&spTypeLib,

&Reserved);

}

}

}

UINT n = spTypeLib->GetTypeInfoCount();

for(int i = 0; i < n; i++)

{

BSTR szInterfaceName;

spTypeInfo = NULL;

hr = spTypeLib->GetTypeInfo(i, &spTypeInfo);

hr = spTypeInfo->GetDocumentation(-1, &szInterfaceName, 0, 0, 0);

printf("INTERFACE = %S\n", szInterfaceName);

TYPEATTR *pTypeAttr;

spTypeInfo->GetTypeAttr(&pTypeAttr);

int index;

for(index = 0; index < pTypeAttr->cFuncs; index++ )

{

int RestrictedMethods = 7;

FUNCDESC* pFuncDesc = 0;

hr = spTypeInfo->GetFuncDesc(index, &pFuncDesc);

Page 23: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

if(SUCCEEDED(hr) && !(pFuncDesc->wFuncFlags & 1))

{

printf("\n");

BSTR szMethodName;

hr = spTypeInfo->GetDocumentation(pFuncDesc->memid,

&szMethodName, 0, 0, 0);

if(SUCCEEDED(hr))

{

printf("(%d)[ID:0x%08x] *** %S *** \n Virtual

Function Address = 0x%x\n",

index - RestrictedMethods, pFuncDesc->memid,

szMethodName, *(lpVftbl+index));

if(!wcscmp(SearchFunc, szMethodName) ||

SearchIndex == (index - RestrictedMethods))

{

printf("\n");

dumpcode((unsigned char *)*(lpVftbl+index), 0x40);

/*

==== 테스트 호출 ====

BSTR test = ::SysAllocString(L"TEST");

pXecCtl40->UpdateModules(test);

::SysFreeString(test);

*/

}

}

}

}

}

}

if(SearchFunc != NULL)

::SysFreeString(SearchFunc);

pXecCtl40->Release();

CoUninitialize();

}

[그림 20] Virutal Function Table(가상함수 테이블) 출력기 소스

Page 24: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

자, 지금 우리가 위에서 본 것이 바로 Virtual Function Table Dumper 의 전체소스이다. 그렇

다면 이 프로그램이 어떻게 작동하는지 궁금할 것이므로 사용하는 방법과 결과를 눈으로 보

자.

[그림 21] Virutal Function Table(가상함수 테이블) 출력기 실행

위와 같이 COM 의 Instance 값과 가상함수 테이블의 주소들 그리고 함수(Method) 명 까지

모두 뽑아내는 것을 볼 수 있다.

Page 25: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 22] Virutal Function Table(가상함수 테이블) 출력기 실행

위에서 보는 것처럼 바이너리를 덤프까지 출력하도록 프로그래밍 하였는데 그 이유는 바이

너리 시퀀스(Binary Sequence)를 따서 실제 바이너리에 해당되는 함수위치를 찾기 위함이다.

디스어셈블러까지 붙일려고 했지만 시간이 촉박하여 그렇게 하지 못했다. 이처럼 가상함수

테이블에 존재하는 함수의 주소를 얻어내어 UpateModules 함수의 주소(메모리에 적재된 코

드영역의 주소) 를 알아내었다. 앞에서 오류를 범했던 삽질에 비해 상당히 간단하다고 생각

할지 모르겠다. 일단, 이러한 원리를 설명하기 전에 진짜 UpdateModules 함수의 주소가 맞

는지 먼저 테스트 해보자. 그림24 에서 UpdateModules 함수의 주소를 보게되면

Page 26: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

0x10009020 이라고 되어있다. 그 주소를 IDA 에서 찾아야 한다. XWebCLT.dll(XecureWeb

ActiveX 모듈) 파일을 IDA 로 로딩한 뒤에 주소찾기를 이용해서 0x10009020 위치로 가보자.

[그림 23] 실제로 가상함수 테이블에서 찾아낸 주소가 있는지 확인

[그림 24] 주소가 존재함. UpdateModules 함수(Method)에 브레이크를 걸고 호출 준비

일단 IDA 에서 찾아보니 Virtual Function Table Dumper 로 얻어낸 주소위치가 존재한다. 위

와 같이 함수 앞단에 브레이크를 걸자.

Page 27: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 25] Debug application setup 메뉴에서 인터넷익스플로어를 Application 으로 설정함

디버거를 작동시키면서 인터넷 익스플로어가 작동해야만 Example.html 파일을 열어볼 수

있기 때문에 위의 화면처럼 인터넷 익스플로어를 Application 항목에 입력하고 F9 버튼을

눌러서 디버거를 실행시켜보자. 그러면 인터넷 익스플로어가 실행되는 것을 보게 될 것이다.

[그림 26] Exeption 이 발생할 것이다!! 무시해도 좋음

위와 같이 IDA 디버거로 인터넷 익스플로어를 실행시키면 익셉션(Exception)이 발생하면서

화면이 깨질텐데 무시해도 상관없는 익셉션이므로 다음과 같이 설정해주고 ”Yes”를 누르면

Page 28: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

인터넷 익스플로어가 정상적으로 디버거에서 작동할 것이다.

[그림 27] 익셉션 이름설정, Stop program 체크제거

[그림 28] 파일열기로 이미 UpdateModules 함수를 호출하는 HTML 파일을 로드함

익셉션을 무시하고 인터넷 익스플로어가 정상적으로 작동하면 위와 같이 파일열기로

Example.html 파일을 열어보자. Example.html 파일에는 document.all.Xecure.UpdateModules

함수를 호출하는 자바스크립트 구문이 있으므로 호출을 시도하게 될 것이다.

Page 29: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 29] 정확히 UpdateModules 함수에서 브레이크가 걸림

그림31 에서 자바스크립트인 document.all.Xecure.UpdateModules 구문이 작동할 때 브레이

크가 걸려서 멈추게 되는 것을 알 수 있다. 앞에서 덤퍼(Dumper)로 찾은 주소가 실제

UpdateModules 함수의 주소가 맞다는 의미이다. 다시한번 정확하게 UpdateModules 함수

(Method)의 엔트리포인트인지 판단하기 위해서 F8(Trace) 기능으로 스택을 관찰해보자. 참고

로 Example.html 파일은 다음처럼 처음과 달리 조금 수정되었다.

<SCRIPT LANGUAGE='JAVASCRIPT'>

document.all.Xecure.UpdateModules("http://About:Blank");

</SCRIPT>

다음 화면은 Trace(F8) 로 어셈블리 한 스텝씩 이동하면서 스택에 "http://About:Blank" 라는

문자열이 들어오는지 확인해본 것이다.

Page 30: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 30] 트레이싱을 좀 하다보면 스택에 입력인자가 들어오는 것이 확인됨

위의 그림32 처럼 오른쪽에 스택의 내용을 보면 aHttpAboutBlank 라고 적혀있다. 이 값을

따라가면 우리가 인자로 넘겨줬었던 " http://About:Blank " 문자열이 존재하는 것을 알 수

있다. 즉, 실제의 UpdateModules 함수의 진입점(EntryPoint) 을 정확히 찾은 것이다. 혹시라

도 UpdateModules 함수에 BOF 취약점이 존재하는지 어셈블리를 분석해 보고 싶은 사람이

있다면 HTML 스크립트를 작성하지 말고 IDA 만으로 분석하면 된다.

<소스의 분석과 가상함수 테이블 덤프에 대한 원리>

이제 앞에서 보았던 소스에 대하여 약간의 설명을 할 것이다. Virtual Function Table

Dumper 소스는 전체적으로 COM 프로그래밍을 해본 사람은 이해하는데 있어서 어려울 것

이 없는 내용이다. 혹시 COM 프로그래밍을 모르는 사람이 있다면 공부하라. 그 사람을 위

해서 COM 을 다 설명하는 것은 이 Megazine 의 영역을 벗어나는 것이다. COM 이란 주제

만으로도 충분히 책 한권 분량의 글을 쓸 만큼 방대하기 때문이다. 그래도 여기서는 중요한

COM 의 원리에 대해서는 설명을 진행할 것이다. 그래야 핵심원리를 파악할 수 있기 때문

이다. 다음의 그림을 보면서 설명한다.

Page 31: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 31] 상속과정 (TypeLibrary Viewer 화면)

[참고사항]: ActiveX 나 COM 파일을 열면 리소스 영역에 타입라이브러리가 탑재됨.(리소스

편집기로 볼 수 있음. 타입라이브러리는 C++ 에서 처럼 클래스의 헤더나 마찬가지이다.)

위에서 보는 구조는 거의 모든 COM 모듈에서 나타나고 있는 상속 관계이다. 클래스나 구

조체로 그림을 그려보자. 그러면 그림에서 처럼 IUnknown 이 가장 최상위이고 IDispatch 가

그 다음이고 개발자가 만드는 COM 인터페이스가 가장 밑에 있다. 개발자가 개발하는 거의

모든 COM 이 이러한 상속관계를 거치는 것이 대부분이다. 이것을 메모리의 레이아웃

(Layout) 으로 보게되면 다음의 그림과 같다.

[그림 32] 메모리 레이아웃

Page 32: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

위의 메모리 틀에서 상속과정은 포인터로 이루어진다. 즉, IUnknown 과 IDispatch 라는 두개

의 인터페이스를 상속받아 XecureWeb 이라는 COM 을 만들었으므로 가장 상위에 둘이 갖

고있는 기본 함수들이 존재해야 한다. 그리고 그 다음에 XecureWeb 의 함수들이 이어질 것

이다. 그것을 가리키는 포인터가 바로 __vfptr(가상함수 포인터) 이다. 이 가상함수 포인터의

[0x0] 번째에는 QueryInterface 함수가 위치하고 그 다음 [0x1] 번째는 AddRef 함수, [0x2]

번째는 Release 함수가 존재할 것이다. 그렇다면 [0x3] 번째는 어떻게 될 것인가? 예상대로

바로 IDispatch 가 갖고있는 함수들이 이어진다. [0x4] 번째는 GetTypeInfoCount 부터 시작하여

[0x5] 번째 GetTypeInfo, [0x6] 번째 GetIDsOfNames, [0x7] 번째가 Invoke 함수가 들어간다. 아니 필자

는 이걸 어떻게 알고 있을까라고 궁금해 하지마라. 이미 그림33 의 상속과정에 빨간색 선으로 다 그

려놓은 것을 고대로 배열순서로 적어낸 것 뿐이다.(C++ 을 이해하고 있다면 쉬운내용이다.) 그렇다면

이제 이해가 될 것이다. [0x8] 번째 부터는 XecureWeb 컨트롤에 구현된 함수(Method)들의 주소가 틀

어박혀 있다고 예측을 하였을 것이다. 그렇다 바로 그 주소들이 바로 실제 함수들의 몸체를 가리키는

주소이다. 그렇기 때문에 그 주소들을 덤프뜨려고 하는 것이다. 그런데 그 주소를 가리키는 과정이

COM 을 이해해야 한다는 것이다. 그래서 꼭 필요한 개념이 IUnknown 이란 것이다. 다음의 그림을

보면 IUnknown 이란 놈이 정의된 헤더인데 구조체와 똑같이 생각하면 된다는 것을 알 수 있다. COM

을 다루는데 있어서 C 로 다룰 수 있도록 제공하는 IUnknown 의 인터페이스 모양이 다음과 같이 생

겼다.

[그림 33] IUnknown 은 구조체(클래스) 포인터와 다를 바 없다.

쉽게 생각해서 COM 에서 인터페이스라는 단어를 모두 구조체라고 생각을 하자. 그러면 위

에서 보는 것처럼 IUnknown 구조체는 IUnknownVtbl 구조체의 주소를 지정할 수 있는 포인

터변수 lpVtbl 란 놈을 갖고 있는게 전부이다. 그리고 IUnknownVtbl 역시 구조체이고 세개

Page 33: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

의 함수포인터 변수를 갖고있으며 lpVtbl 에 의해 지정받아지고 있다. 즉, 모두 어느 주소를

지정하는 가에 대한 것일 뿐이며 실체(본체)는 없다. 본체는 나중에 포인터만 알면 본체의

주소를 포인터들이 가리키는 곳에 구현해서 넣어주는 것이고 실체는 다 포인터들이다.

IDispatch 가 IUnknown 을 상속받는다면 그 의미는 IUnknown 의 포인터를 이어받는 꼴이

다. 정확히 C++ 의 개념과 일치한다는 것이다. COM 은 C++ 그 이상도 이하도 아니다. 단

지 자동화 처리가 있는가 없는가의 차이인데 QueryInterface 는 현재 갖고 있는 인터페이스

들을(예를들면 IDispatch) 요구하게되면 해당 포인터를 넘겨주는 것이고 AddRef 는 현재

COM 인스턴스가 참조되고 있는가를 카운팅하며 Release 는 그 참조카운터를 줄이고 제거

하는게 전부이다. 필자의 경험상 IUnknown 에 있는 함수들은 쓸모가 없으면 굳이 구현해주

지 않아도 작동만 잘 되었다. (물론, 불안하겠지만 작동은 잘됨)

(참고: 위에서 본 것처럼 pXecCtl40->__vfptr 과 같이 가상함수 포인터(Virtual Function

Pointer)에 직접 접근할 수 없다.)

이제 소스에서 보는 인스턴스(Instance)인 pXecCtl40 변수는 VC++ 에서 XecureWeb DLL 파

일(XWebCLT.dll) 을 임포트 했기 때문에 IXecCtl40 *pXecCtl40; 와 같이 선언하여 사용할

수 있다. 임포트(Import) 를 하게되면 헤더파일이 생성되고 그 파일안에 IXecCtl40 클래스가

정의되어 있다. 그 다음 다음과 같이 CoCreateInstance 함수로 pXecCtl40 변수에 실제 인스

턴스를 할당하게 된다.

hr = CoCreateInstance(CLSID_XecCtl40, NULL, CLSCTX_INPROC_SERVER, IID_IXecCtl40, (void

**)&pXecCtl40);

위의 함수에서 CLSID_XecCtl40 과 IID_IXecCtl40 은 각각 헤더파일에 선언되어 있으며

CLSID_XecCtl40 은 XecureWeb 의 클래스 아이디이며 IID_IXecCtl40 은 타입라이브러리의

아이디를 의미한다. 필자가 프로그래밍하면서 착각한 것 중에서 IID_IXecCtl40 (타입라이브러

리 ID) 를 지정해줘야 하는데 범용적인 IID_IUnknown 을 지정해줘서 한참을 고생했었다.

IID_IUnknown 를 지정해서 얻으면 앞서 설명한 것처럼 가상함수 테이블을 가리키는 포인터

그 자체를 쉽게 얻어낼 것이라 생각했고 테이블에서 함수들을 그대로 긁으려고 생각했지만

어처구니 없이 전혀 다른 위치를 가리키고 있었다.(엉뚱하게 가리켜도 XecureWeb 의 코드

영역이기에 분석하기 전까지는 당연히 모를 수 밖에 없었음)

Page 34: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

[그림 34] IID_IUnknown 으로 COM 을 생성했을 때 가상함수 테이블이 잘 못 구해짐

이제 소스에 대해서 좀 더 중점적으로 설명한다. 소스에서는 OLE VIEW 라는 VC++ 에 기본

적으로 포함된 유틸리티로 봤을 때 존재하는 함수들 목록을 타입라이브러리로부터 뽑아내는

코드가 주를 이루고 있다. 간단히 설명하면 pClassInfo->GetContainingTypeLib(&spTypeLib,

&Reserved); 와 같은 형태로 타입라이브러리를 spTypeLib 에 포인터를 얻어온 후에

UINT n = spTypeLib->GetTypeInfoCount();

for(int i = 0; i < n; i++)

{

BSTR szInterfaceName;

spTypeInfo = NULL;

hr = spTypeLib->GetTypeInfo(i, &spTypeInfo)

위의 코드처럼 타입라이브러리에 존재하는 모든 인터페이스에 대해 루프를 돌면서 함수들을

뽑아낸다. 이러한 일련의 과정은 함수의 주소를 뽑아낸다기 보다는 타입라이브러리를 기반

으로 함수들의 오프셋을 뽑아내는 과정이라고 할 수 있다. 함수의 주소를 뽑을 수 있는 방

법을 찾아봤지만 존재하지 않았다. 위의 코드에서 보면 GetTypeInfo 함수로 spTypeInfo 변

수에 한 개의 인터페이스를 얻어낸다. 그리고 그 인터페이스에 존재하는 함수들을 찾아낸다.

물론, 지금 보는 것처럼 GetTypeInfo 에 i 라는 순차번호로 인터페이스를 지정하는 것처럼

함수역시 이런 순차번호(몇 번째 있는가? 즉, 오프셋) 로 얻어내는 것 밖에는 안된다.

Page 35: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

TYPEATTR *pTypeAttr;

spTypeInfo->GetTypeAttr(&pTypeAttr);

int index;

for(index = 0; index < pTypeAttr->cFuncs; index++ )

위의 코드에서는 spTypeInfo 가 타입라이브러리에서 한 개의 인터페이스를 뽑아냈으므로 다

시 GetTypeAttr 함수로 그 인터페이스의 속성을 뽑아낼 수 있다. 뽑아낸 속성을 pTypeAttr

변수에 할당하게되면 pTypeAttr->cFuncs 라는 변수를 취함으로써 그 인터페이스에 몇 개의

함수가 존재하는 가를 알아낼 수 있다. 그 함수 개수만큼 루프를 돌면서 다시 함수의 속성

을 뽑아낸다.

FUNCDESC* pFuncDesc = 0;

hr = spTypeInfo->GetFuncDesc(index, &pFuncDesc);

위와 같이 FUNCDESC 변수 pFuncDesc 를 하나 선언하고 인터페이스인 spTypeInfo 에서

GetFuncDesc 함수로 index 번째 함수의 속성을 뽑아내서 pFuncDesc 변수에 담아버리면 현

재 인터페이스의 index 번째 함수의 이름을 뽑아낼 수 있다. 이렇게 되면 pFuncDesc-

>memid 로 현재 함수의 디스패치 번호도 알아낼 수 있는데 이 디스패치 번호를 알아야 함

수이름을 얻어낼 수 있다. OLE VIEW 를 보면 디스패치 번호가 있다고 앞서 그림으로 보여

준 바 있다.

hr = spTypeInfo->GetDocumentation(pFuncDesc->memid, &szMethodName, 0, 0, 0);

위와 같이 함수이름을 얻어낼 수 있고, 소스에서 주목할 부분이 다음이다.

printf("(%d)[ID:0x%08x] *** %S *** \n Virtual Function Address = 0x%x\n",

index - RestrictedMethods, pFuncDesc->memid, szMethodName, *(lpVftbl+index));

if(!wcscmp(SearchFunc, szMethodName) || SearchIndex == (index - RestrictedMethods))

{

printf("\n");

dumpcode((unsigned char *)*(lpVftbl+index), 0x40);

출력 시 index – RestrictedMethods 라고 한 이유는 RestrictedMethods 는 숫자 7 인데, 앞

에서 IUnknown 과 IDispatch 의 함수가 총 7개라서 그렇다. 우리는 처음에 lpVftbl 변수에

Page 36: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

가상함수 테이블을 가리키는 포인터를 얻어냈는데 lpVftbl 은 완전히 테이블의 처음부터 가

리키고 있다. 즉, IUnknown 부터 가리키고 있으므로 총 7 개의 함수를 빼야 XecureWeb 의

함수들부터 시작한다는 것이다. 이 처리를 없애면 다음과 같이 출력된다.

[그림 35] 제한된(Restricted) 함수 출력 처리를 안 하면 상속받은 함수부터 출력됨

즉, 정상적으로 인터페이스에서 함수들을 뽑아내는 루틴자체도 상속받은 것부터 얻어온다.

hr = spTypeInfo->GetFuncDesc(index, &pFuncDesc);

if(SUCCEEDED(hr) ) && !(pFuncDesc->wFuncFlags & 1))

그래서 위와 같이 spTypeInfo(인터페이스) 에서 GetFuncDesc 로 0 번째부터 뽑아왔을 때 그

함수가 QueryInterface 같은 함수라면 pFuncDesc->wFuncFlags 속성을 검사해서(1을 &연산

하면 알 수 있음) 제한된(Restricted) 함수인지 알아낸다. 만약 제한된 함수라면 index 만 증

가하면서 다음함수로 계속 넘어갈 것이고 BlockEnc 라는 함수가 나오면 제한된 함수가 아니

Page 37: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

라 개발자가 만든 함수이므로 이 함수부터 출력하게 한 것이다. 또 하나 중요한 것이 출력

부분의 *(lpVftbl+index) 라고 된 부분이다. 우리가 가상함수 테이블을 가리키는 포인터를 구

했고 그 테이블로부터 index 번째만 계속 더해나가면서 주소를 뿌리게하면 모든 함수들에

매칭되는 함수주소를 뽑아낼 수 있는 것이다.

만약 프로그램 실행 시 어떤 함수의 OPCODE 를 알고 싶은지 인자로 함수의 이름이나 순번

을넘기면 우리는 타입라이브러리에서 함수이름을 뽑아서 매칭되는지 확인하고 가상함수 테

이블에서 그 함수의 주소를 얻어내어 dumpcode 로 해당 함수의 OPCODE 를 뿌려주면 되

는 것이다. 이렇게 하면 우리는 모든 ActiveX 뿐만이 아니라 타입라이브러리를 갖고있는

COM 모듈에 대해서 원하는 함수의 주소를 뽑아낼 수 있다.(간혹, 타입라이브러리가 없는

COM 도 있음) 이상으로 원하는 함수의 주소를 마음대로 뽑을 수 있게 되었다. 참고로 지금

보여주는 소스는 완전한 범용이 아니다. 모든 COM 에 대해서 컴파일 할 필요 없이 함수들

을 뽑아주지는 못한다는 것이다. 그것이 IDA 플러그인(plug-in)으로 완전 자동화시키지 못한

이유이다. 하지만, 결과물을 IDA 에 적용시키는 프로젝트를 진행할 것이다. 참고로 말씀 드

리는 것은 사실 이 소스 자체로만 보면 거의 범용이다. 왜냐면 import 에 지정한 DLL 파일

만 바꿔주고 CoCreateInstance 함수에서 CLSID 와 타입라이브러리 ID 를 원하는 ActiveX 로

지정해주면 컴파일해서 바로 출력해보면 되기 때문이다. 한 두줄 정도 수정하고 컴파일해야

하는 거라면 그다지 불편할 일은 없다. 다만, IDA 에서 분석하면서 함수이름을 볼 수 있으면

더 편하다는 것인데 예를들면 지금의 프로그램을 실행시켜서 함수주소와 이름을 뽑아내어

결과물을 만들고 이를 IDA 에서 플러그인으로 읽어들이면 디스어셈블리 상태에서 주소로만

보이던 함수들이 전부 함수이름으로 싹 바뀌어 버릴 것이다. 이런 IDA 플러그인을 만드는

것은 매우 쉬운 일이다. 하지만 이로써 ActiveX 를 분석하는데 있어서 엄청 쉬워지는 것이

다. 이로써 필자는 이 매거진을 통하여 모든 ActiveX(COM) 함수들을 분석할 시 타입라이브

러리를 보지않아도 IDA 만으로 분석할 수 있는 실전적인 접근법을 제시하였다. BOF 같은

취약점을 IDA 로만 분석하여 찾아내고 싶은 사람은 필자가 제시한 이 기법을 활용해보기

바란다.(이 문서가 작성되고나서 몇 달이 지난 후에 COM 헬퍼라는 2002년도 에 작성된

IDA Pro 용 플러그인이 존재하는 것을 발견하였고, 그 방대한 양의 플러그인을 조금 분석하

였는데 지금 필자가 제작한 것과 거의 같은 원리로 제작되어있다는 것을 알아내었다. 관심

있는 독자들은 찾아보고 연구하길 권한다. 현재는 IDA Pro 5.1 버전에서 작동되도록 재수정

된 2007 년 버전까지 나왔다. 인터넷에는 이처럼 알려지지않은 혹은 알고있어도 값어치를

메길수 없는 진귀한 보석들이 주위에 있다는 것을 새삼 다시 느끼게 해준다.)

<필자의 화>

누구나 다 알고있지만 하지않으면 모르는 것과 다를바 없다. 도전하지않고 이루지않으면 못

하는 것과 안하는 것의 차이는 아무것도 없다. 한 줄의 코딩이 수십수백번의 디버깅을 했는

지아닌지는 그 누구도 알 수 없다. 아는자는 눈에 보이는 것만 보며 수십수백번의 디버깅을

Page 38: ActiveX COM Methods Analysis(함수분석법)pds9.egloos.com/pds/200802/09/20/ActiveX-Anal.pdf · 견하였으나 개인적으로 bof(버퍼오버플로우) 공격까지만 완성짓고

볼 줄 모르지만 진정으로 아는자는 눈에보이는 것뿐만 아니라 그 뒤에 숨은 수십수백번의

디버깅까지도 볼 수 있어야한다.

이 것이 바로 필자가 해커로써 원하는 진실이다. “진실은 저 넘어에..”

가리워진 곳을 볼 수 있는 힘을 스스로 기르는 것이 진정한 공부이고 연구이다.