Split code into modules

This commit is contained in:
maride 2021-05-30 21:13:49 +02:00
parent bf068a2780
commit a4396df9ce
5 changed files with 219 additions and 198 deletions

216
barf.py
View File

@ -20,204 +20,13 @@
# In doubt, see https://github.com/maride/barf # In doubt, see https://github.com/maride/barf
# Have fun with the script! :) # 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 # include project files
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_!?" from BreakpointManager import BreakpointManager
from Helper import *
from Bruteforce import *
# 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
# main func # main func
def main(): def main():
@ -231,13 +40,24 @@ def main():
bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"]) bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"])
# start the bruteforcing madness ;) # start the bruteforcing madness ;)
DisableLogging() # DisableLogging()
Bruteforce(bm, args["knownPrefix"], args["knownSuffix"]) Bruteforce(bm, args["knownPrefix"], args["knownSuffix"])
# g'night, gdb # g'night, gdb
gdb.execute("quit") 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 # actually execute main function
main() main()

49
src/BreakpointManager.py Normal file
View File

@ -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

79
src/Bruteforce.py Normal file
View File

@ -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

44
src/CounterBreakpoint.py Normal file
View File

@ -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

29
src/Helper.py Normal file
View File

@ -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("+--------------------------------------------+")