This article was originally published on the Red Hat Customer Portal. The information may no longer be current.
In our previous blog, we saw how arbitrary code execution resulting from stack-buffer overflows can be partly mitigated by marking segments of memory as non-executable, a technology known as Execshield. However stack-buffer overflow exploits can still effectively overwrite the function return address, which leads to several interesting exploitation techniques like ret2libc, ret2gets, and ret2plt. With all of these methods, the function return address is overwritten and attacker controlled code is executed when the program control transfers to overwritten address on the stack.
In 1998 GCC introduced StackGuard, which was successfully used in conjunction with other security hardening technologies to rebuild the Red Hat Linux 7.3 distribution (GCC 2.96-113). The StackGuard patch was also applied to the source for GCC 3.2-7 used in the Red Hat Linux 8 distribution to rebuild both the compiler and GLIBC.
StackGuard basically works by inserting a small value known as a canary between the stack variables (buffers) and the function return address. When a stack-buffer overflows into the function return address, the canary is overwritten. During function return the canary value is checked and if the value has changed the program is terminated. Thus reducing code execution to a mere denial of service attack. The performance cost of inserting and checking the canary is very small for the benefit it brings, and can be reduced further if the compiler detects that no local buffer variables are used by the function so the canary can be safely omitted.
Canaries
There are currently three types of canaries which are supported by StackGuard:
Terminator canaries
Most buffer overflow attacks are based on certain string operations which end at string terminators. A terminator canary contains NULL(0x00), CR (0x0d), LF (0x0a), and EOF (0xff), four characters that should terminate most string operations, rendering the overflow attempt harmless. This prevents attacks using strcpy() and other methods that return upon copying a null character while the undesirable result is that the canary is known.
This type of protection can be bypassed by an attacker overwriting the canary with its known values and the return address with specially-crafted value resulting in a code execution. This can be when non-string functions are used to copy buffers and both the buffer contents and the length of the buffer are attacker controlled.
Random canaries
A random canary is chosen at random at the time the program execs. With this method, the attacker could not learn the canary value prior to the program start by searching the executable image. The random value is taken from /dev/urandom if available, and created by hashing the time of day if /dev/urandom is not supported. This randomness is sufficient to prevent most prediction attempts. If there is an information leak flaw in the application, which can be used to read the canary value, this kind of protection could be bypassed.
random XOR canaries
Random XOR canaries are random canaries that are XOR-scrambled using all or part of the control data (frame pointer + return address etc). In this way, once the canary or the control data is clobbered, the canary value is wrong and it will result in immediate program termination.
How does stackguard actually work
Compilers implement this feature by selecting appropriate functions, storing the stack canary during the function prologue, checking the value in the epilogue, and invoking a failure handler if it was changed. For example consider the following code:
void function1 (const char* str){
char buffer[16];
strcpy(buffer, str);
}
StackGuard automatically converts this code to:
extern uintptr_t __stack_chk_guard;
noreturn void __stack_chk_fail(void);void function1(const char* str){
uintptr_t canary = __stack_chk_guard;
char buffer[16];
strcpy(buffer, str);
if ( (canary = canary ^ __stack_chk_guard) != 0 )
__stack_chk_fail();}
Note how the secret value is stored in a global variable (initialized at program load time) and is copied into the stack frame and how it is safely erased from the stack as part of the check, preventing the value being leaked when this stack space is re-used. Since stacks grow downwards on many architectures, in this example the canary gets overwritten whenever input to strcpy is more than 16 characters.
The detection method works because it is impossible to get the correct value via trial and error. Since one incorrect canary value prevents further alterations, an attacker cannot keep trying until the correct value is found. In the example above, if the canary contained a zero byte, it would be impossible for a single strcpy to overwrite it correctly. This forces the attacker to either not attack, or be detected and be unable to alter the stack any further. This does not mean that the buffer cannot be exploited, but it makes exploitation much more difficult, often requiring multiple bugs to be used together. Also, __stack_chk_guard can be stored in various places; some architectures use TLS (Thread-local Storage) data for it.
One heuristic ordering often used, with the stack growing downwards, is first storing the canary, then buffers (that might overflow into each other), and finally all the small variables unaffected by overruns. This is based on the idea that it is generally less dangerous if arrays are modified, compared to variables that hold flags, pointers and function pointers, which much more seriously alter execution. Some compilers randomize the order of stack variables and randomize the stack frame layout, which further complicates determining the right input with the intended malicious effect.
Limitations of StackGuard:
Though StackGuard may be effective in preventing stack-buffer overflow attacks it has certain limitations as well:
-
An information disclosure flaw in a different part of the program could disclose the global __stack_chk_guard value. This would allow an attacker to write the correct canary value and overwrite the function return address.
-
Not all buffer overflows are on stack. StackGuard cannot prevent heap-based buffer overflows.
-
While StackGuard effectively prevents most stack buffer overflows, some out-of-bounds write bugs can allow the attacker to write to the stack frame after the canary, without overwriting the canary value itself.
-
If a function has multiple local data structures and pointers to functions, these are allocated on the stack as well, before the canary value. If there is a buffer overflow in any one of these structures, the attacker can use this to overwrite adjacent buffers/pointers which could result in arbitrary code execution. This really depends on the arrangement of data on the stack.
-
On some architectures, multi-threaded programs store the reference canary __stack_chk_guard in Thread Local Storage, which is located a few kb after the end of the thread's stack. In these circumstances, a sufficiently large overflow can overwrite both canary and __stack_chk_guard to the same value, causing the detection to incorrectly fail.
-
Lastly, for network applications which fork() child processes, there are techniques for brute forcing canary values. This only works in some limited cases though.
The GCC compiler provides various options to control StackGuard implementation during compilation. Despite of some limitations, StackGuard is quite effective in protecting binaries against runtime stack-buffer overflows.
저자 소개
Huzaifa Sidhpurwala is a principal Product Security Engineer, working for Red Hat Product Security Team.
채널별 검색
오토메이션
기술, 팀, 환경을 포괄하는 자동화 플랫폼에 대한 최신 정보
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
클라우드 서비스
관리형 클라우드 서비스 포트폴리오에 대해 더 보기
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
오리지널 쇼
엔터프라이즈 기술 분야의 제작자와 리더가 전하는 흥미로운 스토리
제품
- Red Hat Enterprise Linux
- Red Hat OpenShift Enterprise
- Red Hat Ansible Automation Platform
- 클라우드 서비스
- 모든 제품 보기
툴
체험, 구매 & 영업
커뮤니케이션
Red Hat 소개
Red Hat은 Linux, 클라우드, 컨테이너, 쿠버네티스 등을 포함한 글로벌 엔터프라이즈 오픈소스 솔루션 공급업체입니다. Red Hat은 코어 데이터센터에서 네트워크 엣지에 이르기까지 다양한 플랫폼과 환경에서 기업의 업무 편의성을 높여 주는 강화된 기능의 솔루션을 제공합니다.