Skip to content

Instantly share code, notes, and snippets.

@dhondta
Last active February 10, 2024 10:27
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save dhondta/efe84a92e4dfae3b6c14932c73ab2577 to your computer and use it in GitHub Desktop.
Save dhondta/efe84a92e4dfae3b6c14932c73ab2577 to your computer and use it in GitHub Desktop.
Tinyscript tool to bruteforce the password of a PDF

PDF password bruteforcer

This is a small tool using Tinyscript and pypdf or pikepdf to bruteforce the password of a PDF given an alphabet (defaults to printables) and a length (default is 8).

$ pip install pypdf tinyscript
$ tsm pdf-password-bruteforcer
#!/usr/bin/python3
from tinyscript import *
try:
import pikepdf
BACKEND = "pikepdf"
except ImportError:
import pypdf
BACKEND = "pypdf"
__author__ = "Alexandre D'Hondt"
__version__ = "1.4"
__copyright__ = ("A. D'Hondt", 2020)
__license__ = "gpl-3.0"
__examples__ = ["secret.pdf -p '[a-z0-9]{5}'"]
__doc__ = """
*PDF Password Bruteforcer* allows to execute a bruteforce attack on a given PDF file,
setting a regular expression pattern for the target password.
"""
BANNER_FONT = "tombstone"
BANNER_STYLE = {'fgcolor': "lolcat"}
def bruteforce_pdf_password(path, regex):
if BACKEND == "pypdf":
with open(path, 'rb') as f:
reader = pypdf.PdfReader(f)
for p in ts.bruteforce_re(regex):
logger.debug(p)
try:
reader.decrypt(p)
len(reader.pages)
logger.success("FOUND: " + p)
return True
except pypdf.errors.FileNotDecryptedError:
pass
except Exception as e:
logger.exception(e)
break
else:
for p in ts.bruteforce_re(regex):
logger.debug(p)
try:
with pikepdf.open(path, password=p) as f:
logger.success("FOUND: " + p)
return True
except pikepdf._qpdf.PasswordError:
pass
except Exception as e:
logger.exception(e)
break
return False
if __name__ == '__main__':
parser.add_argument("file", type=ts.file_exists, help="encrypted PDF file")
parser.add_argument("-p", "--pattern", default="^[0-9a-zA-Z!-_]{1,8}", help="password pattern")
initialize(add_time=True, noargs_action="demo", action_at_interrupt="confirm")
logger.info("Starting PDF password bruteforce...")
logger.handlers[-1].terminator = ""
if not bruteforce_pdf_password(args.file, args.pattern):
logger.failure("Password not found")
H4sICGaOFGIAA3Rlc3QucGRmAK2UTWgUSRTHPXmowx5EArIRSiSSBLLV1d3T0y1jdrUnk406JmbG
LxI/qrurk44zXWNNzZK4iiCCiHgQRQ+CelAPKnrwEEFQxEsOSlAPgt78APHiVd1dd2t6xt7OTlxz
sOlLv/q/V7//q3rdMZTN9eCfdNDx8tXsU4ChApkzAUEmA1BxqkIhsokgJTYG0BAZo1WoSsUw6O0F
NPQipZpM2RB4VTii1TU7vxSI8gCyWS0UECdTtWSqzUJBQ1GFerQDKo7Xyg5MNT/iSgAN0yqrcVey
RHlDnLkFKqDRUG4fdCaoKxprA2UFpr8Ay1dW4HKPpglkc1ZZxybhiCK/McQSOU+9gMyNJYD1JPBG
Go6Jcaim64qq4JSUweW1EzsubOleu/jo51s/w/yaw5XJk+1nNp94fhvVizRUMK5nNOuNIHkMEBXp
pIASWtq0d8ai9NwulRgvVIhLodkwsYmUaT1JAahQc0TUqKgEQL/SYGy83nOUC0qCcrlPrkQEzVKX
eTQ+oGbLANoWeNKQlBfypLoXWo0N1gWiOkS5zcoVFta7Z8berYT118f2/9hzr/hDfj6jZtMDytLf
Apf2czIVr6X+3983TcTUCwTd3b34lydL3x3fNR+o9RWYJHiz5XnyPVu+QHh+d++SlXD9/SPzwWMl
ST8IO2+O+v6BmQH983Lv7Ko7E8Pnz13paxsYDT/92f/Hg33jL95Xu+RMwB7NUnQ5WFCLsVFBkNAj
3ANoC+wcdaYfXr0+c2rrQ2X5m0dt9uNF/3lkma1QjYmxaibHBs/5reSZl5UdgZ1BZvXrfQcPnZl9
69p/s7ZDXfWJpEQELPyqQE67V3MlYGcfP3BJqe05fnpabb+m5VZ8zM4u+/DBkJpiIEoy++nHUeei
3T347K8bhd+7EjyTnPpADrcKlPiBRiqlpaAP4xiW9zJaCf+NGUZLDKutOk1RW2KGrrXqzFadjq3W
3HlY0qbeErPSLXvgOXyCk6BEOWj8HEOfweho5JT1hS6fqsiLqzS+B7JwJOOmTFf3PExV00o7xE87
HtYdxzB9zXIc0+rNYJ9SVfFVzTStFHFNz6CKmtIlsTSMidUrb/wwY7Juo2wh2E/rjY8uNOEiOgqs
Gibo6OgbzIF/AMArx2CPBgAA
@Paulemeister
Copy link

Paulemeister commented Feb 21, 2022

Either I have brain-rot or the patterns don't work. Even testing a pdf with password "AB" and using the pattern "^[A-Z]{2}" doesn't work. Also the verbose output is very inconsistent. Sometimes the tried passwords are on new lines, sometimes it gets replaced on the same line. Thanks for the work though. Plus the help only shows with --help and not -h. and the help has the old alphabet/length stuff in it, which was removed

@dhondta
Copy link
Author

dhondta commented Feb 22, 2022

Hi @Paulemeister !
Thank you for reporting this.
I have made some changes to the bruteforce_re function recently (when I figured out itertools.product2 caused an issue with concatenated ranges, e.g. [a-bA-B] ; this caused to generate the combinations of [a-b] and [A-B]).
Can you tell me if you use one of the latest versions of Tinyscript (>=1.24.13), please ?

Edit: I tested your use case ; it works as expected.

  • The debug message displays on the same line, because of the line logger.handlers[-1].terminator = "".
  • The solution is displayed above the last debug message (because of the aforementioned line).
$ pdf-password-bruteforce -v test2.pdf -p "[A-Z]{2}"
[...]
12:34:56 [INFO] Starting PDF password bruteforce...
12:34:56 [SUCCESS] FOUND: AB

@Paulemeister
Copy link

Btw, I am on Linux, but that shouldn't really matter with python. My tinyscript version is 1.24.14. Thanks for the quick response

@Paulemeister
Copy link

I narrowed it down to PyPDF2. It isn't maintained since 2018. I went step by step with the python shell and got this error:
NotImplementedError: only algorithm code 1 and 2 are supported So maybe i just gotta use pikepdf

@dhondta
Copy link
Author

dhondta commented Feb 23, 2022

@Paulemeister
You can try with version 1.3 ; it now supports pikepdf as the backend for handling the input PDF.

@iulian0512
Copy link

tinyscript can't be installed on macos, win10 and ubuntu due to old pathlib dependency, see dhondta/python-tinyscript#24

@christaus
Copy link

Works fine on linux mint, thank you, now perhaps I'll get a result :D

@caiquediass
Copy link

i can't install , any help? i tried to install pip install PyPDF2 tinyscript
and i open gitbassh aswell and i tried

Caique@DESKTOP-BIOQSQO MINGW64 ~
$ pip install PyPDF2 tinyscript
Requirement already satisfied: PyPDF2 in c:\users\caique\appdata\local\programs\python\python39\lib\site-packages (2.11.2)
Collecting tinyscript
Using cached tinyscript-1.26.13.tar.gz (131 kB)
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error

× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [12 lines of output]
Traceback (most recent call last):
File "", line 2, in
File "", line 14, in
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools_init_.py", line 12, in

from setuptools.extension import Extension
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools\extension.py", line 7, in

from setuptools.dist import _get_unpatched
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools\dist.py", line 16, in
import pkg_resources
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\pkg_resources.py", line 1479, in
register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
AttributeError: module 'importlib._bootstrap' has no attribute 'SourceFileLoader'
[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

Caique@DESKTOP-BIOQSQO MINGW64 ~
$

@caiquediass
Copy link

inn pip insstall

S C:\Users\Caique> pip install PyPDF2 tinyscript
Requirement already satisfied: PyPDF2 in c:\users\caique\appdata\local\programs\python\python39\lib\site-packages (2.11.2)
Collecting tinyscript
Using cached tinyscript-1.26.13.tar.gz (131 kB)
Preparing metadata (setup.py) ... error
error: subprocess-exited-with-error

× python setup.py egg_info did not run successfully.
│ exit code: 1
╰─> [12 lines of output]
Traceback (most recent call last):
File "", line 2, in
File "", line 14, in
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools_init_.py", line 12, in

from setuptools.extension import Extension
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools\extension.py", line 7, in

from setuptools.dist import _get_unpatched
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\setuptools\dist.py", line 16, in
import pkg_resources
File "c:\users\caique\appdata\local\programs\python\python39\lib\site-packages\pkg_resources.py", line 1479, in
register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
AttributeError: module 'importlib._bootstrap' has no attribute 'SourceFileLoader'
[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.
PS C:\Users\Caique>

@dhondta
Copy link
Author

dhondta commented Dec 6, 2022

Hi @caiquediass
Did you pip install --upgrade setuptools ?

@Enelar
Copy link

Enelar commented Feb 12, 2023

Yeah, just wasted time, only algorithm code 1 and 2 are supported. This PDF uses code 4
Who would hook every exception without even trying to filter their severity.

@dhondta
Copy link
Author

dhondta commented Feb 16, 2023

@enela This should be fixed with using pypdf and the pypdf.errors.FileNotDecryptedError exception.

@tlansec
Copy link

tlansec commented Jul 7, 2023

As a warning for other users tinyscript has an incredible number of dependencies so you may want to use a dedicated environment when installing it.

@bcjarrett
Copy link

bcjarrett commented Nov 20, 2023

Here's a little dockerfile if anyone wants it. Just need to put the script in the same folder as this Dockerfile

# Build
FROM centos:7.9.2009

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

ENV APP_HOME=/app
WORKDIR $APP_HOME

# install system dependencies
RUN yum update -y && \
    yum install -y gcc python3 python3-pip python3-devel
RUN pip3 install --upgrade pip

# Install reqs and copy over files from context
COPY . $APP_HOME
RUN chmod +x $APP_HOME/*
RUN pip3 install pypdf tinyscript
docker build -t pdftool .
docker run --rm -it pdftool /bin/bash
>> python3 pdf-password-bruteforcer.py <filename>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment