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.
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]$
Using Ghidra (the NSA software reverse engineering suite) we can open up the program and find where in the code it uses that string.
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!
Using gdb
we can set a breakpoint, run it and then dump the memory using a little memory dumper I wrote
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 ()
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]$
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]$
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
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]$
Done!