Properly implement calibration

This commit is contained in:
maride 2021-06-17 02:32:26 +02:00
parent 50aa2e7277
commit 6619f7b966
3 changed files with 45 additions and 13 deletions

View File

@ -41,7 +41,7 @@ def main():
bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"]) bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"])
# Manage the target with the TargetManager # 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 ;) # start the bruteforcing madness ;)
Bruteforce(bm, tm, args["knownPrefix"], args["knownSuffix"], args["chunksize"]) Bruteforce(bm, tm, args["knownPrefix"], args["knownSuffix"], args["chunksize"])

View File

@ -14,14 +14,10 @@ def BruteforceChar(bm, tm, knownPrefix, knownSuffix, chunksize):
found = False found = False
## detect best score # detect best score
# we want to get the score for "everything correct except last character". refScore = Calibrate(bm, tm, knownPrefix + keyFragment, knownSuffix, chunksize)
# we do this by combining knownPrefix + keyFragment with an "impossible" character. if refScore is False:
# 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. return False
# 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()
# iterate over every character in the charset # iterate over every character in the charset
for c in generateCharset(chunksize): for c in generateCharset(chunksize):
@ -29,9 +25,7 @@ def BruteforceChar(bm, tm, knownPrefix, knownSuffix, chunksize):
inp = knownPrefix + keyFragment + c + knownSuffix inp = knownPrefix + keyFragment + c + knownSuffix
# and try it # and try it
bm.ResetBreakpoints() score = RunAndScore(bm, tm, inp)
tm.Run(inp)
score = bm.PopScore()
# yay, that's a hit # yay, that's a hit
if score > refScore or bm.HitWin(): if score > refScore or bm.HitWin():
@ -79,6 +73,38 @@ def Bruteforce(bm, tm, knownPrefix, knownSuffix, chunksize):
return knownPrefix + knownSuffix 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. # 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 # the chunksize is the amount of characters to stuff into an entry
def generateCharset(chunksize): def generateCharset(chunksize):

View File

@ -13,6 +13,7 @@ from PersistenceBreakpoint import PersistenceBreakpoint
# The TargetManager aims to be the one-size-fits-all solution for execution handling. # 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. # That means it is designed to have a unified interface, independent of e.g. persistent mode.
class TargetManager: class TargetManager:
breakpointManager = None
usePersistent = False usePersistent = False
# vars used for persistent mode # vars used for persistent mode
@ -22,11 +23,13 @@ class TargetManager:
checkpointIndex = 1 checkpointIndex = 1
isRunning = False isRunning = False
# bm is the BreakpointManager in use
# usePersistent is a boolean, determing if the experimental persistent mode should be used # usePersistent is a boolean, determing if the experimental persistent mode should be used
# startAddr is the address to start the persistent run # startAddr is the address to start the persistent run
# endAddr is the address to jump back to startAddr # endAddr is the address to jump back to startAddr
# buffAddr is the address of the target buffer to be written in persistent mode # 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 self.usePersistent = usePersistent
if usePersistent: if usePersistent:
@ -68,6 +71,9 @@ class TargetManager:
# Please note, as this may cast some confusion, that at this point the binary # 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 # 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. # 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 # the executable is already running and reset
# means we just need to feed input into the binary, then continue running it # means we just need to feed input into the binary, then continue running it