SYSENTER/INT 2E

SYSENTER / INT 2E

윈도우에서 사용하는 winapi들은 대부분 커널레벨에서 구현이 되어있다. 그렇기 때문에 그냥 디렉터리에 들어 갈때도 웹서핑을 할때도 인지하지는 못하지만 컴퓨터는 수많은 시스템콜을 호출하게된다.
 어플리케이션 레벨(Ring 3)에서 커널레벨(Ring0)로 진입하기 위한 방법은 소프트웨어 인터럽트, 콜게이트(INT 2E)를 사용하거나 Fast System Call(SYSENTER)를 사용하는 2가지의 방법으로 나뉜다. 먼저 Fast System Call 이 제공되기 전(인텔 펜티엄 2 이전)에 사용되었던 INT 2E를 알아보자.







INT 2E는 기존의 인터럽트 방식을 사용하여 SSDT에 접근한다. 위의 그림에서도 볼수 있듯이 여러 작업을 거쳐 함수에 접근하므로 SYSENTER에 비해 느리다. 반면에 SYSENTER는 팬티엄2부터 제공되는 MSR(0x176)에 KiFastCallEntry의 주소가 저장되어 있으므로 INT 2E의 방식보다 빠르게 함수에 접근 할 수 있다.

Read more

code injection

code injection은 다른 프로세스에 내 코드를 심어 다른 프로세스가 내 코드를 수행하게 한다.

코드 인젝션의 코드를 간략히 설명해보면
1. 인젝션 할 코드(기계어)도 내 프로세스 메모리에 올라간다.
2. 인젝션 대상 프로세스를 suspend로 생성하고
3. 내 프로세스 메모리를 모두 읽어서 인젝션 대상 프로세스에 쓴다.
4. context 구조체로 eip를 injectedCode() 함수시작로 맞춰주고
5. 인젝션 당한 프로세스를 실행 시킨다.

*injectedCode() 는 내 프로세스에 맞게 컴파일된 기계어이므로 함수의 주소를 못찾아갈수도 있기 때문에 dll을 동적으로 로드해 주어야한다.




이 코드를 실행하면 svchost.exe라는 프로세스가 생성되어 injectedCode() 함수 안의 코드를 실행 한다.(위의 코드는 선언등이 빠져 실행되지 않는다.)
Read more

stack, prologue, leave, ret

임시 자원들을 관리하기 위하여 프로세스는 stack(쓰레드마다 stack이 생성된다.)이라는 메모리 영역을 할당받아 관리한다.
stack 자료구조는 후입선출법(LIFO)으로 나중에 들어온 데이터를 먼저 뽑아쓰는 구조로
stack 메모리는 stack자료구조를 push, pop과 esp, ebp로 구현한다.

esp는 스택의 최상단을 포인팅, ebp는 스택의 베이스를 포인팅하는 레지스터이고
push data는 stack 상단에 data를 넣고 esp를 증가시켜주고 pop reg로 esp가 가리키고 있는 data를 reg에 넣고 esp를 내려주는 명령어이다.

스택메모리에서 함수가 호출 될 때, 함수에서 빠져나올 때 마다 ebp, esp가 셋팅되는 것을 알아보자.

함수가 호출되고 지역변수를 잡은 후의 스택 메모리의 모습이다.(아래가 높은주소)




새로운 함수 호출

새로운 함수의 호출은 call func() 로 실행 될 수 있다. call func()는 push eip(RET backup) + jmp func() 와 같다. 
func() 함수가 끝난 후 돌아올 eip주소를 백업하고(RET),  func()의 기계어 코드가 있는 곳으로 eip를 이동시켜 func() 를 실행한다.

call func() 가 실행된 직후의 스택의 모습은 위의 그림에서 RET 값이 셋팅되고 esp 가 RET를 가리키고 있는 모습이다. ebp는 func()을 호출한 콜러의 ebp를 가리키고 있다.

이제 eip는 func()함수의 프롤로그를 수행 할 차례이다. 함수는 시작부분에 push ebp/mov ebp, esp라는 프롤로그를 갖고 있다.
프롤로그가 실행되면 push ebp로 (SFP)콜러의 ebp가 백업되고 ebp, esp가 SFP를 가리키게 된다. 이렇게 func()의 스택이 초기화 되어 func()는 스택을 독립적으로 사용할 수 있다.

변수의 전달

argv는 call을 하기 전에 스택에 argv를 push하여 func()로 전달하고 ebp+'x'로 값을 얻어쓴다.
local value는 func()의 프롤로그가 실행 된 후 sub esp, 'x'로 esp를 local value만큼 위로 올리고 esp + 'x'로 값을 얻어서 쓴다.

함수에서 콜러로 복귀

func() 함수가 모두 실행 된 후에는 func()를 호출한 콜러로 복귀를 해야한다. 스택 정리를 위해서 leave, ret을 실행 한다.
leave 는 mov esp, ebp/pop ebp로 esp를 ebp위치와 같게 내려 놓고 SFP(콜러의 ebp backup)를 ebp에 넣어 ebp를 이전 함수의 위치로 복귀 시킨다. pop명령어를 실행 하였기 
때문에 esp는 RET를 가리키고 있다. 

그 다음에는 ret(pop eip + jmp eip)가 실행된다. pop eip로 eip에는 RET(call func()의 바로 밑의 주소)이 들어가고 jmp eip로 eip는 func()가 호출된 다음의 코드를 가리키게 된다.
이렇게 되면 func()가 호출되기 이전의 스택모양과 똑같아진다!

Read more

mutex를 이용한 중복실행 방지


운영체제는 컴퓨터 자원을 안전하게 관리하여야 한다.
critical resource 에 대해 경쟁 관계가 있는 프로세스들이 존재하는 경우에 Mutual exclusion, deadlock, starvation의 제어 문제가 발생 할 수 있다.

deadlock은 세마포어를 사용하여 해결 할 수 있다. 세마포어는 카운트 값을 주고 critical section에 접근 할 수 있는 프로세스의 수의 제한을 두어 프로세스가 자원을 점유할 때 카운트를 -1 해주고 자원을 반납할 때 +1 해주는 식으로 deadlock을 해결한다.
Mutex는 2진 세마포어와 비슷하다.(카운트 = 1, 쓰레드) 자원에 오로지 하나의 쓰레드만 접근 할 수 있게 한다.

Mutex를 사용하여 중복 실행 방지코드를 작성해보자.



CreateMutex 함수로 MutexInstance를 생성(3번째 인자가 고유네임)한다.
CreateMutex 함수를 호출 하였는데 고유네임으로 이미 뮤텍스가 생성 되 있다면 CreateMutex함수는 ERROR_ALREADY_EXISTS 에러를 리턴하게된다.
프로세스 메인 쓰레드에 MutexInstance를 생성해 놓으면 CreateMutex 함수의 에러코드를 확인해 중복 실행을 방지 할 수 있다.
Read more

CVE-2013-3897 취약점 분석 보고서

 이 자료는 우리가 KITRI (한국정보기술연구원) 침해대응 과정 중 수료 프로젝트로 진행했던 자료이다. 

 취약점을 분석해논 우리나라 자료가 거의 존재하지 않았기 때문에 어떻게 분석해야 하는지 매우 막막했다. 특히 3897 취약점은 인터넷 익스플로러(IE Explorer)의 취약점이었기 때문에 디버깅 하기가 매우 까다로웠다. 처음 분석을 시도하는 우리가 POC 를 처음부터 작성한다는 것은 무리로 보였으므로, 먼저 POC가 존재하는지 파악했고 다행히 POC가 ExploitDB에 존재했기 때문에 해당 취약점에 대한 이해도를 높일 수 있었다. 존재하는 POC를 우리의 환경과 필요에 맞게 재작성하여 사용했다.

 처음 분석한 IE Explorer 취약점 분석... 처음 분석을 시도하려는 사람들에게 도움이 되고자 PDF 파일을 링크로 첨부한다.


CVE-2013-3897_분석 보고서.pdf
Read more

SEH overwriting

SEH overwriting (stackoverflow)

victim : windows XP SP3 32bit, globalSCAPE secure FTP server (192.168.50.152)

attacker : backtrack5 R2 32bit






FTP 커맨드 입력 받을  입력받는 문자열에 대한 길이 체크를 안하기 때문에 stack overflow 취약점이 발생한다위의 그림은 입력 받는 함수의 이전 함수가 호출 됬을 때의 스택 구조이다 사용자가 cmd 입력하면 cookie 위쪽의 RET 밑에 매개변수 전달 형식으로 사용자가 입력한 CMD 쌓이게 된다.

여기서 FTP  입력받는 문자열의 길이 체크를 안하기 때문에 사용자가 cmd 길게 입력한다면 밑에 있는 cookie SFP, RET 모두 수정   있다.
스택 오버플로 공격  트램플린 공격의 경우 RET 현재 레지스터에서 권한이있는 영역의 메모리 주소를 갖고 있는 레지스터를 CALL 해주는 코드의 시작주소를 넣고 레지스터가 갖고있는 시작주소부터 shellcode 삽입하면 공격을 실행   있지만
 security cookie 스택이 보호되고 있는 위의 경우에는 exception 발생하여 트램플린 공격이 무력화 된다.

 경우에는 SEH overwrite 기법을 사용하여 공격을 실행   있다프로그램은 exception 발생하면 fs레지스터의 (오프셋) 참조하여 GDT(kernel) TEB(Thread Environment Block) 포함된 TIB(Thread Information Block) 체인형식으로 구성되있는 구조체인 _EXCEPTION_REGISTRATION_RECORD(_next, _handler) 첫번째부터 순서대로 참조하여 handler() 호출한다. handler() 무조건 순서대로 전부 호출되며 exception 처리할 능력이 있다면 처리를 하고 처리할 능력이 없다면 다음 _EXCEPTION_REGISTRATION_RECORD 넘어가서 handler() 호출한다마지막 _EXCEPTION_REGISTRATION_RECORD _next에는 0xffffffff 마지막을 확인   있다. SEH overwrite공격은 여기서 _next _handler 조작 함으로써 실행 되는 공격이다.



cmd socket A 2043 넣고 RET변경을 위한 4 넣고 B 60 넣고 오버플로 공격을 실행한 FTP 스택메모리이다. (RET변경은 의미 없다)

*A  2043개가 들어가야 오버플로가 생긴다는 것은 dummy 일정한 pattern으로 보내 exception 발생했을 때의 eip 보고 확인   있다.






_next(Pointer to next SEH record) _handler(SE handler) 덮여 쓰여진 것을   있다.
exception 발생하면 _handler 호출된다 _handler pop, pop, ret  있는 로딩된 dll 메모리주소를 넣는다(프로그램에 로딩되는 dll파일을 윈헥스등으로 열어 검색한다.) pop, pop, ret코드를 넣는 이유는 handler 호출될  전달되는 매개 변수와 관련이 있다. pop, pop, ret 하게되면 3번째 인자인 현재 _EXCEPTION_RECORD 주소가 ret 의해 eip 들어가게 된다그러면 _EXCEPTION_RECORD(_next)에는 jmp 6이라는 기계어 코드를 덮어 써서 _handler밑에서부터 실행되도록 한다. jmp 6 \x90\x90(NOP) \xAA\xBB\xCC\xDD(pop, pop, ret 있는 주소이렇게 6바이트를 점프해야 하기 때문에 6이다.


  그림의 SE handler 바로 밑부터 실행 됨으로 여기에 shellcode  넣으면 공격 성공을   있다.






위의 그림은 _next  _handler 덮어 씌운 모습이다. 0x1650110B pop,pop,ret코드의 시작주소이고 0x06EB  jmp 6, 0x9090 nop이다.
exception 발생하면 0x1650110B 가서 pop, pop, ret 실행하고 0x0147FA9C 와서 jmp 6 실행하여 SE handler 밑으로 eip 내려가게 된다
이제 SE handler 밑에 shellcode 덮으면 공격이 된다.



python으로 작성된 4321 port  접속 연결하여 cmd 넘겨주는 exploit코드를 실행   netcat으로 4321 port listen하고 있으면



밑의 그림처럼 victim cmd 넘어 오게된다.



Read more

UPX packing

UPX packing

개발자는 파일의 크기를 줄이거나 분석하기 어렵게 하기 위해서 packer를 사용하여 응용프로그램을 packing한다. packer에는 여러 종류가 있는데 가장 기본적인 UPX 패커의 동작원리를 보면서 packer에 대해 이해해보자





패킹작업을 수행하면 위의 그림대로 code와 파일 복구를 위한 IAT등을 UPX1영역에 복사하고 복구를 위한 recovery code가 그 위에 작성되고 ep가 recovery code의 시작에 위치하게 된다. 패킹된 UPXcalc.exe 를 실행하면 recovery code가 실행된 후 원래 code가 실행된다.







UPXcalc.exe를 실행시 동작 과정을 보자. 실행시 ep는 recovery code의 시작(1번)에 위치한다. recovery code는 먼저 UPX0섹션을 모두 초기화 한다. 그리고 복사 해둔 원본 code를 복사한다. 

이제부터는 IAT를 채워 넣을 차례다. 
IAT는 하드에 파일로 존재할때랑 메모리에 로딩됬을 때 다른곳을 포인팅하고 있다. 하드에 있을때는 dll에서 사용하는 함수의 이름을 포인팅하고 있지만 메모리에 로딩되면 IAT는 dll안의 함수 시작주소를 직접 가리키게 된다.

UPXcalc.exe가 로딩될때는 UPX 의 import table을 보고 dll들을 로딩하였기 때문에 원본 코드가 사용하는 dll은 로딩이 안되있을수도 있다. 그렇기 때문에 packer(recovery code)는 직접 동적 로딩을 하여야한다. 백업해둔 name을 갖고 LoadLibrary()와 GetProcAddress()함수를 사용하여 원본 code가 사용하는 dll들을 동적 로딩하고 사용하는 함수의 시작주소를 IAT에 넣어 원본의 IAT를 복구한다.

※원본code는 이미 함수들의 정보가 포함된 컴파일된 기계어이므로 dll만 로딩해주면 원본code의 함수들이 정상 동작한다. (걍 코드로 dll을 동적로딩해서 함수의 주소값으로 참조하여 사용하려면 calling convection, parameters의 형과 개수, return value의 형이 필요하다)

이렇게 IAT까지 복구를 다 해주면 jmp문으로 원본code가 있는 곳으로 eip를 뛰게한 후 원본code를 실행하게 된다. 계산기가 실행된다!




Read more

process 생성시 debugger로 물기

디버깅을 하다보면 프로세스가 createprocess나 resumethread로 새로운 프로세스를 실행 시키는 경우가 있다.
새롭게 로딩된 프로세스를 디버깅하기 위해서는 레지스트리 키값을 생성 해주면 된다.




위 그림과 같이 

HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Image File Execution Options/ 

밑에 새롭게 로딩되는 프로세스 이름을 키값으로 생성하고 프로세스 이름키 안에 문자열값인 Debugger 값을 추가하고 데이터에 디버거의 절대 경로를 추가하면 된다.

calc.exe를 실행 하면 ImmunityDebugger에 물리게된다.
Read more

walking process & DLL loading

현재 메모리에 올라가 있는 process 목록을 구하는 api

 CreateToolHelp32Snapshot()
 Process32First()
 Process32Next()

walking process API를 이용해 explorer.exe 프로세스를 찾아서 explorer.exe의 자식 프로세스들을 모두 종료 시키는 코드


악성코드의 DLL injection api

 GetProcAddress()
 LoadLibrary()
 CreateRemoteThread()





Read more