들어가기 전에
작심삼일(作心三日)
-마음을 만들어도 3일밖에 가지 않는다-
이런 사자성어가 있다.
하지만 나는 작심삼일이 아닌 작심삼초라는 의미가 더 맞는 말 같다.
그렇다. 문제 풀이는 2025-08-06부터 쓰고 지금은 공부한 내용 정리하는 걸로 넘어가겠다.
라는 게으름 발린 소리를 지껄여 본다.
BOF (Buffer OverFlow) 란?
프로그래밍에서 발생하는 보안 취약점 중 하나이다.
우리가 프로그래밍을 할 때 지역 배열/변수를 사용한다면 그 정보는 위 사진의 buf라는 공간에 저장된다.
(Canary는 다음 글에서 다룰 예정이므로 이 글에선 없는 것으로 보자)
이렇게 정수형 변수 a와 배열 변수 b, 실수형 변수 c를 선언하자.
이러면 스택 프레임에는 a가 먼저 들어가고 다음은 b[10]만큼이 마지막으로 c가 들어가게 되는 후입선출 방식으로 저장된다.
buf 공간은 유동적이다.
그러나 공간이 유동적인 것이지 할당된 배열의 크기가 유동적인 것은 아니다.
즉, 선언된 배열의 크기보다 더 큰 입력값을 저장하게 된다면 저장 공간이 부족하여 입력값이 넘치게 된다.
b는 10만큼의 공간을 가지고 있다. 하지만 입력을 "KoreaDigitalMediaHighSchool" 라고 한다면
입력하는 길이는 10자를 초과하게 된다.
그러면 10자 이후의 입력값은 어디에 저장될까?
바로 b선언 이후에 선언된 c에 저장된다.
이를 통해 우리는 특정 변수에 원하는 값을 집어넣거나 Return Address의 값을 조작하여 원하는 함수를 실행시킬 수 있다.
실제 코드를 보자
학교에서 c언어를 배운다면 scanf라는 입력을 받는 함수에 대해 배웠을 것이다.
비슷한 함수로 gets 함수가 있다. gets는 매우 위험한 함수이다.
왜냐하면, 입력하는 길이의 제한을 걸지 않기 때문이다. (scanf도 scanf만 사용하면 길이 제한이 없다.)
위의 c언어 코드는 gets 함수로 입력받은 값을 buf에 저장하는 매우 위험한 코드이다.
또한, 정상적으로 프로그램을 사용한다면 실행되지 않을 win 함수가 있다.
(win 함수는 쉘을 실행시키는데 쉘은 게임에서 관리자 권한 같은 역할이다.)
우리가 서버에 악성 코드 파일을 만들어야 한다고 가정하자.
그러면 우리는 관리자 권한을 얻어 서버에 악성 코드 파일을 만드는 방법을 떠올릴 수 있다.
위의 코드를 보면 win 함수는 관리자 권한의 역할을 하는 쉘을 실행시킨다.
따라서 우리는 win 함수가 실행되도록 프로그램에 입력값을 넣으면 쉘을 얻고 악성 코드 파일을 넣을 수 있을 것이다.
이제 어떻게 입력해야 할지 구상해보자
buf는 스택에서 다음과 같이 저장되어 있을 것이다.
buf에 할당된 크기인 10 이상의 입력값을 넣어버리면 SFP를 덮고 RET을 조작할 수 있을 것이다.
SFP는 함수 호출 시 이전 스택 프레임의 기준점을 저장하는 역할을 하며
스택 프레임 구조에 따라 덮어야 하는 바이트 수가 달라진다.
RET(Return Address)은 함수가 끝났을 때 어디로 되돌아갈지 결정하는 역할을 한다.
저 코드에서는 프로그램을 끝내는 작업을 한다.
우리는 win 함수를 실행하고 싶기 때문에 RET의 주소를 win 함수의 주소로 덮을 것이다.
그러기 위해선 buf와 SFP를 아무 값으로 덮어서 RET 직전까지 가면 된다.
즉, buf의 크기 + SFP 크기 + win 함수의 주소를 넣으면 우리가 원하는 쉘을 얻고
이를 통해 악성 코드를 심을 수 있다.
너무 이론적이다.
실제 게임의 일부 기능을 재현한 코드를 보고 익혀보자.
조금 전과 같이 gets로 입력을 받기 때문에 BOF 취약점이 발생한다.
그러나 아까와는 달리 administrator이라고 하는 관리자 함수를 호출하는 코드가 존재한다.
하지만 admin 변수의 값이 1일 때만 실행되며 admin 변수의 값은 0이기 때문에 일반적으로 관리자 함수에 접근할 수 없다.
우리의 목적은 관리자 함수 호출이다. 즉, admin 변수를 1로 바꾸는 것이다.
다음과 같이 선언된 변수가 스택 프레임에 저장될 것이다.
여기서 패딩(padding)은 로컬 변수는 스택 상에서 8바이트 단위 정렬(alignment)을 맞춘다 라는 규칙에 의해
4바이트인 admin 변수를 8바이트로 맞추기 위해 삽입된 것이다.
chatting에 할당된 크기 16을 덮고 패딩의 크기 12를 덮으면 admin 값을 임의로 조작할 수 있게 된다.
따라서
위처럼 익스플로잇 코드를 작성할 수 있다.
A*16은 chatting에 할당된 크기 16을 덮기 위해, B*12는 패딩의 크기 12를 덮기 위해 있으며
마지막 p32(1)은 admin변수의 위치로 admin 변수의 값을 1로 바꾸는 역할을 한다.
우리가 그냥 입력을 하게 되면
위와 같이 입력한 메시지를 그대로 출력하고 프로그램이 종료되지만
방금 전에 만들었던 익스플로잇 코드를 실행시키면 위와 같이 administrator 함수를 실행할 수 있다.
Yeppppiiiiiii
마치며
Pwnable의 기초이면서 매우 강력한 BOF 취약점에 대해 알아보았다.
다음 글에서는 위의 공격을 방어하는 Canary 보호기법을 설명하겠다.