Land #3600 - GPP Junk Padding Fix
commit
7044dabea1
|
@ -129,14 +129,40 @@ class GPP
|
||||||
# Decrypts passwords using Microsoft's published key:
|
# Decrypts passwords using Microsoft's published key:
|
||||||
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
|
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
|
||||||
def self.decrypt(encrypted_data)
|
def self.decrypt(encrypted_data)
|
||||||
unless encrypted_data
|
password = ""
|
||||||
return ""
|
return password unless encrypted_data
|
||||||
end
|
|
||||||
|
|
||||||
password = ""
|
password = ""
|
||||||
padding = "=" * (4 - (encrypted_data.length % 4))
|
retries = 0
|
||||||
epassword = "#{encrypted_data}#{padding}"
|
original_data = encrypted_data.dup
|
||||||
decoded = Rex::Text.decode_base64(epassword)
|
|
||||||
|
begin
|
||||||
|
mod = encrypted_data.length % 4
|
||||||
|
|
||||||
|
# PowerSploit code strips the last character, unsure why...
|
||||||
|
case mod
|
||||||
|
when 1
|
||||||
|
encrypted_data = encrypted_data[0..-2]
|
||||||
|
when 2, 3
|
||||||
|
padding = '=' * (4 - mod)
|
||||||
|
encrypted_data = "#{encrypted_data}#{padding}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Strict base64 decoding used here
|
||||||
|
decoded = encrypted_data.unpack('m0').first
|
||||||
|
rescue ::ArgumentError => e
|
||||||
|
# Appears to be some junk UTF-8 Padding appended at times in
|
||||||
|
# Win2k8 (not in Win2k8R2)
|
||||||
|
# Lets try stripping junk and see if we can decrypt
|
||||||
|
if retries < 8
|
||||||
|
retries += 1
|
||||||
|
original_data = original_data[0..-2]
|
||||||
|
encrypted_data = original_data
|
||||||
|
retry
|
||||||
|
else
|
||||||
|
return password
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
|
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
|
||||||
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# encoding: binary
|
||||||
require 'rex/parser/group_policy_preferences'
|
require 'rex/parser/group_policy_preferences'
|
||||||
|
|
||||||
xml_group = '
|
xml_group = '
|
||||||
|
@ -76,75 +77,89 @@ xml_ms = '
|
||||||
</Groups>
|
</Groups>
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# Win2k8 appears to append some junk padding in some cases
|
||||||
|
cpassword_win2k8 = []
|
||||||
|
# Win2k8R2 - EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wEMON8tIIslS6707RU1F7Bh
|
||||||
|
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wEMON8tIIslS6707RU1F7BhTµkp', 'N3v3rGunnaG!veYo']
|
||||||
|
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wGSwOI7Be//GJdxd5YYXUQHTµkp', 'N3v3rGunnaG!veYou']
|
||||||
|
# Win2k8R2 - EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wFSuDccBEp/4l5EuKnwF0WS
|
||||||
|
cpassword_win2k8 << ['EqWFlA4kn2T6PHvGi09M7seHuqCYK/slkJWIl7mK+wFSuDccBEp/4l5EuKnwF0WS»YÂVAA', 'N3v3rGunnaG!veYouUp']
|
||||||
cpassword_normal = "j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw"
|
cpassword_normal = "j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw"
|
||||||
cpassword_bad = "blah"
|
cpassword_bad = "blah"
|
||||||
|
|
||||||
describe Rex::Parser::GPP do
|
describe Rex::Parser::GPP do
|
||||||
GPP = Rex::Parser::GPP
|
GPP = Rex::Parser::GPP
|
||||||
|
|
||||||
##
|
##
|
||||||
# Decrypt
|
# Decrypt
|
||||||
##
|
##
|
||||||
it "Decrypt returns Local*P4ssword! for normal cpassword" do
|
it "Decrypt returns Local*P4ssword! for normal cpassword" do
|
||||||
result = GPP.decrypt(cpassword_normal)
|
result = GPP.decrypt(cpassword_normal)
|
||||||
result.should eq("Local*P4ssword!")
|
result.should eq("Local*P4ssword!")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Decrypt returns blank for bad cpassword" do
|
it "Decrypt returns blank for bad cpassword" do
|
||||||
result = GPP.decrypt(cpassword_bad)
|
result = GPP.decrypt(cpassword_bad)
|
||||||
result.should eq("")
|
result.should eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "Decrypt returns blank for nil cpassword" do
|
it "Decrypt returns blank for nil cpassword" do
|
||||||
result = GPP.decrypt(nil)
|
result = GPP.decrypt(nil)
|
||||||
result.should eq("")
|
result.should eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
it 'Decrypts a cpassword containing junk padding' do
|
||||||
# Parse
|
cpassword_win2k8.each do |encrypted, expected|
|
||||||
##
|
result = GPP.decrypt(encrypted)
|
||||||
|
result.should eq(expected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "Parse returns empty [] for nil" do
|
##
|
||||||
GPP.parse(nil).should be_empty
|
# Parse
|
||||||
end
|
##
|
||||||
|
|
||||||
it "Parse returns results for xml_ms and password is empty" do
|
it "Parse returns empty [] for nil" do
|
||||||
results = GPP.parse(xml_ms)
|
GPP.parse(nil).should be_empty
|
||||||
results.should_not be_empty
|
end
|
||||||
results[0][:PASS].should be_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
it "Parse returns results for xml_datasrc, and attributes, and password is test1" do
|
it "Parse returns results for xml_ms and password is empty" do
|
||||||
results = GPP.parse(xml_datasrc)
|
results = GPP.parse(xml_ms)
|
||||||
results.should_not be_empty
|
results.should_not be_empty
|
||||||
results[0].include?(:ATTRIBUTES).should be_true
|
results[0][:PASS].should be_empty
|
||||||
results[0][:ATTRIBUTES].should_not be_empty
|
end
|
||||||
results[0][:PASS].should eq("test")
|
|
||||||
end
|
|
||||||
|
|
||||||
xmls = []
|
it "Parse returns results for xml_datasrc, and attributes, and password is test1" do
|
||||||
xmls << xml_group
|
results = GPP.parse(xml_datasrc)
|
||||||
xmls << xml_drive
|
results.should_not be_empty
|
||||||
xmls << xml_schd
|
results[0].include?(:ATTRIBUTES).should be_true
|
||||||
xmls << xml_serv
|
results[0][:ATTRIBUTES].should_not be_empty
|
||||||
xmls << xml_datasrc
|
results[0][:PASS].should eq("test")
|
||||||
|
end
|
||||||
|
|
||||||
it "Parse returns results for all good xmls and passwords" do
|
xmls = []
|
||||||
xmls.each do |xml|
|
xmls << xml_group
|
||||||
results = GPP.parse(xml)
|
xmls << xml_drive
|
||||||
results.should_not be_empty
|
xmls << xml_schd
|
||||||
results[0][:PASS].should_not be_empty
|
xmls << xml_serv
|
||||||
end
|
xmls << xml_datasrc
|
||||||
end
|
|
||||||
|
|
||||||
##
|
it "Parse returns results for all good xmls and passwords" do
|
||||||
# Create_Tables
|
xmls.each do |xml|
|
||||||
##
|
results = GPP.parse(xml)
|
||||||
it "Create_tables returns tables for all good xmls" do
|
results.should_not be_empty
|
||||||
xmls.each do |xml|
|
results[0][:PASS].should_not be_empty
|
||||||
results = GPP.parse(xml)
|
end
|
||||||
tables = GPP.create_tables(results, "test")
|
end
|
||||||
tables.should_not be_empty
|
|
||||||
end
|
##
|
||||||
end
|
# Create_Tables
|
||||||
|
##
|
||||||
|
it "Create_tables returns tables for all good xmls" do
|
||||||
|
xmls.each do |xml|
|
||||||
|
results = GPP.parse(xml)
|
||||||
|
tables = GPP.create_tables(results, "test")
|
||||||
|
tables.should_not be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue