Quick and Dirty Reverse Engineering

Posted on 15 November 2019

A while ago I had to work with a particularly frustrating application that was required to connect to a hardware appliance. Both of which will remain unnamed in this post.

This application generates it own self signed certificate which it uses to connect to the appliance. The issue was that application generates sha1 certs but the appliance only supports sha2. No problem just use the private key from the application to create a sha2 cert.

But! The application encrypts the private key with a password. Bugga. Furthermore the application utilizes a obfuscated password generator which makes it hard to debug.

The Process:

  1. When the application creates the key it prints out “Private Key created and written to”:

     [tom@tomandtim reverse_engineering]$ ./program cert add 192.168.0.16 2048
     Key size set: 2048
     Private Key created and written to: /home/tom/reverse_engineering/cert/client/192.168.0.16Key.pem
     Certificate created and written to: /home/tom/reverse_engineering/cert/client/192.168.0.16.pem
     [tom@tomandtim reverse_engineering]$
    
    
  2. Using Ghidra (the NSA software reverse engineering suite) we can open up the program and find where in the code it uses that string.

    searching for strings

  3. Looking at the code we can see it uses the PEM_write_bio_PrivateKey function of openssl to encrypt and save the private key. So that means at the time of execution the password must be held in memory. So if we pause the application and then dump it’s memory we can find the password!

    searching for strings

  4. Using gdb we can set a breakpoint, run it and then dump the memory using a little memory dumper I wrote

    1. Start gdb, set the breakpoint and run the program:

       [tom@tomandtim reverse_engineering]$ gdb --args ./program cert add 192.168.0.16 2048
       (gdb) break PEM_write_bio_PrivateKey
       Breakpoint 1 at 0x58f260
       (gdb) run
       Starting program: /home/tom/reverse_engineering/./program cert add 192.168.0.16 2048
       [Thread debugging using libthread_db enabled]
       Using host libthread_db library "/lib64/libthread_db.so.1".
       Key size set: 2048
       Breakpoint 1, 0x000000000058f260 in PEM_write_bio_PrivateKey ()
      
    2. In a new shell locate the pid using ps and run the ./mem_dump.sh script. Pay close attention to the script arguments, because we are already debugging the script we use the “dryrun” function to give us the gdb commands we need for the debugging session we already have open. You can disable the dryrun to allow the script todo the dump for you if required.

       [tom@tomandtim reverse_engineering]$ ./mem_dump.sh
       ./mem_dump.sh pid dryrun
      
       PID = PID you want do dump
       dryrun = "yes" to just print the GDB commands or "no" to dump the memory
      
       Example:
      
       ./mem_dump.sh 1234 no
      
       [tom@tomandtim reverse_engineering]$
       [tom@tomandtim reverse_engineering]$ ps -ef|grep program
       tom      10850  8534  0 17:12 pts/4    00:00:00 gdb -q --args ./program cert add 192.168.0.16 2048
       tom      2037  10850  0 17:12 pts/4    00:00:00 /home/tom/reverse_engineering/./program cert add 192.168.0.16 2048
       tom      10890 15995  0 17:12 pts/7    00:00:00 grep --color=auto program
       [tom@tomandtim reverse_engineering]$ ./mem_dump.sh 2037 yes
       Dumping Stack...
       dump memory /tmp/mem_dump_pid_2037_7ffffffde000-7ffffffff000.mem 0x7ffffffde000 0x7ffffffff000
       Dumping Heap...
       dump memory /tmp/mem_dump_pid_2037_007db000-0081f000.mem 0x007db000 0x0081f000
       Dumping Program...
       dump memory /tmp/mem_dump_pid_2037_00400000-00696000.mem 0x00400000 0x00696000
       dump memory /tmp/mem_dump_pid_2037_00796000-007db000.mem 0x00796000 0x007db000
       Done
       [tom@tomandtim reverse_engineering]$
      
    3. Type those commands into gdb and allow the program to continue

       (gdb) dump memory /tmp/mem_dump_pid_2037_7ffffffde000-7ffffffff000.mem 0x7ffffffde000 0x7ffffffff000
       (gdb) dump memory /tmp/mem_dump_pid_2037_007db000-0081f000.mem 0x007db000 0x0081f000
       (gdb) dump memory /tmp/mem_dump_pid_2037_00400000-00696000.mem 0x00400000 0x00696000
       (gdb) dump memory /tmp/mem_dump_pid_2037_00796000-007db000.mem 0x00796000 0x007db000
       (gdb) c
       Continuing.
       Private Key created and written to: /home/tom/reverse_engineering/cert/client/192.168.0.16Key.pem
       Certificate created and written to: /home/tom/reverse_engineering/cert/client/192.168.0.16.pem
       [Inferior 1 (process 2037) exited normally]
       (gdb) q
       [tom@tomandtim reverse_engineering]$
      
  5. Now we can look through the dump files for a password

     [tom@tomandtim reverse_engineering]$ strings /tmp/mem_dump_pid_2037_007db000-0081f000.mem
     &=Un
     9J\o
     6Me~
     )IZl
     ;Wt
     0F]u
    
     bla bla bla 
    
     authorityKeyIdentifier
     keyid:always,issuer:always
     basicConstraints
     CA:true
     4%::<'}z4k$y    <------- That looks like a password!
     authorityKeyIdentifier
     keyid:always,issuer:always
    
     'A\x
     <Nau
     ":Sm
     /ATh}
     2Jc}
     .?Qdx
     <Yw
    
  6. And try it out:

     [tom@tomandtim reverse_engineering]$ echo "4%::<'}z4k\$y" |openssl rsa -in /home/tom/reverse_engineering/cert/client/192.168.0.16Key.pem -check -noout -passin stdin
     RSA key ok
     [tom@tomandtim reverse_engineering]$
    
  7. Done!