imnyang's workspace

뒤로

포너블이란?#

포너블은 시스템 해킹 분야의 문제 유형 중 하나예요. Pwn은 원래 Own(소유하다)에서 파생된 단어로, 시스템을 자신의 것으로 만든다는 의미를 가지고 있어요.
따라서 Pwnable은 “시스템을 자신의 의도대로 제어할 수 있게 만드는 것”을 의미해요.

포너블 문제에서는 프로그램 내부의 취약점을 찾아 공격자가 원하는 동작을 수행하도록 만드는 것이 목표예요.
일반적으로는 쉘을 획득하여 시스템 명령을 실행하는 단계까지 도달하는 것을 목표로 해요.


Shell이란?#

쉘은 사용자와 커널 사이에서 명령을 전달하는 도구로 보면 좋아요.
사용자가 입력한 명령어를 운영체제가 이해할 수 있도록 변환하여 전달해주는 역할을 수행해요.

대표적인 리눅스 쉘은 다음과 같아요.

  • /bin/sh
  • /bin/bash

포너블에서는 취약점을 이용하여 이러한 쉘 프로그램을 실행하는 것을 목표로 해요.
예를 들어 다음과 같은 함수가 실행되면 공격자는 쉘을 획득할 수 있어요.

system("/bin/sh");
c
execve("/bin/sh", 0, 0);
c

즉, 포너블의 핵심 목표 중 하나는 프로그램의 취약점을 이용해 /bin/sh와 같은 쉘을 실행하는 것이에요.


Buffer Overflow#

Buffer Overflow는 프로그램이 버퍼에 할당된 크기보다 더 많은 데이터를 입력받아 인접한 메모리 영역까지 덮어쓰는 취약점이에요.

예를 들어 8 byte 크기의 버퍼에 10 byte를 입력하면 초과된 2 byte가 주변 메모리를 덮어쓰게 돼요. 이 과정에서 중요한 데이터가 변조될 수 있어요.

특히 Stack 영역에서 발생하는 버퍼 오버플로우를 Stack Buffer Overflow라고 불러요.

Screenshot_20260621_223359.png


Return Address Overwrite#

함수가 호출되면 스택에는 함수가 끝난 뒤 돌아갈 위치를 저장하는 Return Address가 저장돼요.

버퍼 오버플로우를 통해 반환 주소까지 덮어쓸 수 있다면 함수 종료 후 원래 코드가 아니라 공격자가 원하는 위치로 실행 흐름을 변경할 수 있어요.

이러한 기법을 Return Address Overwrite라고 해요.


Stack Canary#

Stack Canary는 버퍼 오버플로우 공격을 탐지하기 위해 스택에 삽입되는 보안 기법이에요.

스택의 구조는 대략 다음과 같아요.

Screenshot_20260621_223437.png

버퍼와 반환 주소 사이에 Canary 값을 배치하여 공격자가 반환 주소를 덮어쓰려면 반드시 Canary도 함께 덮어쓰게 만들어요.

Canary 값은 어디서 오는가?#

리눅스의 Stack Canary 값은 TLS(Thread Local Storage)에 저장된 값을 참조해 사용해요. 일반적으로 64비트 환경에서는 TLS + 0x28 위치의 값을 사용해요.

ex)

0x8f3a91c27b46d000
plaintext

64비트 환경에서는 8 byte 크기의 랜덤한 값이 사용되고, 32비트 환경에서는 4 byte 크기의 값이 사용돼요.

어떻게 공격을 막나요?#

함수가 종료될 때 프로그램은 현재 스택에 저장된 Canary 값과 원본 Canary 값을 비교해요.

예를 들어 공격자가 버퍼 오버플로우를 통해 Canary를 다음과 같이 덮어썼다고 가정해 봅시다.

0x4141414141414141
plaintext

프로그램은 원래 Canary 값과 현재 Canary 값을 비교하게 되고, 두 값이 다르므로 공격이 발생했다고 판단해요.

그 결과 프로그램은 다음과 같은 오류를 출력하며 종료돼요.

*** stack smashing detected ***: terminated
text

즉, Canary는 반환 주소가 변조되기 전에 프로그램을 강제로 종료하여 공격을 차단하는 역할을 해요.

Canary의 첫 바이트가 0인 이유#

Stack Canary의 마지막 바이트는 항상 NULL(0x00)로 설정돼요.

ex)

0x8f3a91c27b46d000
plaintext

이는 문자열 출력 함수가 NULL 문자를 만나면 출력을 종료한다는 특성을 이용한 보호 기법이에요.

만약 널바이트가 없다면 버퍼 뒤에 위치한 Canary 값이 문자열 출력 과정에서 그대로 노출될 수 있어요. 하지만 널바이트가 존재하면 문자열이 중간에 끊기므로 Canary 유출 가능성을 크게 줄일 수 있어요.

Stack Canary 우회 방법#

Stack Canary는 버퍼 오버플로우 공격을 방어하기 위한 강력한 기법이지만, Canary 값을 알아낼 수 있다면 우회할 수 있어요.

1. Canary Leak#

가장 대표적인 방법은 메모리 유출 취약점을 이용하는 것이에요.

프로그램에 메모리 내용을 출력하는 취약점이 존재한다면 스택에 저장된 Canary 값을 읽어낼 수 있어요.

예를 들어 Canary 값이 다음과 같다고 가정해 볼게요.

0x8f3a91c27b46d000
text

공격자는 먼저 정보 유출 취약점을 통해 이 값을 획득해요.

그 후 BOF 공격 시 다음과 같이 입력해요.

[버퍼]
[원래 Canary]
[SFP]
[조작된 RET]
text

Canary 값을 정확하게 유지한 상태로 반환 주소만 덮어쓰므로 프로그램은 Canary가 변조되지 않았다고 판단해요.

결과적으로 Stack Canary 검사를 통과하면서 원하는 위치로 실행 흐름을 변경할 수 있어요.

2. Partial Overwrite#

특정 상황에서는 반환 주소의 일부 바이트만 덮어쓰는 기법을 사용할 수 있어요.

이 경우 Canary 영역까지 도달하지 않고도 실행 흐름을 변경할 수 있기 때문에 Canary 검사가 의미가 없어져요.

다만 PIE, ASLR 등의 보호 기법이 함께 적용된 경우에는 성공하기 어려워요.

3. Non-Control Data Attack#

모든 BOF 공격이 반환 주소를 덮어쓰는 것은 아니에요.
예를 들어 다음과 같은 코드가 있다고 가정해 볼게요.

char buf[32];
int is_admin = 0;
c

버퍼 오버플로우를 이용해 is_admin 값을 1로 바꿀 수 있다면 관리자 권한을 획득할 수 있어요.
이 경우 반환 주소를 건드리지 않으므로 Stack Canary는 아무 역할도 하지 못해요.

-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAg4c/dn4BitGH1/xNjKoKEp97I2b
eU57QXvkDBEdNNrEMAAAATYmxvZy5pbW55YS5uZy9wb3N0cwAAAAAAAAAGc2hhNTEyAAAA
UwAAAAtzc2gtZWQyNTUxOQAAAEA2uUaxXrS4fwqxapXhX73IwcYMPlLhRiLWONK/ukczig
xcLqA5lYamvQn5cab6DJ2KBrBgjXpqVHEuY0Q4q6MK
-----END SSH SIGNATURE-----
[Layer7] 2026년 6월 17일 시스템 해킹 1차시 과제
http://blog.imnya.ng/layer7/13
저자 imnyang
게시일 2026년 06월 20일