mirror of
				https://github.com/maride/barf.git
				synced 2025-10-10 19:06:51 +00:00 
			
		
		
		
	Init commit
This commit is contained in:
		
						commit
						bf068a2780
					
				
							
								
								
									
										243
									
								
								barf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								barf.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,243 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # | ||||||
|  | # (c) 2021 Martin "maride" Dessauer | ||||||
|  | # | ||||||
|  | # BARF, or the Breakpoint-Assisted Rough Fuzzer, is a tool to do intelligent bruteforcing. | ||||||
|  | # The "intelligent" part comes from watching breakpoints and counting how often they were hit. | ||||||
|  | # Input is fed into the target program, character-wise, and the character with the best score wins. ;) | ||||||
|  | # This is done as long as there is a better score to get, and/or until a "win breakpoint" is hit. | ||||||
|  | # If that's hard to understand on the first read, see some of the examples. ;) | ||||||
|  | # | ||||||
|  | # This script is not designed to be directly called. Instead, it gets imported by gdb, via the -x argument. | ||||||
|  | # Because passing arguments into gdb-python scripts is not trivial, the script _should_ be called by the barf.sh wrapper. | ||||||
|  | # If you have any reasons to avoid the wrapper script, ... uh well. Your choice. You can call the barf.py script via gdb like this: | ||||||
|  | # gdb -nx -ex "py barf_positive_addr=False;barf_negative_addr='0x5555555551c0';barf_win_addr='0x5555555551ec';barf_known_prefix='';barf_known_suffix=''" -x barf.py ./beispiel1 | ||||||
|  | #  -nx avoids loading .gdbinit | ||||||
|  | #  -ex throws your arguments into gdb-python (must be specified _before_ handing in the script | ||||||
|  | #  -x specifies the location of the script | ||||||
|  | #  after that comes your executable (./beispiel1 in this case) | ||||||
|  | # | ||||||
|  | # In doubt, see https://github.com/maride/barf | ||||||
|  | # Have fun with the script! :) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # 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 | ||||||
|  | 
 | ||||||
|  | # main func | ||||||
|  | def main(): | ||||||
|  |     MOTD() | ||||||
|  |     gdb.execute("set pagination off") | ||||||
|  | 
 | ||||||
|  |     # check our args :) | ||||||
|  |     args = getArguments() | ||||||
|  | 
 | ||||||
|  |     # Create our breakpoints, managed by the BreakpointManager | ||||||
|  |     bm = BreakpointManager(args["positiveAddr"], args["negativeAddr"], args["winAddr"]) | ||||||
|  | 
 | ||||||
|  |     # start the bruteforcing madness ;) | ||||||
|  |     DisableLogging() | ||||||
|  |     Bruteforce(bm, args["knownPrefix"], args["knownSuffix"]) | ||||||
|  | 
 | ||||||
|  |     # g'night, gdb | ||||||
|  |     gdb.execute("quit") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # actually execute main function | ||||||
|  | main() | ||||||
|  | 
 | ||||||
							
								
								
									
										80
									
								
								barf.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										80
									
								
								barf.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,80 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | # | ||||||
|  | # wrapper script around barf.py | ||||||
|  | # | ||||||
|  | # In doubt, see https://github.com/maride/barf | ||||||
|  | # Have fun with the script! :) | ||||||
|  | 
 | ||||||
|  | # setting defaults for the arguments | ||||||
|  | POSITIVEADDR="" | ||||||
|  | NEGATIVEADDR="" | ||||||
|  | WINADDR="" | ||||||
|  | KNOWNPREFIX="" | ||||||
|  | KNOWNSUFFIX="" | ||||||
|  | 
 | ||||||
|  | # getopt is kind-of unstable across distributions and versions, so we implement it on our own | ||||||
|  | # hat-tip to https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash | ||||||
|  | while [[ $# -gt 0 ]]; do | ||||||
|  | 	key="$1" | ||||||
|  | 
 | ||||||
|  | 	case $key in | ||||||
|  | 		-p|--positive-addr) | ||||||
|  | 		POSITIVEADDR="$2" | ||||||
|  | 		shift; shift | ||||||
|  | 		;; | ||||||
|  | 		-n|--negative-addr) | ||||||
|  | 		NEGATIVEADDR="$2" | ||||||
|  | 		shift; shift | ||||||
|  | 		;; | ||||||
|  | 		-w|--win-addr) | ||||||
|  | 		WINADDR="$2" | ||||||
|  | 		shift; shift | ||||||
|  | 		;; | ||||||
|  | 		-h|--help) | ||||||
|  | 		SHOWHELP=1 | ||||||
|  | 		shift | ||||||
|  | 		;; | ||||||
|  | 		-b|--prefix) | ||||||
|  | 		KNOWNPREFIX="$2" | ||||||
|  | 		shift; shift | ||||||
|  | 		;; | ||||||
|  | 		-a|--suffix) | ||||||
|  | 		KNOWNSUFFIX="$2" | ||||||
|  | 		shift; shift | ||||||
|  | 		;; | ||||||
|  | 		*) # unknown option - we assume it is the target literal | ||||||
|  | 		TARGETFILE="$key" | ||||||
|  | 		shift | ||||||
|  | 		;; | ||||||
|  | 	esac | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | # check if we have all arguments we need | ||||||
|  | if [ "$POSITIVEADDR" == "" ] && [ "$NEGATIVEADDR" == "" ] || [ "$TARGETFILE" == "" ] ; then | ||||||
|  | 	# nope, missing some args | ||||||
|  | 	SHOWHELP=1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # check if the arguments are valid | ||||||
|  | if [ ! -e "$TARGETFILE" ]; then | ||||||
|  | 	echo "The file $TARGETFILE does not exist." | ||||||
|  | 	exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # see if the user needs our help | ||||||
|  | if [ "$SHOWHELP" == 1 ]; then | ||||||
|  | 	echo "Usage: ./barf.sh" | ||||||
|  | 	echo "		-p | --positive-addr 0x123456	a location to be counted as good hit" | ||||||
|  | 	echo "		-n | --negative-addr 0x789ABC	a location to be counted as bad hit" | ||||||
|  | 	echo "		-w | --win-addr      0xDEF042	a location reached if your input is correct" | ||||||
|  | 	echo "		-< | --prefix        CTF{	a known prefix, e.g. the prefix of your flag" | ||||||
|  | 	echo "		-> | --suffix        }		a known suffix, e.g. the suffix of your flag" | ||||||
|  | 	echo "		-h | --help			a great and useful help message, you should try it!" | ||||||
|  | 	echo "		./path/to/your/crackme		the path to the target to be fuzzed" | ||||||
|  | 	echo "Note that you need to either specify --positive-addr or --negative-addr and your target of course." | ||||||
|  | 	exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # ready for take-off | ||||||
|  | gdb --quiet -nx --eval-command "py barf_positive_addr='$POSITIVEADDR';barf_negative_addr='$NEGATIVEADDR';barf_win_addr='$WINADDR';barf_known_prefix='$KNOWNPREFIX';barf_known_suffix='$KNOWNSUFFIX'" --command barf.py $TARGETFILE | ||||||
|  | 
 | ||||||
							
								
								
									
										
											BIN
										
									
								
								examples/single-char
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/single-char
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										54
									
								
								examples/single-char.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								examples/single-char.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | // single-char.c
 | ||||||
|  | // -------------
 | ||||||
|  | //
 | ||||||
|  | // The binary reads some chars from stdin and checks it against a hard-coded flag.
 | ||||||
|  | // If the entered flag is correct, a corresponding message will be printed out.
 | ||||||
|  | //
 | ||||||
|  | // Compile with
 | ||||||
|  | //  gcc -o single-char single-char.c
 | ||||||
|  | //
 | ||||||
|  | // Quick binary analysis
 | ||||||
|  | //  - load into gdb
 | ||||||
|  | //  - start, so the binary is mapped to the final position
 | ||||||
|  | //  - execute "disas main"
 | ||||||
|  | // Look at 0x00005555555551c7 <+130>. It moves 0 to rbp-0x4, that's the foundFlag = 0 below.
 | ||||||
|  | // This is the perfect address for --negative-addr
 | ||||||
|  | // Finding the win function is even easier. We just need to search for the point where puts("yay, ...") is called.
 | ||||||
|  | // And that is at 0x00005555555551ec. It is not important if you choose the instruction moving the string into
 | ||||||
|  | // memory, or the instruction calling puts(), as long as it is inside the correct part of the if() block ;)
 | ||||||
|  | //
 | ||||||
|  | // With the addresses identified above, we call barf with:
 | ||||||
|  | //  ./barf.sh --negative-addr 0x5555555551c7 --win-addr 0x5555555551ec ./single-char
 | ||||||
|  | // 
 | ||||||
|  | // Please note that your addresses will likely differ, e.g. if you edit the source file below.
 | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #define BUFSIZE 32 | ||||||
|  | 
 | ||||||
|  | int main(int argc ,char* argv[]) { | ||||||
|  | 	char buf[BUFSIZE]; | ||||||
|  | 	char flag[BUFSIZE] = "CTF{F00_b4R_B4z_fL4g!}\n"; | ||||||
|  | 	int foundFlag = 1; | ||||||
|  | 
 | ||||||
|  | 	// read flag
 | ||||||
|  | 	fgets(buf, BUFSIZE, stdin); | ||||||
|  | 
 | ||||||
|  | 	// walk flag
 | ||||||
|  | 	int i = 0; | ||||||
|  | 	while(buf[i] != '\0' && i < BUFSIZE) { | ||||||
|  | 		if(buf[i] != flag[i]) { | ||||||
|  | 			foundFlag = 0; | ||||||
|  | 		} | ||||||
|  | 		i++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// check flag
 | ||||||
|  | 	if(foundFlag) { | ||||||
|  | 		puts("yay, that's the flag! :)"); | ||||||
|  | 	} else { | ||||||
|  | 		puts("nay, that's not the flag! :("); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user