Stack Cookie (GS aka. canary) 2

2018. 1. 23. 19:180x04 pwnable/윈도우즈 어플리케이션 취약점 분석

728x90

이전 포스팅에서는 유동적으로 입력한 데이터가 기존의 버퍼의 크기를 초과 했을 때 스택이 오염 된다는 것에 언급하였다. 이번에는 조금 다른 얘기를 해볼까 한다.


정상적인 코드에 쓸데없이 라인이 추가되어 LOC(Line Of Code)가 증가하게 되면 퍼포먼스가 감소되는 것 같다. 그 이유는 여기에서 추측해볼 수 있었다. 순전히 나의 생각을 서술한 것 이기 때문에 틀릴 수도 있다. 


컴파일러는 퍼포먼스 감소 영향을 최소화하기 위해 _alloca를 사용해 스택에 메모리를 할당하거나, 함수가 스트링 버퍼를 가지고 있을 때만 스택 쿠키를 추가하는 로직을 사용하게 되었고, 추가적으로 보호 매커니즘은 버퍼가 5바이트 이상의 데이터를 가질 때만 활성화 된다. 


다음은 상기에 서술한 문장 PoC다.



아래는 버퍼가 5바이트 이상일 때와 3바이트 일 때를 비교한 사진이다.



GS는 함수나 스택으로 할당 된 메모리에만 붙게 되는데 코드 실행의 효율성과 오버헤드를 줄이기 위해 붙는 메모리에도 일정크기 이하엔 붙지 않는 특성이 있다. 그 일정크기가 5바이트 이내 인 것이다.


우리가 이미 경험했던 버퍼오버플로우는 스택에 공격자가 작성한 데이터가 초기에 정해둔 버퍼의 크기를 초과하면서 eip가 덮이는 행위 그 자체였다. 하지만 GS 옵션이 활성화되게 되면 한 가지를 신경써서 덮어야한다. 그것이 바로 쿠키인 것이다. 한번 더 복습하면 쿠키는 저장 된 EBP와 저장 된 EIP 앞에 위치하게 된다. 



GS 옵션이 지니고 있는 무기는 쿠키 뿐만이 아니다. 또 하나의 무기는 "변수 재배치 기법"이다. 함수에 의해 사용되는 지역변수와 인자들을 덮어쓰는 것을 방지하기 위해 컴파일러는 스택 프레임의 구성을 재배치하고 다른 변수들보다 높은 주소에 스트링 버퍼를 추가한다.그래서 버퍼 오버플로우가 발생하더라도, 지역변수가 아닌 스트링 버퍼에 값이 쌓이게 된다.


이번에는 스택 쿠키 즉 /GS를 우회하는 방법에 대해 알아보고자 한다.

스택 기반 오버플로우 보호 매커니즘을 우회할 수 있는 가장 쉬운 방법은 쿠키 값을 추측하거나 계산하는 것이다. 이를 통해 공격자의 버퍼에 있는 같은 값으로 쿠키를 덮을 수 있다. 

그런데 의문이 조금 든다. EBP값이 계속 바뀌는 ASLR 환경이라면 쿠키 값을 추측할 수 없지 않는가... 그냥 이런거 다 필요없이 Stack cookie region 만 제외하고 overwrite 하는 방법은 안 될 까?  고민하지 말고 테스트를 진행해보자. 


윈도우즈 10 32비트 계산기 셸코드 이다.



이제 취약한 코드에 익스플로잇을 해보자.

//Goal : bypass about GS

//Author : c0nstant

//IDE : VS2017 X86 

//Compile Option : /GS , DEP, SafeSEH, NonASLR


#include <stdio.h>

#include <windows.h>

#define MAX_BUF (1000)

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

{

char buffer[100]; // 100bytes

FILE *f;


if (argc != 2)

{

perror("Usage : [filename] ");

}


f = fopen(argv[1], "r");

fgets(buffer, MAX_BUF, f);


fclose(f);

return 0;

} 


python으로 bypass.txt를 만들어서 사용하였다. 

python 페이로드는 정확한 페이로드가 나오기 전까지 적지 않을 것이다.



해당 패턴에 의해 seh next가 덮여진다.


SEH는 seh_next와 seh_handler로 구성되어 있다. 

그림으로 이해해보자. 


지금 그림을 보면 위에가 seh_next, 아래가 seh_handler 이다. 각각은 4바이트를 지니고 있으며 이들은 구조체 임을 알 수가 있다. seh_next는 핸들러가 가리키는 주소인데 이를 적절히 사용할 수 있다. 바로 SHORT JMP 라는 OPCODE EB 10이다. 

SEH_HANDLER는 다음에 다룰 EIP를 정할 수 있다. 이때 대부분 POP POP RET라는 가젯을 이용하게 된다. POP POP을 하게 되면 스택을 2번 복구하고 난 다음의 위치에 돌아갈 주소를 담을 수 있다. 


직접 실습을 해보자. 우선 우리는 패턴의 오프셋을 통해 seh_next offset을 알게 되었다.

offset 164인데 여기서 seh_next를 (4byte) 제외하면 (164-4)이다.


자고일어나서 하자.


실습을 이어가면서 설명하려고 한다. 


급하게 해야 할 일이 생겨서 그 할 일을 다 하고 포스팅을 이어가려고 한다.


이번실습은 본의 아니게 많은 것을 생각하게 만들어 주었다. 

더 열심히 보안공부 해야겠다.