diff -Nru pdfcrack-0.9/changelog pdfcrack-0.10/changelog --- pdfcrack-0.9/changelog 2007-10-23 12:23:21.000000000 -0500 +++ pdfcrack-0.10/changelog 2008-04-30 15:44:34.000000000 -0500 @@ -1,3 +1,15 @@ +2008-05-01 Henning Norén - 0.10 +* Add support for different newline-styles in wordlists +* Fix three nasty issues when parsing binary strings + A big thanks to Will McCullen who reported and helped debug them +* Change step 6 when computing the encryption key to only apply to rev 3+ +* Add warnings when the parser got a O/U binary string != 32 bytes +* Add support for non-hex ID strings +* Change error messages when not finding the ID or Encrypt object in trailer +* Try with empty password when bruteforcing from 0 characters (#1767244) +* Mention pdftk in the README (#1767245) +* Start search for the trailer in the last KB +* bump version to 0.10 2007-10-23 Henning Norén - 0.9 * flush the buffers in printProgress * make use of %zu when printf-ing size_t diff -Nru pdfcrack-0.9/debian/changelog pdfcrack-0.10/debian/changelog --- pdfcrack-0.9/debian/changelog 2008-09-20 08:12:31.000000000 -0500 +++ pdfcrack-0.10/debian/changelog 2008-09-20 08:12:31.000000000 -0500 @@ -1,3 +1,18 @@ +pdfcrack (0.10-1ubuntu1) intrepid; urgency=low + + * Merge from Debian unstable, remaining changes (LP: #272468): + - Suggest pdf-viewer instead of pdf-reader + + -- Nathan Handler Sat, 20 Sep 2008 07:55:10 -0500 + +pdfcrack (0.10-1) unstable; urgency=low + + * New upstream release (closes: #490187). + * debian/control + + Set Standards-Version to 3.8.0 (no changes). + + -- Nacho Barrientos Arias Wed, 23 Jul 2008 11:16:06 +0200 + pdfcrack (0.9-1ubuntu1) intrepid; urgency=low * Suggest pdf-viewer instead of pdf-reader (LP: #224507) diff -Nru pdfcrack-0.9/debian/control pdfcrack-0.10/debian/control --- pdfcrack-0.9/debian/control 2008-09-20 08:12:31.000000000 -0500 +++ pdfcrack-0.10/debian/control 2008-09-20 08:12:31.000000000 -0500 @@ -4,7 +4,7 @@ Maintainer: Ubuntu MOTU Developers XSBC-Original-Maintainer: Nacho Barrientos Arias Build-Depends: debhelper (>= 5), quilt -Standards-Version: 3.7.2 +Standards-Version: 3.8.0 Homepage: http://sourceforge.net/projects/pdfcrack Package: pdfcrack diff -Nru pdfcrack-0.9/main.c pdfcrack-0.10/main.c --- pdfcrack-0.9/main.c 2007-03-11 18:57:43.000000000 -0500 +++ pdfcrack-0.10/main.c 2008-03-14 19:24:01.000000000 -0500 @@ -31,7 +31,7 @@ #define PRINTERVAL 20 /** Print Progress Interval (seconds) */ #define CRASHFILE "savedstate.sav" #define VERSION_MAJOR 0 -#define VERSION_MINOR 9 +#define VERSION_MINOR 10 /** alarmInterrupt is used to print out the progress at specific intervals */ static void @@ -252,17 +252,13 @@ if(ret) { if(ret == EENCNF) fprintf(stderr, "Error: Could not extract encryption information\n"); - else if(ret == ETRANF) - fprintf(stderr, "Error: First trailer not found\n"); - else if(ret == ETRENF) - fprintf(stderr, "Error: Encryption object not found in trailer\n"); - else if(ret == ETRINF) - fprintf(stderr, "Error: ID object not found in trailer\n"); + else if(ret == ETRANF || ret == ETRENF || ret == ETRINF) + fprintf(stderr, "Error: Encryption not detected (is the document password protected?)\n"); ret = 4; goto out1; } else if(e->revision < 2 || (strcmp(e->s_handler,"Standard") != 0)) { - fprintf(stderr, "The specific version is not supported\n"); + fprintf(stderr, "The specific version is not supported (%s - %d)\n", e->s_handler, e->revision); ret = 5; goto out1; } diff -Nru pdfcrack-0.9/Makefile pdfcrack-0.10/Makefile --- pdfcrack-0.9/Makefile 2006-10-27 07:52:08.000000000 -0500 +++ pdfcrack-0.10/Makefile 2008-04-30 16:14:58.000000000 -0500 @@ -1,5 +1,5 @@ CFLAGS= -Wall -Wshadow -Wwrite-strings -Wsign-compare -Wfloat-equal \ - -Wconversion -Wmissing-noreturn -Wbad-function-cast \ + -Wmissing-noreturn -Wbad-function-cast \ -Wmissing-prototypes -Winline -Wredundant-decls -O3 all: pdfcrack diff -Nru pdfcrack-0.9/passwords.c pdfcrack-0.10/passwords.c --- pdfcrack-0.9/passwords.c 2007-10-23 12:39:50.000000000 -0500 +++ pdfcrack-0.10/passwords.c 2008-04-30 15:43:21.000000000 -0500 @@ -1,5 +1,5 @@ /** - * Copyright (C) 2006 Henning Norén + * Copyright (C) 2006-2008 Henning Norén * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,16 +50,21 @@ passlength = 0; ch = getc(wordList); - while(ch != '\n' && ch != EOF && passlength < 32) { + while(ch != '\n' && ch != '\r' && ch != EOF && passlength < 32) { outbuf[passlength++] = ch; ch = getc(wordList); } /** clean up garbage of passwords longed than 32 chars */ if(unlikely(passlength == 32)) - while(ch != '\n' && ch != EOF) + while(ch != '\n' && ch != '\r' && ch != EOF) ch = getc(wordList); + if(ch == '\r') { + ch = getc(wordList); + if(ch != '\n') + ungetc(ch, wordList); + } if(unlikely(ch == EOF)) wlMore = false; @@ -133,7 +138,7 @@ charset = stdchars; charsetLen = strlen((const char*)charset); - /** Make sure that max- and min-password is smaller than 32 */ + /** Make sure that max- and min-password are smaller than 32 */ if(maxPw < PASSLENGTH) maxPasswordLen = maxPw; else diff -Nru pdfcrack-0.9/pdfcrack.c pdfcrack-0.10/pdfcrack.c --- pdfcrack-0.9/pdfcrack.c 2007-10-23 12:42:21.000000000 -0500 +++ pdfcrack-0.10/pdfcrack.c 2008-04-30 15:47:49.000000000 -0500 @@ -1,5 +1,5 @@ /** - * Copyright (C) 2006 Henning Norén + * Copyright (C) 2006-2008 Henning Norén * Copyright (C) 1996-2005 Glyph & Cog, LLC. * * This program is free software; you can redistribute it and/or @@ -46,7 +46,7 @@ /** buffers for stuff that we can precompute before the actual cracking */ static uint8_t *encKeyWorkSpace; -static uint8_t password_user[32]; +static uint8_t password_user[33]; static uint8_t *rev3TestKey; static unsigned int ekwlen; @@ -97,7 +97,7 @@ const int permissions, const uint8_t *ownerkey, const uint8_t *fileID, const unsigned int fileIDLen) { /** - * Algorithm 3.2 Computing an encryption key (PDF Reference, v 1.6, p.101) + * Algorithm 3.2 Computing an encryption key (PDF Reference, v 1.7, p.125) * * Make space for: * field | bytes @@ -108,7 +108,7 @@ * fileID | * [extra padding] | [4] (Special for step 6) **/ - unsigned int size = (revision > 2 && !encMetaData) ? 72 : 68; + unsigned int size = (revision > 3 && !encMetaData) ? 72 : 68; encKeyWorkSpace = malloc(size + fileIDLen); /** Just to be sure we have no uninitalized stuff in the workspace */ @@ -127,7 +127,7 @@ memcpy(encKeyWorkSpace + 68, fileID, fileIDLen); /** 6 */ - if(revision > 2 && !encMetaData) { + if(revision > 3 && !encMetaData) { encKeyWorkSpace[68+fileIDLen] = 0xff; encKeyWorkSpace[69+fileIDLen] = 0xff; encKeyWorkSpace[70+fileIDLen] = 0xff; @@ -195,14 +195,23 @@ foundPassword(void) { char str[33]; - /** The only password that can be found in plaintext is the password pointed - to by currPW. If we for example would like to print out the userpassword - when we have found the correct ownerpassword we need to recalculate it - from this password - */ memcpy(str,currPW,currPWLen); str[currPWLen] = '\0'; printf("found %s-password: '%s'\n", workWithUser?"user":"owner", str); + + /** The only password that can be found in plaintext is the password pointed + to by currPW. If SHOWUSERPASSWORD is define we will get the password + via user_password _including_ the pad. + For this to be really usable we would print out the possible userpasswords + based on finding possible beginnings of the pad (there are real passwords + which can be used which matches the same as a padded (not that it really + matters)). + */ + +#ifdef SHOWUSERPASSWORD + if(!workWithUser) + printf("found user-password: '%s'\n", password_user); +#endif } /** Common handling of the key for all rev3-functions */ @@ -270,7 +279,7 @@ lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -278,12 +287,16 @@ rc4Decrypt(enckey, encdata->o_string, 32, encKeyWorkSpace); md5(encKeyWorkSpace, ekwlen, enckey); - if(rc4Match40b(enckey, encdata->u_string, pad)) + if(rc4Match40b(enckey, encdata->u_string, pad)) { +#ifdef SHOWUSERPASSWORD + memcpy(password_user, encKeyWorkSpace, 32); +#endif return true; + } ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } @@ -296,7 +309,7 @@ length = encdata->length/8; lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -308,12 +321,16 @@ RC4_DECRYPT_REV3(32); memcpy(encKeyWorkSpace, test, 32); - if(isUserPasswordRev3()) + if(isUserPasswordRev3()) { +#ifdef SHOWUSERPASSWORD + memcpy(password_user, encKeyWorkSpace, 32); +#endif return true; + } ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } @@ -324,7 +341,7 @@ lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -336,7 +353,7 @@ ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } @@ -349,7 +366,7 @@ length = encdata->length/8; lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -370,7 +387,7 @@ ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } @@ -383,7 +400,7 @@ length = encdata->length/8; lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -405,7 +422,7 @@ ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } @@ -417,7 +434,7 @@ lpasslength = 0; startTime = time(NULL); - while(nextPassword()) { + do { BEGIN_CRACK_LOOP(); do { @@ -429,7 +446,7 @@ ++nrprocessed; } while(permutate()); - } + } while(nextPassword()); return false; } diff -Nru pdfcrack-0.9/pdfparser.c pdfcrack-0.10/pdfparser.c --- pdfcrack-0.9/pdfparser.c 2006-10-27 07:52:08.000000000 -0500 +++ pdfcrack-0.10/pdfparser.c 2008-04-30 16:14:39.000000000 -0500 @@ -1,5 +1,5 @@ /** - * Copyright (C) 2006 Henning Norén + * Copyright (C) 2006-2008 Henning Norén * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,6 +25,13 @@ /** Please rewrite all of this file in a clean and stable way */ +struct p_str { + uint8_t *content; + uint8_t len; +}; + +typedef struct p_str p_str; + __attribute__ ((pure)) static inline bool isWhiteSpace(const int ch) { return (ch == 0x20 || (ch >= 0x09 && ch <= 0x0d) || ch == 0x00); @@ -78,13 +85,13 @@ return i; } -static inline int +static int parseInt(FILE *file) { return parseIntWithC(file, getc(file)); } -static inline char +static char parseWhiteSpace(FILE *file) { int ch; do { @@ -186,31 +193,38 @@ return 0; } -static uint8_t* +static p_str* parseHexString(const uint8_t *buf, const unsigned int len) { unsigned int i,j; - uint8_t *ret; - - ret = malloc(sizeof(uint8_t)*(len/2)); + p_str *ret; + + ret = malloc(sizeof(p_str)); + ret->content = malloc(sizeof(uint8_t)*(len/2)); + ret->len = (len/2); for(i=0, j=0; icontent[j] = hexToInt(buf[i]) * 16; + ret->content[j] += hexToInt(buf[i+1]); j++; } + return ret; } -static uint8_t* +static p_str* objStringToByte(const uint8_t* str, const unsigned int len) { - unsigned int i, l; + unsigned int i, j, l; uint8_t b, d; uint8_t tmp[len]; - uint8_t *ret; + p_str *ret; for(i=0, l=0; i= '0' && str[i] < '8') { d = 0; - for(; i= '0' && str[i] < '8' && - (d*8)+str[i] < 256; i++) { + (d*8)+(str[i]-'0') < 256; j++, i++) { d *= 8; d += (str[i]-'0'); } + /** + * We need to step back one step if we reached the end of string + * or the end of digits (like for example \0000) + **/ + if(i < len || j < 3) { + i--; + } + b = d; } } @@ -253,17 +275,20 @@ tmp[l] = b; } - ret = malloc(sizeof(uint8_t)*l); - memcpy(ret, tmp, l); - + ret = malloc(sizeof(p_str)); + ret->content = malloc(sizeof(uint8_t)*(l)); + ret->len = l-1; + + memcpy(ret->content, tmp, l); + return ret; } -static uint8_t* +static p_str* parseRegularString(FILE *file) { unsigned int len, p; int ch; - uint8_t *ret; + p_str *ret; uint8_t buf[BUFFSIZE]; bool skip = false; @@ -310,11 +335,10 @@ findTrailer(FILE *file, EncData *e) { int ch; /** int pos_i; */ - unsigned int i = 0; bool encrypt = false; bool id = false; int e_pos = -1; - uint8_t buf[BUFFSIZE]; + p_str *str = NULL; ch = getc(file); while(ch != EOF) { @@ -357,34 +381,29 @@ ch = parseWhiteSpace(file); while(ch != '[' && ch != EOF) ch = getc(file); - while(ch != '<' && ch != EOF) - ch = getc(file); - i = 0; - while(ch != '>' && i < BUFFSIZE && ch != EOF) { - if((ch >= '0' && ch <= '9') || - (ch >= 'a' && ch <= 'f') || - (ch >= 'A' && ch <= 'F')) { - buf[i++] = ch; - } - ch = getc(file); + + if(str) { + if(str->content) + free(str->content); + free(str); } - if(ch == EOF) - break; - if(i % 2 != 0) - buf[++i] = '0'; -/** + + str = parseRegularString(file); + /** pos_i = ftell(file); printf("found ID at pos %x\n", pos_i); */ - id = true; + if(str) + id = true; ch = getc(file); } else ch = getc(file); if(encrypt && id) { /**printf("found all, returning: epos: %d\n",e_pos);*/ - e->fileID = parseHexString(buf, i); - e->fileIDLen = (i/2); + e->fileID = str->content; + e->fileIDLen = str->len; + free(str); return e_pos; } } @@ -399,8 +418,14 @@ } /** printf("finished searching\n");*/ + if(str) { + if(str->content) + free(str->content); + free(str); + } + if(!encrypt && id) - return ETRENF; + return ETRENF; else if(!id && encrypt) return ETRINF; else @@ -418,6 +443,7 @@ bool fr = false; bool fu = false; bool fv = false; + p_str *str = NULL; ch = getc(file); while(ch != EOF) { @@ -466,7 +492,12 @@ } break; case 'O': - e->o_string = parseRegularString(file); + str = parseRegularString(file); + if(str->len != 32) + fprintf(stderr, "WARNING: O-String != 32 Bytes: %d\n", str->len); + e->o_string = str->content; + if(str) + free(str); fo = true; break; case 'P': @@ -486,7 +517,12 @@ } break; case 'U': - e->u_string = parseRegularString(file); + str = parseRegularString(file); + if(str->len != 32) + fprintf(stderr, "WARNING: U-String != 32 Bytes: %d\n", str->len); + e->u_string = str->content; + if(str) + free(str); fu = true; break; case 'V': @@ -552,17 +588,20 @@ int getEncryptedInfo(FILE *file, EncData *e) { - int e_pos; + int e_pos = -1; bool ret; - e_pos = findTrailer(file, e); - if(e_pos < 0) - return e_pos; - - ret = findEncryptObject(file, e_pos, e); - if(!ret) { + + if(!fseek(file, 0L, SEEK_END-1024)) + e_pos = findTrailer(file, e); + if(e_pos < 0) { rewind(file); - ret = findEncryptObject(file, e_pos, e); + e_pos = findTrailer(file, e); } + if(e_pos < 0) { + return e_pos; + } + rewind(file); + ret = findEncryptObject(file, e_pos, e); if(!ret) return EENCNF; diff -Nru pdfcrack-0.9/README pdfcrack-0.10/README --- pdfcrack-0.9/README 2006-10-27 07:52:08.000000000 -0500 +++ pdfcrack-0.10/README 2008-04-30 16:34:29.000000000 -0500 @@ -1,4 +1,4 @@ -Code and documentation are copyright 2006 Henning Norén +Code and documentation are copyright 2006-2008 Henning Norén Parts of pdfcrack.c and md5.c is derived/copied/inspired from xpdf/poppler and are copyright 1995-2006 Glyph & Cog, LLC. @@ -32,7 +32,7 @@ * Extremely simple permutations of passwords (makes first letter uppercase) - currently only useful for bruteforcing with charsets: -* Auto-save when interrupted +* Auto-save when interrupted (Ctrl-C or send SIGINT to the process) * Loading saved state - currently only for bruteforcing with charsets: @@ -43,3 +43,6 @@ Sort your wordlist by length for best performance and consider that almost all passwords in PDFs are in iso latin 1 so use the correct character encoding in your terminal and/or wordlist when using special characters. + +This tool can not decrypt a Password Protected PDF. +Look up the pdftk toolkit which can do that, when you know the password.