Of course I saw that “eval”, so I immediately stopped reading the book and started reading about how I could exploit this little Python program. The book doesn’t mention that this example could be a security problem, but it looks like they wanted to add a comment, because on page 49 it reads:

As mentioned previously, this is potentially dangerous if someone sneaks some malicious cod into our shelve, but we’ll finesse such concerns for now.

In my opinion it would be better to show a programmer how easy it is to specify a whitelist of characters (eg. say a-zA-Z0-9 and the single quote). From a security perspective, incorrect or insufficient input filtering is very dangerous. Although most of the time not the root cause, input filtering can help to prevent buffer overflows, XSS, SQL injection and most other injections. Whitelisting in Python would be done as following:

Of course this whitelist would not cover Unicode characters. So the filter could be changed to a blacklist filter, which allows all Unicode characters, which should still be pretty safe (although blacklists are bad practice):

Of course this is just a workaround, the proper solution would be not to use the eval function. Instead all the input should be treated as strings and only fields with integers (age and pay) should be parsed to integers. The following code is the proper solution for the above example:

As you see, the echo command is executed and printed on the next line. I told myself that was to easy and I wanted to exploit the program in harder circumstances. As described in that article, some people try to “secure” an eval call by overwriting the __builtins__ of Python. So let’s assume the line with eval in the example 1-22 looks as following:

setattr(record, field, eval(newtext, {'__builtins__':{}}))

To clear the builtins is considered a “security measure” for a lot of people, but as described in the article above it just makes it harder to exploit, not impossible. So I wanted to try that segmentation fault technique in the article on the example. It worked well for Python 2.7:

So because the book is about Python 3, we have to change the arguments for the code object constructor. But which arguments does it take? To find that out I started a Python 3 console and typed in the following:

>>> def foo(): pass
...
>>> help(type(foo.__code__))

We get a nice description of the constructor (__init__ method) of the code object and the message that this isn’t for the faint of heart. Fine for me. We get the following argument description for the constructor:

Ok, those two lines still work fine on the interactive Python 3 shell (and by removing one of the first 0 arguments for the code init method it will also work in Python 2.7). Can we exploit the test application now? Yes we can:

Ok, seg faulting is fine, but what about executing real code? For that we have to go a little bit back. First, let’s try again in Python 2.7 with the cool trick described in an article on reddit. If we run the following code in a python interactive interpreter, it will show us the help page (of the help command itself):

Of course if you add some more 9s to those numbers, you can DoS the interpreter.

But if we try to read a file, the restricted mode gets hit on my machine. It seems the restricted mode is “entered when the builtins in main_dict are not the same as the interpreter’s builtins”. Here’s the corresponding code:

Ok, so no subprocesses and no reading of files. Let’s simply do some cool stuff with the system module, like showing /etc/passwd above. Let’s start an HTTP Server which serves a directory listing of the root directory on port 8000:

I guess we’re pretty clear that from this point on we would have control over the machine, which is running the eval command. Here are the payloads (in several different versions) as one liners for Python 2.7. The file, open and fileinput payload fails because of the IOError of the restricted mode when the builtins are different, os.popen fails with a permission denied for me. The rest works on my machine:

So we would have to find another way to restore the builtins or we have to properly build a code object like we did for the segmentation fault above. I guess another thing for my TODO list.
UPDATE 19th Feb 2013:
Talked to Ned, he wrote a nice script to find builtins. And here we go for python 3.3:

Of course now that you have Ned’s script, you can simply find other places where the builtins live, the Pattern is just one example. E.g. if one of theses strings ever gets blacklisted (think web application firewall).