├── .gitignore ├── LICENSE ├── README.md └── fuzzpoints.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ryan Sullivan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fuzzpoints 2 | 3 | ### Description 4 | Fuzzpoints extend upon gdb's breakpoint functionality to provide security researchers another way to randomly modify memory in search of vulnerabilities. A new command, called fuzz, is added to gdb which allows researchers to set a trigger and target location to guide memory modification. The fuzz command takes five parameters: trigger address, target address, target size, fuzz factor, and seed. 5 | 6 | ### Fuzz Command Options/Usage 7 | Usage: fuzz \[trigger address\] \[target address\] \[target size\] \[fuzz factor\] \[seed\] 8 | 9 | - trigger address = any address or symbol similar to the ```b``` command in gdb 10 | - target address = any address or symbol similar to the ```b``` command in gdb 11 | - target size = any numerical value or expression such as ```sizeof``` in terms of bytes 12 | - fuzz factor = a floating point value ranging from 0 to 1 13 | - seed = any numerical value (will be processed by ```gdb.parse_and_eval()```) 14 | 15 | Examples: 16 | 17 | ```fuzz *0x1337 *0xbaddecaf 256 0.5 800``` 18 | 19 | ```fuzz *main+20 SOMEVAR sizeof(SOMEVAR) 0.0001 0``` 20 | 21 | ### Install Instructions 22 | Including the following line in your .gdbinit file 23 | ```bash 24 | source ~/fuzzpoints/fuzzpoints.py 25 | ``` 26 | 27 | ### Example 28 | Lets walk through a trivial example to demonstrate usage. First lets take a very simple application to target. 29 | ```C 30 | #include 31 | 32 | int main(int argc, char* argv[]) 33 | { 34 | char INPUT[] = "i will not forget to check the bounds of user input"; 35 | const unsigned int loops = 10; 36 | unsigned int i = 0; 37 | for(;i 62 | This is free software: you are free to change and redistribute it. 63 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" 64 | and "show warranty" for details. 65 | This GDB was configured as "x86_64-apple-darwin15.2.0". 66 | Type "show configuration" for configuration details. 67 | For bug reporting instructions, please see: 68 | . 69 | Find the GDB manual and other documentation resources online at: 70 | . 71 | For help, type "help". 72 | Type "apropos word" to search for commands related to "word"... 73 | Reading symbols from ./a.out...Reading symbols from /Users/ryansullivan/a.out.dSYM/Contents/Resources/DWARF/a.out...done. 74 | done. 75 | (gdb) disassemble main 76 | Dump of assembler code for function main: 77 | 0x0000000100000e70 <+0>: push %rbp 78 | 0x0000000100000e71 <+1>: mov %rsp,%rbp 79 | 0x0000000100000e74 <+4>: sub $0x60,%rsp 80 | 0x0000000100000e78 <+8>: lea 0xe1(%rip),%rax # 0x100000f60 81 | 0x0000000100000e7f <+15>: mov $0x34,%ecx 82 | 0x0000000100000e84 <+20>: mov %ecx,%edx 83 | 0x0000000100000e86 <+22>: lea -0x40(%rbp),%r8 84 | 0x0000000100000e8a <+26>: mov 0x17f(%rip),%r9 # 0x100001010 85 | 0x0000000100000e91 <+33>: mov (%r9),%r9 86 | 0x0000000100000e94 <+36>: mov %r9,-0x8(%rbp) 87 | 0x0000000100000e98 <+40>: movl $0x0,-0x44(%rbp) 88 | 0x0000000100000e9f <+47>: mov %edi,-0x48(%rbp) 89 | 0x0000000100000ea2 <+50>: mov %rsi,-0x50(%rbp) 90 | 0x0000000100000ea6 <+54>: mov %r8,%rdi 91 | 0x0000000100000ea9 <+57>: mov %rax,%rsi 92 | 0x0000000100000eac <+60>: callq 0x100000f18 93 | 0x0000000100000eb1 <+65>: movl $0xa,-0x54(%rbp) 94 | 0x0000000100000eb8 <+72>: movl $0x0,-0x58(%rbp) 95 | 0x0000000100000ebf <+79>: cmpl $0xa,-0x58(%rbp) 96 | 0x0000000100000ec6 <+86>: jae 0x100000ef1 97 | 0x0000000100000ecc <+92>: lea 0xc1(%rip),%rdi # 0x100000f94 98 | 0x0000000100000ed3 <+99>: lea -0x40(%rbp),%rsi 99 | 0x0000000100000ed7 <+103>: mov $0x0,%al 100 | 0x0000000100000ed9 <+105>: callq 0x100000f1e 101 | 0x0000000100000ede <+110>: mov %eax,-0x5c(%rbp) 102 | 0x0000000100000ee1 <+113>: mov -0x58(%rbp),%eax 103 | 0x0000000100000ee4 <+116>: add $0x1,%eax 104 | 0x0000000100000ee9 <+121>: mov %eax,-0x58(%rbp) 105 | 0x0000000100000eec <+124>: jmpq 0x100000ebf 106 | 0x0000000100000ef1 <+129>: mov 0x118(%rip),%rax # 0x100001010 107 | 0x0000000100000ef8 <+136>: mov (%rax),%rax 108 | 0x0000000100000efb <+139>: cmp -0x8(%rbp),%rax 109 | 0x0000000100000eff <+143>: jne 0x100000f0d 110 | 0x0000000100000f05 <+149>: xor %eax,%eax 111 | 0x0000000100000f07 <+151>: add $0x60,%rsp 112 | 0x0000000100000f0b <+155>: pop %rbp 113 | 0x0000000100000f0c <+156>: retq 114 | 0x0000000100000f0d <+157>: callq 0x100000f12 115 | End of assembler dump. 116 | (gdb) 117 | 118 | ``` 119 | We can see the ```printf``` call is happening at location main+105. This would be a great place to put our fuzzpoint's trigger address. Now we need to determine what to set for our target address. We can do this a number of ways. For binaries with debug symbols present, we can utilize gdb and use the variable name from source code; in this case it's ```INPUT```. If these symbols aren't present, however, you can supply an address instead. To determine the size of our buffer, we can again utilize gdb's support for the sizeof function. In this example we only want to flip 1% of the bits in our buffer. The seed is provided in order to reproduce the results. The example command that we want to run for our scenario is shown below. 120 | ``` 121 | (gdb) fuzz *main+105 INPUT sizeof(INPUT) 0.01 500 122 | Breakpoint 1 at 0x100000f52: file test.c, line 10. 123 | ``` 124 | Now we just run the program to get the following output: 125 | ``` 126 | (gdb) run 127 | Starting program: /Users/ryansullivan/a.out 128 | I will noforget to checj the bounds of user inpuv 129 | I will nmforget to checj thm bounds of e3er inpuv 130 | A will nmforget to checj thm bound3`of e3er ijpuv 131 | A will nmforget0to 132 | C will nm? fobget0to 133 | B will nm? fobcet0to 134 | B will nm? fobcet0po 135 | B will nm? Fobcet0po 136 | B will nm? Fobcet0po 137 | B will nm? FObcet0po 138 | [Inferior 1 (process 10108) exited normally] 139 | ``` 140 | Notice how the program on each iteration is modifying the value stored at ```INPUT```. 141 | -------------------------------------------------------------------------------- /fuzzpoints.py: -------------------------------------------------------------------------------- 1 | import gdb, math, random, struct 2 | 3 | 4 | class fuzzpoint (gdb.Breakpoint): 5 | 6 | def __init__(self, trigger, target, size, factor): 7 | self.target = target 8 | self.size = size 9 | self.factor = factor 10 | super(fuzzpoint,self).__init__(trigger) 11 | 12 | def stop(self): 13 | buf_size = gdb.parse_and_eval(self.size) 14 | 15 | mod_count = int(math.floor(buf_size * 8 * gdb.parse_and_eval(self.factor))) 16 | if mod_count < 1: mod_count = 1 17 | 18 | for i in range(0, mod_count): 19 | offset = random.randint(0,buf_size-1) 20 | rand_byte = gdb.parse_and_eval('%s + %d' % (self.target, offset)) 21 | buf = gdb.selected_inferior().read_memory(rand_byte, 1) 22 | orig = struct.unpack('B', buf[0])[0] 23 | rand_bit = random.randint(0,7) 24 | update = (orig ^ (1 << rand_bit)) 25 | gdb.selected_inferior().write_memory(rand_byte, chr(update), 1) 26 | return False 27 | 28 | 29 | class fuzz (gdb.Command): 30 | """Create a fuzzpoint with a given trigger, target, size, factor, and seed""" 31 | 32 | def __init__ (self): 33 | super (fuzz, self).__init__ ("fuzz", gdb.COMMAND_USER) 34 | 35 | def invoke (self, arg, from_tty): 36 | argv = gdb.string_to_argv(arg) 37 | if len(argv) < 5: 38 | print("Error: requires arguments [trigger] [target] [size] [factor] [seed]") 39 | return 40 | 41 | seed = gdb.parse_and_eval(argv[4]) 42 | random.seed(seed) 43 | fuzzpoint(argv[0], argv[1], argv[2], argv[3]) 44 | 45 | fuzz() 46 | --------------------------------------------------------------------------------