Opera 12 (Presto) password extractor (Linux)

This is a tool for extracting and decrypting the passwords stored in "wand.dat" by the so-called "Wand" password manager of the Linux version of the Opera browser up to version 12 (ie. using the Presto engine). It is presented here because the original sources appear to have conked out.

It is straightforward to discover http://github.com/antmak/unwand-opera by a web search using the obvious terms, but it turns out to be fucked. It lists 5 files, but all of them give a 404 when I try to actually look at them. I don't know if this is because the authors of Github - A site where nothing fucking works have found something new to break, or just because the unwand-opera repository is fucked of its own nature, but for whichever cause, it's bleeding useless and I can't fucking read any of it.

GITHUB - FOR FUCK'S SAKE STOP USING THAT STUPID FUCKING "REACT" JAVASHIT BOLLOCKS ALREADY.

It does give a link to http://k0derz.ru/linux-opera-passwords-recovery as the source of the original code, but that is also fucked: it looks like the entire site is now defunct. Fortunately I did manage to find a copy of the page at http://web.archive.org/web/20170108132631/http://k0derz.ru/linux-opera-passwords-recovery/ which does work.

So I copied the code, and gave it a bit of a tidy-up - things like replacing the pointless use of C++ iostream crap to print the output with straight printf(), so it's now all plain C, and putting the variable declarations all together at the beginning of the function instead of spattering them here and there all over the code. I fucking hate that. It's WHELK INFUSING.

Compiling it produced a screen full of warnings about bits of crypto stuff being "deprecated" (cunt), so I should probably recommend compiling it using libssl 3.2.1-3 at the latest, but apart from that nothing to worry about.

Anyway, on testing it, it worked just fine, so here it is.

Download: unwand.c.gz

/* sna@reteam.org - 6th of April 2005 * (was: unwand.cpp) * Modified by Pigeon, Nov. 2025: tidy up, remove unnecessary C++ * unwand.c * gcc unwand.c -O2 -o unwand -lssl -lcrypto * NOTE: using libssl 3.2.1-3 it spews loads of warnings about "MD5" and "DES_..." * being "deprecated", so with later versions it might not work. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/md5.h> #include <openssl/des.h> const unsigned char opera_salt[11] = { 0x83, 0x7D, 0xFC, 0x0F, 0x8E, 0xB3, 0xE8, 0x69, 0x73, 0xAF, 0xFF }; int main(int argc, char **argv) { FILE *fdWand; unsigned long fileSize, wandOffset; unsigned char *wandData, *wandKey; unsigned char *blockLengthPtr, *dataLengthPtr; unsigned long blockLength, dataLength; unsigned char hashSignature1[MD5_DIGEST_LENGTH], hashSignature2[MD5_DIGEST_LENGTH], tmpBuffer[512]; DES_key_schedule key_schedule1, key_schedule2, key_schedule3; DES_cblock iVector; unsigned char *cryptoData, *padding; unsigned long n; unsigned i; int uch; if (argc != 2) { fprintf(stderr, "Usage: unwand <opera wand file>\n"); exit(1); } if (!(fdWand = fopen(argv[1], "r"))) { fprintf(stderr, "%s: cannot open\n", argv[1]); exit(1); } fseek(fdWand, 0, SEEK_END); fileSize = ftell(fdWand); if (!(wandData = (unsigned char *)malloc(fileSize))) { fclose(fdWand); fprintf(stderr, "malloc(%lu) failed!!!\n", fileSize); exit(1); } rewind(fdWand); n = fread(wandData, 1, fileSize, fdWand); fclose(fdWand); if (n < fileSize) { fprintf(stderr, "%s: short read (%lu vs. %lu)\n", argv[1], n, fileSize); exit(1); } wandOffset = 0; /* main loop, find and process encrypted blocks */ while (wandOffset < fileSize) { /* find key length field at start of block */ wandKey = (unsigned char *) memchr(wandData + wandOffset, DES_KEY_SZ, fileSize - wandOffset); if (!wandKey) break; wandOffset = ++wandKey - wandData; /* create pointers to length fields */ blockLengthPtr = wandKey - 8; dataLengthPtr = wandKey + DES_KEY_SZ; if ((blockLengthPtr < wandData) || (dataLengthPtr > (wandData + fileSize))) continue; /* convert big-endian numbers to native */ blockLength = *blockLengthPtr++ << 24; blockLength |= *blockLengthPtr++ << 16; blockLength |= *blockLengthPtr++ << 8; blockLength |= *blockLengthPtr; dataLength = *dataLengthPtr++ << 24; dataLength |= *dataLengthPtr++ << 16; dataLength |= *dataLengthPtr++ << 8; dataLength |= *dataLengthPtr; /* as discussed in the article * (what article?) */ if (blockLength != (dataLength + DES_KEY_SZ + 4 + 4)) continue; /* perform basic sanity checks on data length */ if ((dataLength > (fileSize - (wandOffset + DES_KEY_SZ + 4))) || (dataLength < 8) || ((dataLength % 8) != 0)) continue; /* hashing of (salt, key), (hash, salt, key) */ memcpy(tmpBuffer, opera_salt, sizeof(opera_salt)); memcpy(tmpBuffer + sizeof(opera_salt), wandKey, DES_KEY_SZ); MD5(tmpBuffer, sizeof(opera_salt) + DES_KEY_SZ, hashSignature1); memcpy(tmpBuffer, hashSignature1, sizeof(hashSignature1)); memcpy(tmpBuffer + sizeof(hashSignature1), opera_salt, sizeof(opera_salt)); memcpy(tmpBuffer + sizeof(hashSignature1) + sizeof(opera_salt), wandKey, DES_KEY_SZ); MD5(tmpBuffer, sizeof(hashSignature1) + sizeof(opera_salt) + DES_KEY_SZ, hashSignature2); /* schedule keys. key material from hashes */ DES_set_key_unchecked((const_DES_cblock *)&hashSignature1[0], &key_schedule1); DES_set_key_unchecked((const_DES_cblock *)&hashSignature1[8], &key_schedule2); DES_set_key_unchecked((const_DES_cblock *)&hashSignature2[0], &key_schedule3); memcpy(iVector, &hashSignature2[8], sizeof(DES_cblock)); cryptoData = wandKey + DES_KEY_SZ + 4; /* decrypt wand data in place using 3DES-CBC */ DES_ede3_cbc_encrypt(cryptoData, cryptoData, dataLength, &key_schedule1, &key_schedule2, &key_schedule3, &iVector, 0); if((*cryptoData == 0x00) || (*cryptoData == 0x08)) { printf("<null>\n"); } else { /* remove padding (data padded up to next block) */ padding = cryptoData + dataLength - 1; memset(padding - (*padding - 1), 0x00, *padding); /* dump byte-aligned data[dataLength] little endian UTF-16 as UTF-8. (c) Madhu */ for (i = 0; i < dataLength; i += 2) { uch = cryptoData[i]; uch = uch | cryptoData[i + 1]; if (uch == 0) break; if (uch > 0x7FF) { printf("%c%c%c", (((uch >> 12) & 0xF) | 0xE0), (((uch >> 6) & 0x3F) | 0x80), ((uch & 0x3F) | 0x80)); } else if (uch > 0x7F) { printf("%c%c", (((uch >> 6) & 0x1F) | 0xC0), ((uch & 0x3F) | 0x80)); } else { printf("%c", uch); } } } printf("\n"); wandOffset = wandOffset + DES_KEY_SZ + 4 + dataLength; } free(wandData); return 0; }




Back to Pigeon's Nest


Be kind to pigeons




Valid HTML 4.01!