From 6619f7b9669ad2eba976a182cb6d0102c965596f Mon Sep 17 00:00:00 2001 From: maride Date: Thu, 17 Jun 2021 02:32:26 +0200 Subject: [PATCH] Properly implement calibration --- barf.py | 2 +- src/Bruteforce.py | 48 ++++++++++++++++++++++++++++++++++---------- src/TargetManager.py | 8 +++++++- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/barf.py b/barf.py index 25ce800..ee3cde7 100644 --- a/barf.py +++ b/barf.py @@ -41,7 +41,7 @@ def main(): bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"]) # Manage the target with the TargetManager - tm = TargetManager(args["persistent"], args["startAddr"], args["endAddr"], args["buffAddr"]) + tm = TargetManager(bm, args["persistent"], args["startAddr"], args["endAddr"], args["buffAddr"]) # start the bruteforcing madness ;) Bruteforce(bm, tm, args["knownPrefix"], args["knownSuffix"], args["chunksize"]) diff --git a/src/Bruteforce.py b/src/Bruteforce.py index 030a89c..d12ee43 100644 --- a/src/Bruteforce.py +++ b/src/Bruteforce.py @@ -14,14 +14,10 @@ def BruteforceChar(bm, tm, knownPrefix, knownSuffix, chunksize): found = False - ## detect best score - # we want to get the score for "everything correct except last character". - # we do this by combining knownPrefix + keyFragment with an "impossible" character. - # the resulting score is the base for the next round of guessing, hopefully with a single solution better than the score of knownPrefix + keyFragment + impossibleChar. - # please also note that this will massively fail if the "impossible" character is part of the flag, at the very position it was tested on ... have fun detecting that - bm.ResetBreakpoints() - tm.Run(knownPrefix + keyFragment + "^" * chunksize + knownSuffix) - refScore = bm.PopScore() + # detect best score + refScore = Calibrate(bm, tm, knownPrefix + keyFragment, knownSuffix, chunksize) + if refScore is False: + return False # iterate over every character in the charset for c in generateCharset(chunksize): @@ -29,9 +25,7 @@ def BruteforceChar(bm, tm, knownPrefix, knownSuffix, chunksize): inp = knownPrefix + keyFragment + c + knownSuffix # and try it - bm.ResetBreakpoints() - tm.Run(inp) - score = bm.PopScore() + score = RunAndScore(bm, tm, inp) # yay, that's a hit if score > refScore or bm.HitWin(): @@ -79,6 +73,38 @@ def Bruteforce(bm, tm, knownPrefix, knownSuffix, chunksize): return knownPrefix + knownSuffix +# Finds out the base score when filling the binary with partly correct chars (e.g. the already found-to-be-correct prefix) +# It does this by combining knownPrefix + keyFragment and knownSuffix with an "impossible" character. +# We're only able to proceed if every character (except one) returns the same score - the "except one" score is the winner ;) +# Note that this function will massively fail if the "impossible" character is part of the flag, at the very position it was tested on ... have fun detecting that +def Calibrate(bm, tm, prefix, suffix, chunksize): + score1 = RunAndScore(bm, tm, prefix + '^' * chunksize + suffix) + score2 = RunAndScore(bm, tm, prefix + '`' * chunksize + suffix) + + if score1 == score2: + # we found a stable score, return it + return score1 + else: + # There is some kind of inconsistency in the executable, stop here. + EnableLogging() + print(score1) + print(score2) + print("BARF was unable to calibrate.") + print("While this may have multiple reasons, the most realistic are:") + print(" - The specified binary is not solvable on a round-based approach") + print(" -> reverse the binary further - is there some shuffeling mechanism in place?") + print(" - The 'no way that character is part of the flag' charset is actually part of the flag") + DisableLogging() + return False + + +# Runs the given input and returns its breakpoint score +def RunAndScore(bm, tm, inp): + bm.ResetBreakpoints() + tm.Run(inp) + return bm.PopScore() + + # generateCharset returns an iteratable object (string or set) to be used by the bruteforce function. # the chunksize is the amount of characters to stuff into an entry def generateCharset(chunksize): diff --git a/src/TargetManager.py b/src/TargetManager.py index 0130392..b1af1c4 100644 --- a/src/TargetManager.py +++ b/src/TargetManager.py @@ -13,6 +13,7 @@ from PersistenceBreakpoint import PersistenceBreakpoint # The TargetManager aims to be the one-size-fits-all solution for execution handling. # That means it is designed to have a unified interface, independent of e.g. persistent mode. class TargetManager: + breakpointManager = None usePersistent = False # vars used for persistent mode @@ -22,11 +23,13 @@ class TargetManager: checkpointIndex = 1 isRunning = False + # bm is the BreakpointManager in use # usePersistent is a boolean, determing if the experimental persistent mode should be used # startAddr is the address to start the persistent run # endAddr is the address to jump back to startAddr # buffAddr is the address of the target buffer to be written in persistent mode - def __init__(self, usePersistent, startAddr, endAddr, buffAddr): + def __init__(self, bm, usePersistent, startAddr, endAddr, buffAddr): + self.breakpointManager = bm self.usePersistent = usePersistent if usePersistent: @@ -68,6 +71,9 @@ class TargetManager: # Please note, as this may cast some confusion, that at this point the binary # had a full run-thru, is equipped with checkpoints and breakpoints and is # currently in break mode (not running, so to speak), and is at startAddr. + # But, because we likely already hit some breakpoints (depends on the executable), + # we need to reset the scores. + self.breakpointManager.ResetBreakpoints() # the executable is already running and reset # means we just need to feed input into the binary, then continue running it