From debugging to exploiting

Secure Code


Exploiting a program seems trivial, and yet some of these fancy protections discussed in the first section can come in handy. To be fair, most of them, when combined, can prevent or mitigate different exploitation techniques. However, exploits still exist. Modern GNU/Linux distros come with all their binaries compiled with most of these protections on and with their kernels implementing full support for ASLR and the NX bit.

For example, take a look at any arbitrary binary installed on a Debian GNU/Linux 7.0.8 (Wheezy) system (e.g., sshd; Figure 3).

Figure 3: The sshd binary on Debian GNU/Linux 7.0.8, as with the vast majority of its binaries, has been compiled with all protections enabled.

Seeing all these checks enabled could almost put your mind at ease, right? Not quite so. The vast majority of compilers, if not taught to do so, just compile the code without these protections. Thus, the problem arises as soon as any user develops, compiles, and runs a program.

What happens then? The answer would depend on the nature of this source code and whether it has flaws that can lead to potential vulnerabilities. Luckily, the ASLR and NX bit will be enabled, so at least some basic mitigation techniques should be present to prevent stack-smashing attacks.

Some texts about software security emphasize the importance of writing secure code by avoiding functions that are historically insecure, such as strcpy, strcat, memcpy, and so on. However, this approach is not entirely accurate. You can still make use of these functions, as long as the total amount of data read and written is controlled at all times and you're careful with calls to printf and the sort – to avoid format string attacks [8]. Apart from that, you must compile your own code using the protections discussed in this article, although you might suffer low performance during ELF loading or later.

As an example, if you compile your simulation code with full RELRO and it uses lots of calls to external linear algebra libraries, it is going to take a while before it is completely loaded into memory. Although this is not always desirable, sometimes it is acceptable. However, activating modern protections and replacing ancient, insecure standard library functions with theoretically secure functions is not enough. As seen here, the logic of the program is probably the most important element.

In the PoC, for example, although the nxtParticle field came right after a char * buffer of BZSIZE bytes, the pointer was never overwritten by overflowing this buffer, although it would be feasible to do so. Yet, the program's logic allows the program to be exploited in another way, thanks to the bad design of two routines that write to and then load particles from disk. Pretty obviously, the nxtParticle field should never be written to and later loaded from disk. In this case, full RELRO protection would be a perfect deterrent for this particular vulnerability.

Hardening a system is relatively simple just by generating every single new binary with protections enabled. When using the GNU compiler suite, these protections are activated by calling the compiler with certain flags [9]. As an example, consider how you could mitigate the exploitation of the binary discussed in the PoC section with the minimal protections necessary:

gcc -m32 -lm -Wl,-z,relro,-z,now MD.c -o MD.e32.fullrelro

Using the readelf command lists this binary's sections. As expected, this time the .got.plt section does not exist:

readelf -S ./MD.e32.fullrelro | grep .got.plt

Feeding the maliciously crafted particle into this program again does not achieve the same results. This time, the program crashes, making the exploit useless:

Particle { , next at 0x804b5b4
Propagating: {to 0x804b5b4
Program received signal SIGSEGV,Segmentation fault.
0xf7f3fe11 in ?? () from/lib/i386-linux-gnu/i686/cmov/


In this article, I have merely scratched the surface of software security. Myriad papers, talks, utilities, and even academic research focus on exploits, bypassing attack vectors and the like. Yet, most of the time the main problem is bad design or a sloppy line of code mixed with a security-by-default directive. Although it is extremely difficult to be 99% secure, at least you can try.

The Author

Toni Castillo Girona holds a bachelor's degree in Software Engineering and works as an ICT Research Support expert at a public university in Catalunya (Spain). He writes regularly about GNU/Linux in his blog,

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

comments powered by Disqus