diff --git a/barf.py b/barf.py index c18b24d..e14e99a 100644 --- a/barf.py +++ b/barf.py @@ -20,204 +20,13 @@ # In doubt, see https://github.com/maride/barf # Have fun with the script! :) +# include project path as include path +sys.path.insert(1, "/home/maride/barf/src") -# The charset to try, sorted by the likelihood of a character class -charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_!?" - - -# Breakpoint wrapper class -# A breakpoint class with patented, award-winning score functionality. -class CounterBreakpoint(gdb.Breakpoint): - # addr is the address of the breakpoint - # isGood is a boolean, determing if the breakpoint is good-to-hit or bad-to-hit - # (means a negative breakpoint has isGood = false, and vice versa) - def __init__(self, addr, isGood): - self.isGood = isGood - self.currentScore = 0 - - # gdb requires address literals to start with a star - if addr[0] != "*": - addr = "*" + addr - - super().__init__(addr) - - # returns the score of this breakpoint - def GetScore(self): - return self.currentScore - - # resets the score to 0 - def ResetScore(self): - self.currentScore = 0 - - # returns the score and resets it to 0 afterwards - def PopScore(self): - i = self.GetScore() - self.ResetScore() - return i - - # the function called by GDB if the breakpoint is hit - def stop(self): - if self.isGood: - self.currentScore += 1 - else: - self.currentScore -= 1 - - # don't break into gdb GUI - return False - - -# Abstracts the breakpoints into a single class: -# - returns the score of the positive and negative breakpoint -# - checks if the win function was hit -# - takes care of resetting all breakpoints with a single call -class BreakpointManager: - posB = None - negB = None - winB = None - - def __init__(self, pAddr, nAddr, wAddr): - if pAddr: - self.posB = CounterBreakpoint(pAddr, True) - if nAddr: - self.negB = CounterBreakpoint(nAddr, False) - if wAddr: - self.winB = CounterBreakpoint(wAddr, True) - - def GetScore(self): - score = 0 - if self.posB: - score += self.posB.GetScore() - if self.negB: - score += self.negB.GetScore() - return score - - def ResetBreakpoints(self): - if self.posB: - self.posB.ResetScore() - if self.negB: - self.negB.ResetScore() - if self.winB: - self.winB.ResetScore() - - def PopScore(self): - score = 0 - if self.posB: - score += self.posB.PopScore() - if self.negB: - score += self.negB.PopScore() - return score - - def HitWin(self): - return self.winB.GetScore() != 0 - - -# Enables the typical GDB spam -def EnableLogging(): - gdb.execute("set logging off") - - -# Disables the typical GDB spam -def DisableLogging(): - gdb.execute("set logging file /dev/null") - gdb.execute("set logging redirect on") - gdb.execute("set logging on") - - -# Runs a given input through GDB -def TryInput(inp): - gdb.execute(f"run 2>/dev/null 1>&2 <<< $(echo '{inp}')") - - -# Prints a small MOTD, hence the name of the function -def MOTD(): - print("+--------------------------------------------+") - print("| 🥩 BARF - Breakpoint-Assisted Rough Fuzzer |") - print("| (c) 2021 Martin 'maride' Dessauer |") - print("| github.com/maride/barf |") - print("+--------------------------------------------+") - - -# bruteforces a single character, sandwiched between the known parts. -# Returns the most promising string. -def BruteforceChar(bm, knownPrefix, knownSuffix): - # keyFragment is the variable were we store our found-to-be-correct chars - keyFragment = "" - - 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() - TryInput(knownPrefix + keyFragment + "^" + knownSuffix) - refScore = bm.PopScore() - - # iterate over every character in the charset - for c in charset: - # generate full input string - inp = knownPrefix + keyFragment + c + knownSuffix - - # and try it - bm.ResetBreakpoints() - TryInput(inp) - score = bm.PopScore() - - # yay, that's a hit - if score > refScore: - keyFragment += c - found = True - break - - # check if we found something this round - return keyFragment if found else False - - -# Bruteforce calls BruteforceChar until: -# - BruteforceChar was unable to increase the score using any character in the charset, OR -# - the "win" breakpoint is hit :) -def Bruteforce(bm, knownPrefix, knownSuffix): - while True: - res = BruteforceChar(bm, knownPrefix, knownSuffix) - if res is False: - # no character from the given charset matched. :( - EnableLogging() - print("BARF is done with the charset and was unable to increase the score further. Issues may be:") - print(" - Your charset is too small") - print(" - Your chunk size is too small") - print(" - Your breakpoints are off") - print(" - The specified binary doesn't operate round-wise, so it's impossible to calculate a proper score") - if len(knownPrefix) > 0: - print(f"Anyway, I stopped with the key '{knownPrefix}[...mystery!...]{knownSuffix}'") - print("Maybe that helps you. Have a good night!") - DisableLogging() - return knownPrefix + knownSuffix - else: - # good input, we stepped further - knownPrefix += res - EnableLogging() - print(f"Found new scorer, we're now at '{knownPrefix}[...]{knownSuffix}'") - DisableLogging() - - # let's examine it further - check if we hit the win breakpoint :) - if bm.HitWin(): - EnableLogging() - print("BARF found the flag - or at least managed to hit the 'win' breakpoint!") - print(f"Winning guess for the flag is '{knownPrefix + knownSuffix}'") - DisableLogging() - return knownPrefix + knownSuffix - - -# getArguments grabs the arguments from pre-defined variables and returns it as a dict -def getArguments(): - a = dict() - a["positiveAddr"] = barf_positive_addr - a["negativeAddr"] = barf_negative_addr - a["winAddr"] = barf_win_addr - a["knownPrefix"] = barf_known_prefix - a["knownSuffix"] = barf_known_suffix - return a +# include project files +from BreakpointManager import BreakpointManager +from Helper import * +from Bruteforce import * # main func def main(): @@ -231,13 +40,24 @@ def main(): bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"]) # start the bruteforcing madness ;) - DisableLogging() + # DisableLogging() Bruteforce(bm, args["knownPrefix"], args["knownSuffix"]) # g'night, gdb gdb.execute("quit") +# getArguments grabs the arguments from pre-defined variables and returns it as a dict +def getArguments(): + a = dict() + a["positiveAddr"] = barf_positive_addr + a["negativeAddr"] = barf_negative_addr + a["winAddr"] = barf_win_addr + a["knownPrefix"] = barf_known_prefix + a["knownSuffix"] = barf_known_suffix + return a + + # actually execute main function main() diff --git a/src/BreakpointManager.py b/src/BreakpointManager.py new file mode 100644 index 0000000..8925d8b --- /dev/null +++ b/src/BreakpointManager.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +from CounterBreakpoint import CounterBreakpoint +import gdb + +# Abstracts the breakpoints into a single class: +# - returns the score of the positive and negative breakpoint +# - checks if the win function was hit +# - takes care of resetting all breakpoints with a single call +class BreakpointManager: + posB = None + negB = None + winB = None + + def __init__(self, pAddr, nAddr, wAddr): + if pAddr: + self.posB = CounterBreakpoint(pAddr, True) + if nAddr: + self.negB = CounterBreakpoint(nAddr, False) + if wAddr: + self.winB = CounterBreakpoint(wAddr, True) + + def GetScore(self): + score = 0 + if self.posB: + score += self.posB.GetScore() + if self.negB: + score += self.negB.GetScore() + return score + + def ResetBreakpoints(self): + if self.posB: + self.posB.ResetScore() + if self.negB: + self.negB.ResetScore() + if self.winB: + self.winB.ResetScore() + + def PopScore(self): + score = 0 + if self.posB: + score += self.posB.PopScore() + if self.negB: + score += self.negB.PopScore() + return score + + def HitWin(self): + return self.winB.GetScore() != 0 + diff --git a/src/Bruteforce.py b/src/Bruteforce.py new file mode 100644 index 0000000..016cb62 --- /dev/null +++ b/src/Bruteforce.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +from Helper import * + +# The charset to try, sorted by the likelihood of a character class +charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_!?" + +# bruteforces a single character, sandwiched between the known parts. +# Returns the most promising string. +def BruteforceChar(bm, knownPrefix, knownSuffix): + # keyFragment is the variable were we store our found-to-be-correct chars + keyFragment = "" + + 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() + TryInput(knownPrefix + keyFragment + "^" + knownSuffix) + refScore = bm.PopScore() + + # iterate over every character in the charset + for c in charset: + # generate full input string + inp = knownPrefix + keyFragment + c + knownSuffix + + # and try it + bm.ResetBreakpoints() + TryInput(inp) + score = bm.PopScore() + + # yay, that's a hit + if score > refScore: + keyFragment += c + found = True + break + + # check if we found something this round + return keyFragment if found else False + + +# Bruteforce calls BruteforceChar until: +# - BruteforceChar was unable to increase the score using any character in the charset, OR +# - the "win" breakpoint is hit :) +def Bruteforce(bm, knownPrefix, knownSuffix): + while True: + res = BruteforceChar(bm, knownPrefix, knownSuffix) + if res is False: + # no character from the given charset matched. :( + EnableLogging() + print("BARF is done with the charset and was unable to increase the score further. Issues may be:") + print(" - Your charset is too small") + print(" - Your chunk size is too small") + print(" - Your breakpoints are off") + print(" - The specified binary doesn't operate round-wise, so it's impossible to calculate a proper score") + if len(knownPrefix) > 0: + print(f"Anyway, I stopped with the key '{knownPrefix}[...mystery!...]{knownSuffix}'") + print("Maybe that helps you. Have a good night!") + DisableLogging() + return knownPrefix + knownSuffix + else: + # good input, we stepped further + knownPrefix += res + EnableLogging() + print(f"Found new scorer, we're now at '{knownPrefix}[...]{knownSuffix}'") + DisableLogging() + + # let's examine it further - check if we hit the win breakpoint :) + if bm.HitWin(): + EnableLogging() + print("BARF found the flag - or at least managed to hit the 'win' breakpoint!") + print(f"Winning guess for the flag is '{knownPrefix + knownSuffix}'") + DisableLogging() + return knownPrefix + knownSuffix + + diff --git a/src/CounterBreakpoint.py b/src/CounterBreakpoint.py new file mode 100644 index 0000000..bb59f3c --- /dev/null +++ b/src/CounterBreakpoint.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import gdb + +# Breakpoint wrapper class +# A breakpoint class with patented, award-winning score functionality. +class CounterBreakpoint(gdb.Breakpoint): + # addr is the address of the breakpoint + # isGood is a boolean, determing if the breakpoint is good-to-hit or bad-to-hit + # (means a negative breakpoint has isGood = false, and vice versa) + def __init__(self, addr, isGood): + self.isGood = isGood + self.currentScore = 0 + + # gdb requires address literals to start with a star + if addr[0] != "*": + addr = "*" + addr + + super().__init__(addr) + + # returns the score of this breakpoint + def GetScore(self): + return self.currentScore + + # resets the score to 0 + def ResetScore(self): + self.currentScore = 0 + + # returns the score and resets it to 0 afterwards + def PopScore(self): + i = self.GetScore() + self.ResetScore() + return i + + # the function called by GDB if the breakpoint is hit + def stop(self): + if self.isGood: + self.currentScore += 1 + else: + self.currentScore -= 1 + + # don't break into gdb GUI + return False + diff --git a/src/Helper.py b/src/Helper.py new file mode 100644 index 0000000..5c92761 --- /dev/null +++ b/src/Helper.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +import gdb + +# Enables the typical GDB spam +def EnableLogging(): + gdb.execute("set logging off") + + +# Disables the typical GDB spam +def DisableLogging(): + gdb.execute("set logging file /dev/null") + gdb.execute("set logging redirect on") + gdb.execute("set logging on") + + +# Runs a given input through GDB +def TryInput(inp): + gdb.execute(f"run 2>/dev/null 1>&2 <<< $(echo '{inp}')") + + +# Prints a small MOTD, hence the name of the function +def MOTD(): + print("+--------------------------------------------+") + print("| 🥩 BARF - Breakpoint-Assisted Rough Fuzzer |") + print("| (c) 2021 Martin 'maride' Dessauer |") + print("| github.com/maride/barf |") + print("+--------------------------------------------+") +