Skip to content

A Review of the Third Chapter of the Second Edition of “Hacking: The Art of Exploitation”

After I wrote a review of the first two chapters of the second edition of “Hacking: The Art of Exploitation” by Jon Erickson, I considered writing a review of the third chapter of it. Now that I have finished reading the third chapter of that book, which is the chapter on program exploitation, I review that chapter. In my review of the first two chapters of the book, I mentioned that the second chapter of it alone was worth the price of the book. In this review, I explain how this third chapter is as good as the chapter that preceded it.

When programmers first learn about programming, they tend to focus on simply making the programs that they write appear to run properly. Once these inexperienced programmers find that their programs appear to be free of errors, they may think that they do not need to do anything else with the programs that they have written. However, programs that seem to run correctly may have issues that could lead to unintended consequences. A mistake that is sometimes made by those who teach programming is that of emphasizing getting programs to work in typical cases. The chapter in this book on programming puts much emphasis on going beyond viewing programs as a series of statements written in a high-level language to accomplish certain tasks. This is done for reasons that become quite evident in this chapter on program exploitation.

In this third chapter of the book, methods of taking advantage of certain programming practices that should be avoided are covered. This chapter is divided into sections in which concepts such as buffer overflows and format string vulnerabilities are explained. These sections are divided into subsections in which there are examples that illustrate how these vulnerabilities can be exploited. These examples progressively increase in complexity, efficiency, and effectiveness. For example, in the section on stack-based buffer overflows, an example is given that demonstrates how a buffer being overflowed can affect what is stored in other variables. Then the possible unintended consequence of overwriting a buffer that stores a boolean value on whether or not access is to be granted is illustrated. However, this situation is one that the author admits is contrived, as whether or not it will occur depends on where variables are located in memory. Later, it is demonstrated how the GNU debugger can be used to show where one can see the memory address to which the program is to be directed. And then it is shown how that address can be overwritten with an arbitrary address when the program has a buffer overflow vulnerability. Then an assembler dump is given to show which memory addresses to which a program can be pointed. Then it is shown how memory can be overwritten with instructions that contain shell-spawning code. Then it shown how shellcode can be stored in environment variables to make attacks more efficient.

After short sections on heap-based buffer overflows and overflowing of function pointers, format string vulnerabilities are explained. In the section on format string vulnerabilities, it is demonstrated how code that appears to do what it is supposed to do can have very serious problems. It is first demonstrated how memory on the stack frame, and then in any other location can be viewed when format parameters are missing from printf function calls. Then the techniques for writing to memory addresses through the use of direct parameter access and short writes are given. It is once again shown how overwriting of memory can lead to shellcode being run through the use of methods that are similar to those given in the section on buffer overflows. It is also demonstrated how .dtors and the global offset table can have their memory addresses overwritten so that shellcode can be executed.

The author does well in explaining concepts by giving examples of more basic attacks first, and then demonstrating how more complex attacks work by building on the concepts that were previously explained. Some concepts, however, are not clearly explained. However, when a code example or concept does not quite seem to be clear to readers, they can perform their own experiments by modifying the source code that is on the CD that came with the book. As I myself experimented with the source code, I wondered if it would have been a good idea for there to be exercises included in the book for the reader to complete, as if this book were a textbook. However, readers for whom the book was intended, the inquisitive individuals who truly are hackers, should be able to come up with their own exercises so that they can reinforce their knowledge of material taught in the book. Astute readers will also try to predict upcoming material, as they may see how the different concepts taught in the book fit together. For example, when the section on .dtors and the global offset table are covered, one can predict how the memory addresses to which a program’s execution jumps can be overwritten with addresses of environment variables that contain shellcode.

This chapter was very informative in explaining and demonstrating how these exploits work. I liked how simple concepts were explained, then more complex ones were explained that built on the more basic concepts. Perhaps some of the material could have been explained better. However, there is no substitute for the practical experience that one can gain by working with the many examples that are provided in this book and on the CD that came with it. And there are many code examples included so that those who want to get the most out of the book can easily do so. Explanations of concepts are given mostly in the lucid and detailed manner that I have come to expect when reading this book. I look forward to reading the next chapter of the book, which is the chapter on networking. I also look forward to reviewing it.