I still remember the first time read AlephOne’s ‘Smashing the Stack for Fun and Profit’ – despite not having the proper knowledge to understand it at the time, it put the security bug in my head. It was truly a consciousness raising experience to get that first glimpse of my computer’s inner workings. One thing I did understand from it, loud and clear, is that bounds checking is a must when manipulating strings. Apparently many programmers have not gotten the memo yet however as a search on NVD for ‘stack overflow’ limited to just the past 3 months of published CVEs returns 30 matches!
Whether you’re a developer, a tester, or a researcher, it is crucial to understand the principles of this category of attack. Much defensive advancement has been made in the form of OS and compiler mitigations but the best defense is to understand how to identify and eliminate the underlying programming errors. The focus of this blog then is to explore an example program which is ripe for stack based buffer overflow exploitation. The source code listing below describes a program which allows a user to initiate an Nmap TCP SYN scan of a specified target.
When compiled, this program will simply accept a single command line argument and then pass that argument to /usr/bin/nmap as a scan target. A compiled binary of this source code exists in the path.
/usr/bin/synscan with the sticky bit
As shown in the ‘ls’ output, the /usr/bin/synscan binary has the suid and sgid bits set and it is owned by root:root which means that the execution context of synscan will have the effective permissions of root. This means that the SYN scan can be initiated by any authenticated user rather than just by root users. (Setting the sticky bit on nmap directly could allow all users to execute arbitrary commands as root.)
synscan executes Nmap as root on behalf of the user
Unfortunately for the administrator, there is a fatal flaw in the program logic which can be exploited to gain a root shell. With a little knowledge regarding the stack, it should be clear that line 14 is ripe for exploitation. The use of sprintf() to copy the user-supplied hostname directly into a stack buffer is extremely dangerous. The destination buffer has 64 bytes allocated but no bounds checking is done to verify that argv will fit in a 64 byte buffer. The C programming language leaves memory management up to the user making C as powerful as it is dangerous. (Modern compilers actually do a lot to protect against memory corruption but for the sake of this article we shall consider that the program is compiled without stack canaries and that the operating system does not provide other protection mechanisms such as ASLR. Exploration of these mitigation techniques is reserved for a future post.)
It is no surprise that the process crashes when too much data is passed as the hostname
This crash is possible because the administrator failed to use snprintf(buf, "%s',argv, 64) in place of sprintf(buf, '%s', argv). This is an all too common problem and it isn't limited to sprintf. There is an entire class of abusable unbounded string manipulation functions. (Think: strcpy, strcat, vsprintf, etc.) Unless you are dealing with a controlled or measured buffer, always replace these functions with their bounded counterparts snprintf, strncpy, strncat, etc.
Analysis in the debugger reveals that the program attempted to access memory at the address we specified 0xdeadbeef. How did this happen? Before answering that question it is necessary to understand a little about how the call stack works. To read more about this, continue on to VERT Vuln School: Stack Buffer Overflows 101 (Part 2) which provides some insight into the stack itself and why this program is potentially hazardous. Also please stay tuned to nCircle Connect for Part 3 of this series which will demonstrate functional root shell exploit code for the synscan application.