
canary와 NX가 적용되어있는 x86 아키텍처이다.
소스코드를 살펴보자

뭔가 조금 길지만 while문으로 반복하며 'F'를 입력받으면 box에 값을 넣고 'P'를 입력하면 index를 입력받고 해당 index에 해당하는 box의 값을 출력한다. 그리고 'E'를 입력하면 name_len을 입력받고 해당 크기만큼 name을 입력 받는다.
사실 결국 해야 하는것은 카나리를 구하고, 이를 통해 카나리를 우회하여 get_shell함수의 주소로 return_address를 덮는 것이다.
해당 코드를 살펴보니 배열 box의 index에 해당하는 값을 16진수로 출력하는 부분이 있는데 이 부분에서 취약점이 발생한다. box의 크기가 0x40바이트 즉, 64바이트인데 box의 index0을 기준으로 65번째 값부터가 카나리라고 가정하면 카나리의 값을 추출할 수 있는것이다.
이를 위해서는 스택프레임에 변수들이 어떻게 위치하고 있는지 알아야 하므로 디버거로 확인을 해보자

해당 부분은 main함수의 시작부터 변수를 선언하는 부분이다.
우선 sub sep, 0x94를 통해 main함수에서는 0x94만큼의 스택 공간을 할당하는 것을 볼 수 있다.
이후 2줄은 뭔지 모르겠지만 main함수의 스택 안에 영향을 미치는 것은 아니니 넘어가고..
x86 즉 32비트 운영체제이기 때문에 fs에서 0x28만큼 떨어진 곳에서 카나리 값을 가져오는 것이 아니라 gs부터 0x14만큼 떨어진 곳에서 카나리를 가져오는 것을 알 수 있다. 그리고 64비트에서는 카나리의 값이 8바이트였지만 32비트에서는 카나리의 값이 4바이트이다.
아무튼 gs:0x14에서 가져온 카나리의 값을 ebp에서 0x8만큼 떨어진 곳에 설정을 한다.
ebp에서 8바이트 떨어진 곳에 4바이트 카나리를 저장하면 4바이트만큼의 값이 남는데 이 남는 값이 뭔지는 모르겠다...
아무튼 이후 카나리 값이 들어있는 eax를 xor하여 0으로 만들고

이 부분이 나오는데 해석하면 다음과 같다.
ebp-0x88의 주소를 edx로 복사
eax에 0 대입
ecx에 0x10 대입
edx에 있는 주소값을 edi로 복사
ecx번 만큼 DWORD PTR es:[edi],eax 수행
DWORD PTR es:[edi],eax 가 뭐냐면 edi가 가르키는 주소에 DWORD 크기만큼 즉, 4바이트만큼 eax의 값을 대입하는 것이다. 그리고 edi의 값은 자연스럽게 4바이트씩 증가한다.
그럼 결국 0x10 * 0x4 = 0x40 만큼을 0으로 초기화 하는 것인데 아까 본 배열들의 크기가 마침 0x40이다. 따라서 이 부분은 아까 c 소스에 나온 배열중에 하나를 선언하여 초기화 하는 부분으로 유추를 할 수 있다.

그럼 자연스럽게 이 부분도 남은 배열을 선언하고 초기화하는 부분이고

이 부분이 int형 변수 2개와 크기가 2인 char형 배열을 선언하는 부분으로 볼 수 있다.
지금까지 유추한 내용과 절차지향언어인 c언어의 특성을 고려해봤을때 스택 프레임을 그려본다면 다음과 같을 것이다.

int형 변수인 idx, name_len의 순서와 0x40만큼의 크기를 가지 char형 배열 box와 name의 순서를 저렇게 생각한 이유는 먼저 idx와 box가 c언어에서 먼저 선언되었기 때문에 어셈블리어에서도 할당이 되었을 것이라고 생각하기 때문이다.
중간중간에 ???표시가 된 부분은 그냥 남는 부분이라고 생각한다... 언젠가는 뭔지 알 수 있지 않을까
아무튼 이번 문제의 핵심은 어셈블리어를 통해 스택프레임에 할당된 변수들의 위치를 추정하는 것이라고 생각하기에 문제는 거의 다 풀었다고 볼 수 있다.
아까 "'P'를 입력하면 index를 입력받고 해당 index에 해당하는 box의 값을 출력한다." 라고 말한 부분에서 c언어를 아는 사람들은 알 것이다. index를 통해 배열의 값에 접근하는 것은 배열의 시작 주소에서 index*배열크기 만큼 떨어진 곳의 값을 가져오는 것이라는것을 말이다. 예를 들면 box배열의 크기는 64바이트이지만 index를 100으로 입력하면 name 배열의 값을 가져온다.
따라서 우리는 box[128], box[129], box[130], box[131]을 통해 4바이트의 카나리 값을 가져올것이고, 이렇게 얻은 카나리로 payload를 작성하여 name을 입력받고 종료할 때 전송할 것이다.
payload는 다음과 같다.

get_shell함수의 주소는 PIE가 적용되지 않았기에

함수 주소를 알아내고 그냥 넣었지만 사실 symbols 함수를 이용하여 구해도 될 것 같다.

실행하면 get_shell함수가 잘 실행되는 모습을 볼 수 있다.
'보안 > DreamHack' 카테고리의 다른 글
| Dreamhack Return Oriented Programming (0) | 2024.10.10 |
|---|---|
| Dreamhack Return to Library (1) | 2024.10.07 |
| Dreamhack Return to Shellcode (0) | 2024.10.03 |
| Dreamhack basic_exploitation_001 (0) | 2024.10.01 |
| Dreamhack basic_exploitation_000 (0) | 2024.09.30 |