Ruby for Pentesters #1: Use Modules For Lists Of Constants

Thomas Ptacek | July 3rd, 2008 | Filed Under: Uncategorized

Almost 2 years ago, Dino declared Python to be the lingua-franca of over-the-hill hackers”, boldly asserting that 5 out of 6 security hackers under the age of 30 preferred Ruby instead. Being 30 at the time, I was an easy psychological target for this argument. I made the switch and haven’t regretted it. You can tell me all you want that “named nested functions are just as good as lambdas”, or that “you can fake Ruby blocks with a for loop and a generator”. Ruby is just nicer to write testing code in, and makes me feel at least 2 years younger and less experienced than I really am. Thanks, Ruby!

I’ve been meaning to write a long post about our house Ruby style, and some of the Ruby tips and tricks we’ve picked up along the way. But every time I sit down to write it, that post starts sounding a lot like work. So instead, I’d like to inaugurate a new series of much easier posts: Ruby for Pen-testers.

Where was I?

1. Use Modules For Lists Of Constants

If you test protocols or C code, you run into lists of magic numbers all the time. For example, here’s a bit of ptrace(2):

#define PT_TRACE_ME     0       /* child declares it’s being traced */
#define PT_READ_I       1       /* read word in child’s I space */  
#define PT_READ_D       2       /* read word in child’s D space */
#define PT_READ_U       3       /* read word in child’s user structure */
#define PT_WRITE_I      4       /* write word in child’s I space */
#define PT_WRITE_D      5       /* write word in child’s D space */
#define PT_WRITE_U      6       /* write word in child’s user structure */
#define PT_CONTINUE     7       /* continue the child */
#define PT_KILL         8       /* kill the child process */

This is gross, but it’s C code, so you give them a break. But here’s some code from Pedram’s PyDbg:

TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPPROCESS  = 0x00000002
TH32CS_SNAPTHREAD   = 0x00000004
TH32CS_SNAPMODULE   = 0x00000008
TH32CS_INHERIT      = 0x80000000

Now, Pedram does have the excuse of writing in Python. But here’s Ruby-MySql:

COM_SLEEP             = 0
  COM_QUIT              = 1
  COM_INIT_DB          = 2
  COM_QUERY            = 3

This code has no excuse. (Here’s a rewrite that is much faster). Now, let’s look at net-ssh; if you haven’t read Jamis’ net-ssh code, you shouldn’t write any more packet processing code until you do.

module Constants
    # Transport layer generic messages
    DISCONNECT                = 1
    IGNORE                    = 2
    UNIMPLEMENTED             = 3
    DEBUG                     = 4
    # …
end

Getting closer. But not there yet. Here’s an even better way:

module EFlags
    CARRY = (1<< 0)
    X0 = (1<< 1)
    PARITY = (1<< 2)
    # …
    VINT = (1<< 19)
    VINTPENDING = (1<< 20)
    CPUID = (1<< 21)
end

That’s right: one module per set of constants. In other words, substitute “module” for “enum”. This has many benefits:

  1. It’s clean. You can immediately find all the related magic numbers, both from the list, and by looking at code that uses the magic numbers —- you see Ragweed::EFlags::CARRY, you know to look for “EFlags”.

  2. Modules come with special bonus features.

For instance:

class Module
    def to_name_hash
        @name_hash ||= constants.map {|k| [k.intern, const_get(k.intern)]}.to_hash
    end

    def to_value_hash
        @key_hash ||= constants.map {|k| [const_get(k.intern), k.intern]}.to_hash
    end
end

EFlags.to_value_hash[1 << 19] # => :VINT

… which is super nice when you’re printing out the contents of packets.

Viewing 14 Comments

Trackbacks

close Reblog this comment
blog comments powered by Disqus