#!/usr/bin/perl

# bin/ftp.saint
#
# See if the target provides anonymous ftp, non-intrusive.

require 'config/paths.pl';
require 'config/saint.cf';
require 'perl/misc.pl';
require 'perllib/getopts.pl';

print "BEGIN\n";
$usage="Usage: $0 [-v] target";
&Getopts("v") || die $usage;
if ($#ARGV != 0) {
   if ($ARGV[1] !~ /FW/ && $ARGV[1] !~ /PLUS/) {
      die $usage;
   }
}

$target = $ARGV[0];
$service = &basename($0, ".saint");

@dirs = ('', 'pub/', 'incoming/');

$args = "<<EOF
open $target
quote user anonymous
quote pass -saint\@
cd /
";

for $dir (@dirs) {
    $args .= "put $$.foo $dir$$.foo\n";
    $args .= "dele $dir$$.foo\n";
}

$args .= "quit
EOF
";
print "$args\n" if $opt_v;

open (FOO, "> $$.foo") || die "cannot create test file";
print FOO "SAINT ftp upload file\n";
close FOO;

$dir = "";
$anon=0;
open(FTP, "$FTP -nv $args |") || die "cannot run $FTP";
while(<FTP>) {
    if (defined($opt_v)) {
	print;
    }

    ($dir = "/$1") if /([\w\.\/]*)\/$$.foo/;
    if (/^230/) {
	$anon=1;
	$status = "a";
	$severity = "x";
	$trustee = "";
	$trusted = "";
	$service_output = "ANONYMOUS";
	$text = "offers anon ftp";
	$texts{&saint_string()} = 1;
   } elsif (/^226/) {
	$severity = "nw";
	$trustee = "~ftp@$target";
	$trusted = "ANY\@ANY";
	$service_output = "writable FTP directory";
	$text = "~ftp$dir is writable";
	$texts{&saint_string()} = 1;
   } elsif (/^530/) {
	$status = "a";
	$severity = "x";
	$trustee = "";
	$trusted = "";
	$service_output = "";
	$text = "offers ftp";
	$texts{&saint_string()} = 1;
    } elsif (/^Not connected/) {
	last;
    }
}
close FTP;

# Check for directory traversal by retrieving
# /etc/services and /winnt/win.ini
# (Can't do it in same session as above or it will
# get the 226's mixed up.)

if ($anon) {

    $args = "<<EOF
open $target
quote user anonymous
quote pass -saint\@
cd /
get ../../../../../etc/services $$.unx
get ../../../../../winnt/win.ini $$.win
get .../.../.../.../.../etc/services $$.unx3
get .../.../.../.../.../winnt/win.ini $$.win3
quit
EOF
";
    print "$args\n" if $opt_v;

    $status = "a";
    $severity = "nr";
    $trustee = "~ftp@$target";
    $trusted = "ANY\@ANY";
    $service_output = "FTP server directory traversal";
    $text = "FTP server allows directory traversal";

    $retr = 0;
    open(FTP, "$FTP -nv $args |") || die "cannot run $FTP";
    while(<FTP>) {
	$retr = 1 if /^226/;
    }
    close FTP;

    if ($retr) {
      if (open (UNX, "< $$.unx")) {
	while(<UNX>) {
	    if (/21\/tcp/i) {
		$texts{&saint_string()} = 1;
	    }
	}
	close UNX;
      }
      if (open (WIN, "< $$.win")) {
	while(<WIN>) {
	    if (/./) {
		$texts{&saint_string()} = 1;
	    }
	}
	close WIN;
      }
      if (open (UNX, "< $$.unx3")) {
	while(<UNX>) {
	    if (/21\/tcp/i) {
		$texts{&saint_string()} = 1;
	    }
	}
	close UNX;
      }
      if (open (WIN, "< $$.win3")) {
	while(<WIN>) {
	    if (/./) {
		$texts{&saint_string()} = 1;
	    }
	}
	close WIN;
      }
    }
}

if ($anon) {

    $args = "<<EOF
open $target
quote user anonymous
quote pass -saint\@
quote help
quit
EOF
";
    print "$args\n" if $opt_v;

    $status = "a";
    $severity = "zcio";
    $trustee = "~ftp@$target";
    $trusted = "ANY\@ANY";
    $service_output = "Kerberos detected";
    $text = "Possible vulnerability in Kerberos ftpd";

    $retr = 0;
    open(FTP, "$FTP -nv $args |") || die "cannot run $FTP";
    while(<FTP>) {
	$retr++ if (/\(Version ([\d\.]+)/ && $1>=5 && $1<=5.60);
	$retr++ if (/\bAUTH\b/ && !/AUTH\*/);
    }
    close FTP;

    if ($retr >= 2) {
	$texts{&saint_string()} = 1;
    }
}

unlink "$$.foo";
unlink "$$.unx";
unlink "$$.win";
unlink "$$.unx3";
unlink "$$.win3";

foreach $key (keys %texts) {
    print "$key\n";
}
