Vulnerabilities > CVE-2016-1885 - Improper Restriction of Operations within the Bounds of a Memory Buffer vulnerability in Freebsd 10.1/10.2/9.3

047910
CVSS 6.2 - MEDIUM
Attack vector
LOCAL
Attack complexity
LOW
Privileges required
NONE
Confidentiality impact
NONE
Integrity impact
NONE
Availability impact
HIGH
local
low complexity
freebsd
CWE-119
nessus
exploit available

Summary

Integer signedness error in the amd64_set_ldt function in sys/amd64/amd64/sys_machdep.c in FreeBSD 9.3 before p39, 10.1 before p31, and 10.2 before p14 allows local users to cause a denial of service (kernel panic) via an i386_set_ldt system call, which triggers a heap-based buffer overflow.

Vulnerable Configurations

Part Description Count
OS
Freebsd
3

Common Attack Pattern Enumeration and Classification (CAPEC)

  • Buffer Overflow via Environment Variables
    This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.
  • Overflow Buffers
    Buffer Overflow attacks target improper or missing bounds checking on buffer operations, typically triggered by input injected by an attacker. As a consequence, an attacker is able to write past the boundaries of allocated buffer regions in memory, causing a program crash or potentially redirection of execution as per the attackers' choice.
  • Client-side Injection-induced Buffer Overflow
    This type of attack exploits a buffer overflow vulnerability in targeted client software through injection of malicious content from a custom-built hostile service.
  • Filter Failure through Buffer Overflow
    In this attack, the idea is to cause an active filter to fail by causing an oversized transaction. An attacker may try to feed overly long input strings to the program in an attempt to overwhelm the filter (by causing a buffer overflow) and hoping that the filter does not fail securely (i.e. the user input is let into the system unfiltered).
  • MIME Conversion
    An attacker exploits a weakness in the MIME conversion routine to cause a buffer overflow and gain control over the mail server machine. The MIME system is designed to allow various different information formats to be interpreted and sent via e-mail. Attack points exist when data are converted to MIME compatible format and back.

Exploit-Db

descriptionFreeBSD 10.2 amd64 Kernel - amd64_set_ldt Heap Overflow. CVE-2016-1885. Dos exploit for freebsd_x86-64 platform
fileexploits/freebsd_x86-64/dos/39570.c
idEDB-ID:39570
last seen2016-03-17
modified2016-03-16
platformfreebsd_x86-64
port
published2016-03-16
reporterCore Security
sourcehttps://www.exploit-db.com/download/39570/
titleFreeBSD 10.2 amd64 Kernel - amd64_set_ldt Heap Overflow
typedos

Nessus

  • NASL familyFirewalls
    NASL idPFSENSE_SA-16_02.NASL
    descriptionAccording to its self-reported version number, the remote pfSense install is prior to 2.3. It is, therefore, affected by multiple vulnerabilities.
    last seen2020-06-01
    modified2020-06-02
    plugin id106499
    published2018-01-31
    reporterThis script is Copyright (C) 2018-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/106499
    titlepfSense < 2.3 Multiple Vulnerabilities (SA-16_01 - SA-16_02)
    code
    #
    # (C) Tenable Network Security, Inc.
    #
    
    include("compat.inc");
    
    if (description)
    {
      script_id(106499);
      script_version("1.13");
      script_cvs_date("Date: 2019/11/08");
    
      script_cve_id(
        "CVE-2015-3197",
        "CVE-2015-5300",
        "CVE-2015-7973",
        "CVE-2015-7974",
        "CVE-2015-7975",
        "CVE-2015-7976",
        "CVE-2015-7977",
        "CVE-2015-7978",
        "CVE-2015-7979",
        "CVE-2015-8138",
        "CVE-2015-8139",
        "CVE-2015-8140",
        "CVE-2015-8158",
        "CVE-2016-0702",
        "CVE-2016-0703",
        "CVE-2016-0704",
        "CVE-2016-0705",
        "CVE-2016-0777",
        "CVE-2016-0778",
        "CVE-2016-0797",
        "CVE-2016-0798",
        "CVE-2016-0799",
        "CVE-2016-0800",
        "CVE-2016-1879",
        "CVE-2016-1882",
        "CVE-2016-1885",
        "CVE-2016-10709"
      );
      script_bugtraq_id(
        77312,
        80695,
        80698,
        80704,
        80754,
        81811,
        81814,
        81815,
        81816,
        81959,
        81960,
        81962,
        81963,
        82102,
        82105,
        82237,
        83705,
        83733,
        83743,
        83754,
        83755,
        83763,
        83764
      );
      script_xref(name:"CERT", value:"583776");
      script_xref(name:"CERT", value:"718152");
      script_xref(name:"EDB-ID", value:"39570");
      script_xref(name:"FreeBSD", value:"SA-16:01.sctp");
      script_xref(name:"FreeBSD", value:"SA-16:02.ntp");
      script_xref(name:"FreeBSD", value:"SA-16:05.tcp");
      script_xref(name:"FreeBSD", value:"SA-16:07.openssh");
      script_xref(name:"FreeBSD", value:"SA-16:09.ntp");
      script_xref(name:"FreeBSD", value:"SA-16:11.openssl");
      script_xref(name:"FreeBSD", value:"SA-16:12.openssl");
      script_xref(name:"FreeBSD", value:"SA-16:15.sysarch");
    
      script_name(english:"pfSense < 2.3 Multiple Vulnerabilities (SA-16_01 - SA-16_02)");
      script_summary(english:"Checks the version of pfSense.");
    
      script_set_attribute(attribute:"synopsis", value:
    "The remote firewall host is affected by multiple
    vulnerabilities.");
      script_set_attribute(attribute:"description", value:
    "According to its self-reported version number, the remote pfSense
    install is prior to 2.3. It is, therefore, affected by multiple
    vulnerabilities.");
      script_set_attribute(attribute:"see_also", value:"https://doc.pfsense.org/index.php/2.3_New_Features_and_Changes");
      # https://www.pfsense.org/security/advisories/pfSense-SA-16_01.webgui.asc
      script_set_attribute(attribute:"see_also", value:"http://www.nessus.org/u?b03b53c4");
      # https://www.pfsense.org/security/advisories/pfSense-SA-16_02.webgui.asc
      script_set_attribute(attribute:"see_also", value:"http://www.nessus.org/u?b296df96");
      script_set_attribute(attribute:"solution", value:
    "Upgrade to pfSense version 2.3 or later.");
      script_set_cvss_base_vector("CVSS2#AV:N/AC:L/Au:N/C:C/I:C/A:C");
      script_set_cvss_temporal_vector("CVSS2#E:F/RL:OF/RC:C");
      script_set_cvss3_base_vector("CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H");
      script_set_cvss3_temporal_vector("CVSS:3.0/E:F/RL:O/RC:C");
      script_set_attribute(attribute:"cvss_score_source", value:"CVE-2016-0799");
    
      script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
      script_set_attribute(attribute:"exploit_available", value:"true");
      script_set_attribute(attribute:"exploit_framework_core", value:"true");
      script_set_attribute(attribute:"metasploit_name", value:'pfSense authenticated graph status RCE');
      script_set_attribute(attribute:"exploit_framework_metasploit", value:"true");
    
      script_set_attribute(attribute:"vuln_publication_date", value:"2016/04/01");
      script_set_attribute(attribute:"patch_publication_date", value:"2016/04/01");
      script_set_attribute(attribute:"plugin_publication_date", value:"2018/01/31");
    
      script_set_attribute(attribute:"plugin_type", value:"remote");
      script_set_attribute(attribute:"cpe", value:"cpe:/a:pfsense:pfsense");
      script_set_attribute(attribute:"cpe", value:"cpe:/a:bsdperimeter:pfsense");
      script_end_attributes();
    
      script_category(ACT_GATHER_INFO);
      script_family(english:"Firewalls");
    
      script_copyright(english:"This script is Copyright (C) 2018-2019 and is owned by Tenable, Inc. or an Affiliate thereof.");
    
      script_dependencies("pfsense_detect.nbin");
      script_require_keys("Host/pfSense");
    
      exit(0);
    }
    
    include("vcf.inc");
    include("vcf_extras.inc");
    
    if (!get_kb_item("Host/pfSense")) audit(AUDIT_HOST_NOT, "pfSense");
    
    app_info = vcf::pfsense::get_app_info();
    constraints = [
      { "fixed_version" : "2.3"}
    ];
    
    vcf::pfsense::check_version_and_report(
      app_info:app_info,
      constraints:constraints,
      severity:SECURITY_HOLE,
      flags:{xss:TRUE}
    );
    
  • NASL familyFreeBSD Local Security Checks
    NASL idFREEBSD_PKG_7B6A11B5600A11E6A6C314DAE9D210B8.NASL
    descriptionA special combination of sysarch(2) arguments, specify a request to uninstall a set of descriptors from the LDT. The start descriptor is cleared and the number of descriptors are provided. Due to lack of sufficient bounds checking during argument validity verification, unbound zero
    last seen2020-06-01
    modified2020-06-02
    plugin id92922
    published2016-08-12
    reporterThis script is Copyright (C) 2016-2019 and is owned by Tenable, Inc. or an Affiliate thereof.
    sourcehttps://www.tenable.com/plugins/nessus/92922
    titleFreeBSD : FreeBSD -- Incorrect argument validation in sysarch(2) (7b6a11b5-600a-11e6-a6c3-14dae9d210b8)
    code
    #
    # (C) Tenable Network Security, Inc.
    #
    # The descriptive text and package checks in this plugin were  
    # extracted from the FreeBSD VuXML database :
    #
    # Copyright 2003-2018 Jacques Vidrine and contributors
    #
    # Redistribution and use in source (VuXML) and 'compiled' forms (SGML,
    # HTML, PDF, PostScript, RTF and so forth) with or without modification,
    # are permitted provided that the following conditions are met:
    # 1. Redistributions of source code (VuXML) must retain the above
    #    copyright notice, this list of conditions and the following
    #    disclaimer as the first lines of this file unmodified.
    # 2. Redistributions in compiled form (transformed to other DTDs,
    #    published online in any format, converted to PDF, PostScript,
    #    RTF and other formats) must reproduce the above copyright
    #    notice, this list of conditions and the following disclaimer
    #    in the documentation and/or other materials provided with the
    #    distribution.
    # 
    # THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS"
    # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
    # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
    # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
    # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
    # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
    # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
    # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
    # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION,
    # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    #
    
    include("compat.inc");
    
    if (description)
    {
      script_id(92922);
      script_version("2.7");
      script_cvs_date("Date: 2019/04/11 17:23:06");
    
      script_cve_id("CVE-2016-1885");
      script_xref(name:"FreeBSD", value:"SA-16:15.sysarch");
    
      script_name(english:"FreeBSD : FreeBSD -- Incorrect argument validation in sysarch(2) (7b6a11b5-600a-11e6-a6c3-14dae9d210b8)");
      script_summary(english:"Checks for updated packages in pkg_info output");
    
      script_set_attribute(
        attribute:"synopsis", 
        value:
    "The remote FreeBSD host is missing one or more security-related
    updates."
      );
      script_set_attribute(
        attribute:"description", 
        value:
    "A special combination of sysarch(2) arguments, specify a request to
    uninstall a set of descriptors from the LDT. The start descriptor is
    cleared and the number of descriptors are provided. Due to lack of
    sufficient bounds checking during argument validity verification,
    unbound zero'ing of the process LDT and adjacent memory can be
    initiated from usermode. Impact : This vulnerability could cause the
    kernel to panic. In addition it is possible to perform a local Denial
    of Service against the system by unprivileged processes."
      );
      # https://vuxml.freebsd.org/freebsd/7b6a11b5-600a-11e6-a6c3-14dae9d210b8.html
      script_set_attribute(
        attribute:"see_also",
        value:"http://www.nessus.org/u?0c3298fe"
      );
      script_set_attribute(attribute:"solution", value:"Update the affected packages.");
      script_set_cvss_base_vector("CVSS2#AV:L/AC:L/Au:N/C:N/I:N/A:C");
      script_set_cvss_temporal_vector("CVSS2#E:F/RL:OF/RC:C");
      script_set_cvss3_base_vector("CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H");
      script_set_cvss3_temporal_vector("CVSS:3.0/E:F/RL:O/RC:C");
      script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
      script_set_attribute(attribute:"exploit_available", value:"true");
      script_set_attribute(attribute:"exploit_framework_core", value:"true");
    
      script_set_attribute(attribute:"plugin_type", value:"local");
      script_set_attribute(attribute:"cpe", value:"p-cpe:/a:freebsd:freebsd:FreeBSD");
      script_set_attribute(attribute:"cpe", value:"cpe:/o:freebsd:freebsd");
    
      script_set_attribute(attribute:"vuln_publication_date", value:"2016/03/16");
      script_set_attribute(attribute:"patch_publication_date", value:"2016/08/11");
      script_set_attribute(attribute:"plugin_publication_date", value:"2016/08/12");
      script_end_attributes();
    
      script_category(ACT_GATHER_INFO);
      script_copyright(english:"This script is Copyright (C) 2016-2019 and is owned by Tenable, Inc. or an Affiliate thereof.");
      script_family(english:"FreeBSD Local Security Checks");
    
      script_dependencies("ssh_get_info.nasl");
      script_require_keys("Host/local_checks_enabled", "Host/FreeBSD/release", "Host/FreeBSD/pkg_info", "Settings/ParanoidReport");
    
      exit(0);
    }
    
    
    include("audit.inc");
    include("freebsd_package.inc");
    
    
    if (!get_kb_item("Host/local_checks_enabled")) audit(AUDIT_LOCAL_CHECKS_NOT_ENABLED);
    if (!get_kb_item("Host/FreeBSD/release")) audit(AUDIT_OS_NOT, "FreeBSD");
    if (!get_kb_item("Host/FreeBSD/pkg_info")) audit(AUDIT_PACKAGE_LIST_MISSING);
    
    
    if (report_paranoia < 2) audit(AUDIT_PARANOID);
    
    flag = 0;
    
    if (pkg_test(save_report:TRUE, pkg:"FreeBSD>=11.0<11.0_2")) flag++;
    if (pkg_test(save_report:TRUE, pkg:"FreeBSD>=10.3<10.3_11")) flag++;
    if (pkg_test(save_report:TRUE, pkg:"FreeBSD>=10.2<10.2_24")) flag++;
    if (pkg_test(save_report:TRUE, pkg:"FreeBSD>=10.1<10.1_41")) flag++;
    if (pkg_test(save_report:TRUE, pkg:"FreeBSD>=9.3<9.3_49")) flag++;
    
    if (flag)
    {
      if (report_verbosity > 0) security_warning(port:0, extra:pkg_report_get());
      else security_warning(0);
      exit(0);
    }
    else audit(AUDIT_HOST_NOT, "affected");
    

Packetstorm

data sourcehttps://packetstormsecurity.com/files/download/136276/CORE-2016-0005.txt
idPACKETSTORM:136276
last seen2016-12-05
published2016-03-17
reporterCore Security Technologies
sourcehttps://packetstormsecurity.com/files/136276/FreeBSD-Kernel-amd64_set_ldt-Heap-Overflow.html
titleFreeBSD Kernel amd64_set_ldt Heap Overflow

Seebug

bulletinFamilyexploit
description### `FreeBSD`简介 * `FreeBSD`是一种类`UNIX`的开源操作系统,为不同架构的计算机系统提供了不同程度的支持。`FreeBSD`提供先进的网络、性能、安全以及兼容性,这些特性在其他现代操作系统上仍有所缺失,即使是一些最好的商业操作系统。 * 在网络方面,`FreeBSD`的性能也是相当优异的。在很重的负载之下,`FreeBSD`仍然可以稳定的运行,这也是很多网络服务器采用 FreeBSD 的原因之一。 ### 漏洞描述 * 在`FreeBSD 10.2-RELENG`之前版本内核代码中,`amd_64_set_ldt()`函数存在整数签名错误(在`/sys/amd64/amd64/sys_machdep.c`中定义),实现了`i368_set_ldt`系统调用`amd64`版本的操作系统,最终导致内核堆溢出,本地攻击者可造成系统崩溃 ### 环境搭建及漏洞复现 其实此漏洞发生在amd64_set_ldt函数中,但罪魁祸首其实是里面的另一个函数,此漏洞是由于FreeBSD 10.2系统中的amd64_set_ldt函数在处理指针中的成员函数时,由于对函数值没有进行有效的控制,导致在函数处理时由于整数溢出,导致后续调用bzero()函数时堆被置0,引发系统异常处理,下面对此漏洞进行详细分析。 首先需要在freebsd上创建一个vsftp用于传输PoC文件,之后使用Clang编译,执行PoC即可。 ![](https://images.seebug.org/contribute/0947ad74-b153-43e1-adaa-cb3ea5b11841) 执行PoC之后系统崩溃,产生了vmcore。 ![](https://images.seebug.org/contribute/dcb11a43-c386-467a-8325-c0fc4fd623ce) 我们使用kgdb加载vmcore来看一下崩溃信息。 ![](https://images.seebug.org/contribute/80bb6c52-00a3-454c-adbc-779ab207b862) 实际上在我另一篇FreeBSD的分析中提到了#8处的call trap()这段asm代码,其实到这里,已经进入了linux的内核异常处理过程,那么我们要关注的就是#9位置以及往后的代码,下面我们就从vmcore入手,结合内核源码来分析整个漏洞形成的原因。 ### 漏洞分析 1 (来自用户`@k0Sh1`) 首先,我们在kgdb下通过bt来回溯一下漏洞发生的过程,和刚才直接查看vmcore稍有不同。 ![](https://images.seebug.org/contribute/3b19a238-4059-43e9-979c-e8e9ecf74f12) 其中我们看到原先#9处的位置,现在调用了bzero()函数,其实这个函数是置零函数,其实,这个才是漏洞触发的真正原因!后续分析过程中我们会讲解一下bzero这个函数的函数结构,首先我们来看一下#9之前的调用情况,首先来看一下#10位置。我们来看一下amd64_set_ldt函数。 首先来到崩溃位置622行 ``` bzero(&((struct user_segment_descriptor *)(pldt->ldt_base)) [uap->start], sizeof(struct user_segment_descriptor) * i); ``` 这里调用到了一个sizeof指针i,那么这个i指针从哪里来的呢? ``` largest_ld = uap->start + uap->num; if (largest_ld > max_ldt_segment) largest_ld = max_ldt_segment; i = largest_ld - uap->start; ``` 可以看到,i的大小与uap有关,那么uap又是从哪里来的呢? ``` int amd64_set_ldt(td, uap, descs) ``` 函数定义部分,uap作为参数传入,那么接下来,我们就来到外层函数sysarch_ldt来仔细分析一下uap到底发生了什么,会导致内核堆溢出漏洞的发生。 首先在外层函数sysarch_ldt的定义处 ``` int sysarch_ldt(struct thread *td, struct sysarch_args *uap, int uap_space) ``` 可以看到这里给uap定义了一个明确的类,struct sysarch_args,我们就通过vmcore来看一下这个结构类到底是怎么回事。 ![](https://images.seebug.org/contribute/56187ad4-ae09-4079-8c2b-2d9c10873c92) 这里我们要关注几个点,第一个是op的值为1,第二个是parms的值,接下来回到源代码部分。 ``` if (uap_space == UIO_USERSPACE) { error = copyin(uap->parms, &la, sizeof(struct i386_ldt_args)); if (error != 0) return (error); largs = &la; } else largs = (struct i386_ldt_args *)uap->parms; ``` 进入sysarch_args函数后,在这个if语句中,largs会被struct i386_ldt_args类赋值,赋值的值是uap的parms成员,实际上通过上面图中的回溯时不能准确看到这个成员中的内容的,一会再来讲解如何查看该成员的内容,但要记住这个成员。接下来会进入一处switch语句。 ``` switch (uap->op) { case I386_GET_LDT: error = amd64_get_ldt(td, largs); break; case I386_SET_LDT: if (largs->descs != NULL && largs->num > max_ldt_segment) return (EINVAL); set_pcb_flags(td->td_pcb, PCB_FULL_IRET); if (largs->descs != NULL) { lp = malloc(largs->num * sizeof(struct user_segment_descriptor), M_TEMP, M_WAITOK); error = copyin(largs->descs, lp, largs->num * sizeof(struct user_segment_descriptor)); if (error == 0) error = amd64_set_ldt(td, largs, lp); free(lp, M_TEMP); } else { error = amd64_set_ldt(td, largs, NULL); } break; } ``` 语句中,对uap的成员op做了一个判断,刚才我们通过p查看uap的时候发现op的值为1,那么我们在sys_machdep.h中看一下对这两个CASE的定义,40行。 ``` #define I386_GET_LDT 0 #define I386_SET_LDT 1 ``` 可以看到I386_SET_LDT值为1,也就是说程序会进入下面那处CASE语句中,接下来在语句中我们看到使用了largs这个结构体,之前我们用图中结构体回溯失败了,因为这是64位系统,但是vmcore给出的成员地址却是32位的,系统没法定位那个地址的内容。 但是我们注意到,在接下来amd64_set_ldt的两处调用中,都涉及到largs,作为第二个参数刚才我们已经分析过,第二个参数正是uap,那么这样,我们可以通过#10处的uap的值,来查看largs结构成员的内容。 ![](https://images.seebug.org/contribute/d48d2e4e-d879-4495-b281-0b3433f8faf0) 我们可以看到三个成员都非常重要,start=1,descs=0x0(也就是NULL),num=2147483648,那么回到刚才的源代码位置。 首先descs=NULL,因此第一个if语句不会进入,那么会进入esle语句,也就是说largs会直接作为uap进入amd64_set_ldt函数中。 接下来回到amd64_set_ldt函数。 ``` if (descs == NULL) { /* Free descriptors */ if (uap->start == 0 && uap->num == 0) uap->num = max_ldt_segment; if (uap->num == 0) return (EINVAL); if ((pldt = mdp->md_ldt) == NULL || uap->start >= max_ldt_segment) return (0); largest_ld = uap->start + uap->num; ``` 进入函数后会进入一系列判断,记住我们刚才的值,desc=NULL,因此会进入这个循环,接着start=1,那么第一个if语句不满足。num不等于0,所以第二个if语句不满足,那么largest_ld=uap->start+uap->num这就是关键了! 我们刚才num的值为2147483648,也就是0x800000000,而start值为1,也就是说,而largest_ld的定义是有符号数,因此相加后,值为一个负数! 接下来。 ``` if (largest_ld > max_ldt_segment) largest_ld = max_ldt_segment; i = largest_ld - uap->start; ``` 会将这个值进行一个判断,max_ldt_segment在整个.c文件入口处已经有定义。 ``` int max_ldt_segment = 1024; ``` 由于largest_ld的大小为负数,条件判断肯定通过,但是接下来i为两值相减,减完之后就是uap->num的值,也就是0x80000000,一个极大值。 接下来就说到我们刚才提到的bzero(),这个函数是linux下的一个置0函数,它的定义如下 ``` 原型:extern void bzero(void *s, int n); 参数说明:s 要置零的数据的起始地址; n 要置零的数据字节个数。 ``` 这里,n就是sizeof*i,这个i的大小就是num,是一个极大的值,因此,在调用这个函数后,内存空间将有大面积置0,因此造成了内核崩溃。 ### 漏洞分析 2(来自用户 `@Pyx_`) * 从`FreeBSD`中可以得知,`i386_set_ldt`系统调用了英特尔`i386`版本,这个系统调用可以用来管理`i386`每个进程的局部描述符号(`LDT`)条目。`FreeBSD`的`amd64`版本仍然暴露出了在64位版本的操作系统中这个系统调用用于运行32位应用程序 * 在`FreeBSD`内核中的`sysarch()`函数中我们可以发现特定结构的系统调用,在`/sys/amd64/amd64/sys_machdep.c`文件中定义 ``` int sysarch(td, uap) struct thread *td; register struct sysarch_args *uap; { [...] if (uap->op == I386_GET_LDT || uap->op == I386_SET_LDT) return (sysarch_ldt(td, uap, UIO_USERSPACE)); [...] ``` * 从上面的代码可以看出,如果被调用的是`i386_get)ldt`或`i386_set_ldt`,那么调用`sysarch_ldt()`函数 * 查看`sysarch_ldt()`函数中处理`i386_set_ldt`的代码 ``` int sysarch_ldt(struct thread *td, struct sysarch_args *uap, int uap_space) { struct i386_ldt_args *largs, la; struct user_segment_descriptor *lp; [...] switch (uap->op) { [...] case I386_SET_LDT: if (largs->descs != NULL && largs->num > max_ldt_segment) return (EINVAL); set_pcb_flags(td->td_pcb, PCB_FULL_IRET); if (largs->descs != NULL) { lp = malloc(largs->num * sizeof(struct user_segment_descriptor), M_TEMP, M_WAITOK); error = copyin(largs->descs, lp, largs->num * sizeof(struct user_segment_descriptor)); if (error == 0) error = amd64_set_ldt(td, largs, lp); free(lp, M_TEMP); } else { error = amd64_set_ldt(td, largs, NULL); } break; ``` * 从上面的代码看出,有一个指向`i386_ldt_args`结构体的指针,查看一下定义 ``` struct i386_ldt_args { unsigned int start; union descriptor *descs; unsigned int num; }; ``` * 从上面得知所有`i386_ldt_args`结构体的字段都是由用户控制的,用户使用`i386_set_ldt()`来指定这3个参数,那么查看`i386_set_ldt()` * `int i386_set_ldt(int start_sel, union descriptor *descs, int num_sels);` * 结合上面`sysarch_ldt()`的代码可以得知,如果我们指定`NULL指针`作为`i386_set_ldt()`的第二个参数(`largs->descs`),那么它最终会调用`amd64_set_ldt()`函数,传送`largs`作为第二个参数,一个`NULL指针`作为第三个参数 * 接下来查看`amd64_set_ldt()`函数 ``` int amd64_set_ldt(struct thread *td, struct i386_ldt_args *uap, struct user_segment_descriptor *descs); ``` ``` int amd64_set_ldt(td, uap, descs) struct thread *td; struct i386_ldt_args *uap; struct user_segment_descriptor *descs; { [...] int largest_ld; [...] 608 if (descs == NULL) { 609 /* Free descriptors */ 610 if (uap->start == 0 && uap->num == 0) 611 uap->num = max_ldt_segment; 612 if (uap->num == 0) 613 return (EINVAL); 614 if ((pldt = mdp->md_ldt) == NULL || 615 uap->start >= max_ldt_segment) 616 return (0); 617 largest_ld = uap->start + uap->num; 618 if (largest_ld > max_ldt_segment) 619 largest_ld = max_ldt_segment; 620 i = largest_ld - uap->start; 621 mtx_lock(&dt_lock); 622 bzero(&((struct user_segment_descriptor *)(pldt->ldt_base)) 623 [uap->start], sizeof(struct user_segment_descriptor) * i); 624 mtx_unlock(&dt_lock); 625 return (0); 626 } ``` * 从上面可以看出,漏洞就发生在`amd64_set_ldt()`这个函数中,如果第三个参数被设置成`NULL`时,就会执行之后的代码,完全是由用户控制的 * 上面的两个`if`语句在610和612判断了`uap->start`和`uap->num`,可以避免`uap->num`为0,接下来在614/615的`if`,如果`mdp->ldt`指针为`NULL`或者`uap->start`大于等于`max_ldt_segment(1024)`,那么导致函数退出。如果`mdp->md_ldt`是一个非空的值,那么在触发这个bug之前可以通过增加一个初始入口进入`LDT`来完成,例如: ``` struct segment_descriptor desc = {0, 0, SDT_MEMRW, SEL_UPL, 1, 0, 0, 1, 0 ,0}; i386_set_ldt(LDT_AUTO_ALLOC, (union descriptor *) &desc, 1); ``` * 接下来查看617-619行的漏洞代码 ``` 617 largest_ld = uap->start + uap->num; 618 if (largest_ld > max_ldt_segment) 619 largest_ld = max_ldt_segment; 620 i = largest_ld - uap->start; ``` * 从上面看出`largest_ld = uap->start + uap->num`,`largest_ld`是一个整数,代码行618-619确保`largest_ld`不大于`max_ldt_segment(1024)`,但是,`largest_ld`是由用户控制的,可以通过设置`uap->num`为负数来绕过 * 这个符号的错误最终导致在`FreeBSD`内核的一个堆溢出(之后调用的`bzero()`函数用一个极大的值作为它的`len`参数) ``` 622 bzero(&((struct user_segment_descriptor *)(pldt->ldt_base)) 623 [uap->start], sizeof(struct user_segment_descriptor) * i); ``` ### 参考链接 * [http://www.coresecurity.com/advisories/freebsd-kernel-amd64setldt-heap-overflow](http://www.coresecurity.com/advisories/freebsd-kernel-amd64setldt-heap-overflow)
idSSV:91149
last seen2017-11-19
modified2016-03-23
published2016-03-23
reporterk0shl
titleFreeBSD 10.2 64位内核堆溢出漏洞(CVE-2016-1885)