How to find long-lost passwords in your clipboard history
Inspecting the 'Paste' database.
generated using DALL·E 3
When I cloned an old Android project and tried to build it, I discovered that the release signing key was missing. No problem, I use Bitwarden as my password manager, so the signing key must be saved there – or so I thought. there was an entry for the signing key, but it was empty. It seemed like I had forgotten to paste the password when creating the entry.
After some despair, I remembered where the key might be found: in my clipboard.
I use Paste app to easily copy & paste multiple items at a time and keep a history of all my clipboard items.
The signing key I generated in Bitwarden must still be in my clipboard history! But how do I find it?
Paste has fuzzy search to find items in my clipboard history, which works pretty well. But of course it can’t be used to find an unknown string, so I needed a different approach. I knew when the key was generated, thanks to the creation date of the entry in Bitwarden, but manually scrolling through thousands of items I had copied in the past months was not an option. There has to be another way, right?
# Searching the data
The app must have a database somewhere containing all copied snippets. I was able to find it by inspecting the Paste.app
process in the MacOS Activity Monitor. The Open Files and Ports
tab contains an interesting file handle: Paste.db
.
At first, I was unsure whether I’d be able to easily read the database’s contents, but my first hunch turned out to be correct. It’s simply SQLite. Using the command line to browse through an unknown database is pretty cumbersome, so I used DB Browser for SQLite, which makes it easy to explore SQLite databases. There are a dozen tables, but the interesting one is ZSNIPPET
.
This table contains everything I copied in the past 6 months. My first instinct was to use the timestamp
to quickly jump to the time the Bitwarden note was created. However, for some reason, Paste’s timestamps do not match usual Unix timestamps. I also tried using a known timestamp to calculate the offset between the two, but this didn’t work either.
# Brute force
The next-best solution that came to my mind, was to search through all data.
Before generating the release signing key, I had also copied the command to create the keystore. So I’d just need to find this snippet and go from there.
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
However, I couldn’t just search it using SQLite, since copied text is stored as a binary blob in the database. Using the blob header bplist00
, I found it to use Apple’s “Information Property List” format. And, after dusting off my SQL, I was able to extract all blobs and convert them from ‘plist’ to XML.
$ mkdir bins && cd bins
$ sqlite3 Paste.db "select writefile('object' || ZTIMESTAMP || '.bin', ZPREVIEW2) FROM ZSNIPPET;"
$ ls . | awk '{print $1}' | xargs plutil -convert xml1
After that, I used the excellent tool ripgrep to search through all XML files for the known string keytool
.
$ rg "keytool"
object701805006.782251.xml
27: <string> keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
Now it was just a matter of querying the database using a range around the timestamp 701805006.782251
. And there it was, the keystore password!
# Final thoughts
Don’t be an idiot, make sure to save your passwords.
But the interesting detail here might be security. Any application could find the Paste database and extract all passwords, tokens & keys I’ve ever copied. No sudo
required. A password might even have some additional context thanks to other nearby clipboard items like URLs or email addresses.
# More
- Read how I redesigned this website here → Pretty fonts for a modern look.
- I spent two months developing a survey creation platform, deployed it to >300k users and compared it to Google Forms and SurveyJS. → Comparing Survey Creation Platforms.