pages tagged cvemejo roaminghttps://blog.freesources.org//tags/cve/mejo roamingikiwiki2016-12-07T01:53:27ZOn CVE-2016-4484, a (securiy)? bug in the cryptsetup initramfs integrationhttps://blog.freesources.org//posts/2016/12/CVE-2016-4484/2016-12-07T01:53:27Z2016-12-06T14:21:10Z
<h1 id="On_CVE-2016-4484.2C_a_.28security.29.3F_bug_in_the_cryptsetup_initramfs_integration">On CVE-2016-4484, a (security)? bug in the cryptsetup initramfs integration</h1>
<p>On November 4, I was made aware of a security vulnerability in the integration
of cryptsetup into initramfs. The vulnerability was discovered by security
researchers Hector Marco and Ismael Ripoll of <a href="http://cybersecurity.upv.es/">CyberSecurity UPV Research
Group</a> and got
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-4484">CVE-2016-4484</a>
assigned.</p>
<p>In this post I'll try to reflect a bit on</p>
<ul>
<li><a href="https://blog.freesources.org//tags/cve/#What_CVE-2016-4484_is_all_about">the nature of the vulnerability</a></li>
<li><a href="https://blog.freesources.org//tags/cve/#About_disclosure.2C_wording_and_clickbaiting">the way it was published and discussed by technology news press</a></li>
</ul>
<h2 id="What_CVE-2016-4484_is_all_about">What CVE-2016-4484 is all about</h2>
<p>Basically, the vulnerability is about two separate but related issues:</p>
<h3 id="z-._Initramfs_rescue_shell_considered_harmful">1. Initramfs rescue shell considered harmful</h3>
<p>The <em>main</em> topic that Hector Marco and Ismael Ripoll address in <a href="http://hmarco.org/bugs/CVE-2016-4484/CVE-2016-4484_cryptsetup_initrd_shell.html">their
publication</a>
is that Debian exits into a rescue shell in case of failure during initramfs,
and that this can be triggered by entering a wrong password ~93 times in a
row.</p>
<p>Indeed the Debian initramfs implementation as provided by <em>initramfs-tools</em>
exits into a rescue shell (usually a busybox shell) after a defined amount of
failed attempts to make the root filesystem available. The loop in question
is in <a href="https://anonscm.debian.org/cgit/kernel/initramfs-tools.git/tree/scripts/local"><em>local_device_setup()</em> at the <em>local</em> initramfs script</a></p>
<p>In general, this behaviour is considered as a feature: if the root device
hasn't shown up after 30 rounds, the rescue shell is spawned to provide the
local user/admin a way to debug and fix things herself.</p>
<p>Hector Marco and Ismael Ripoll <a href="http://hmarco.org/bugs/CVE-2016-4484/CVE-2016-4484_cryptsetup_initrd_shell.html#disc">argue</a>
that in special environments, e.g. on public computers with password protected
BIOS/UEFI and bootloader, this opens an attack vector and needs to be regarded
as a security vulnerability:</p>
<blockquote><p>It is common to assume that once the attacker has physical access to the
computer, the game is over. The attackers can do whatever they want. And
although this was true 30 years ago, today it is not.</p>
<p>There are many "levels" of physical access. [...]</p>
<p>In order to protect the computer in these scenarios: the BIOS/UEFI has one
or two passwords to protect the booting or the configuration menu; the GRUB
also has the possibility to use multiple passwords to protect unauthorized
operations.</p>
<p>And in the case of an encrypted system, the initrd shall block the maximum
number of password trials and prevent the access to the computer in that
case.</p></blockquote>
<p>While Hector and Ismael have a valid point in that the rescue shell might
open an additional attack vector in special setups, this is not true for
the vast majority of Debian systems out there: in most cases a local attacker
can alter the boot order, replace or add boot devices, modify boot options in
the (GNU GRUB) bootloader menu or modify/replace arbitrary hardware parts.</p>
<p>The required scenario to make the initramfs rescue shell an additional attack
vector is indeed <em>very</em> special: locked down hardware, password protected
BIOS and bootloader but still local keyboard (or serial console) access are
required at least.</p>
<p>Hector and Ismael <a href="http://hmarco.org/bugs/CVE-2016-4484/CVE-2016-4484_cryptsetup_initrd_shell.html#disc">argue</a>
that the default should be changed for enhanced security:</p>
<blockquote><p>[...] But then Linux is used in more hostile environments, this helpful
(but naive) recovery services shall not be the default option.</p></blockquote>
<p>For the reasons explained about, I tend to disagree to Hectors and Ismaels
opinion here. And after discussing this topic with several people I find my
opinion reconfirmed: the Debian Security Team <a href="https://security-tracker.debian.org/tracker/CVE-2016-4484">disputes the security
impact</a> of the
issue and <a href="http://www.saout.de/pipermail/dm-crypt/2016-November/005433.html">others</a>
<a href="http://www.saout.de/pipermail/dm-crypt/2016-November/005434.html">agree</a>.</p>
<p>But leaving the disputable opinion on a sane default aside, I don't think that
the cryptsetup package is the right place to change the default, if at all.
If you want added security by a locked down initramfs (i.e. no rescue shell
spawned), then at least the bootloader (GNU GRUB) needs to be locked down by
default as well.</p>
<p>To make it clear: if one wants to lock down the boot process, bootloader and
initramfs should be locked down together. And the right place to do this would
be the <a href="https://www.gnu.org/software/grub/manual/html_node/Simple-configuration.html">configurable behaviour of
grub-mkconfig</a>.
Here, one can set a password for GRUB and the boot parameter 'panic=1' which
<a href="https://manpages.debian.org/cgi-bin/man.cgi?query=initramfs-tools&manpath=Debian+8+jessie&format=html&locale=en">disables the spawning of a rescue shell in
initramfs</a>.</p>
<p>But as mentioned, I don't agree that this would be sane defaults. The vast
majority of Debian systems out there don't have any security added by locked
down bootloader and initramfs and the benefit of a rescue shell for debugging
purposes clearly outrivals the minor security impact in my opinion.</p>
<p>For the few setups which require the added security of a locked down bootloader
and initramfs, we already have the relevant options documented in the <a href="https://www.debian.org/doc/manuals/securing-debian-howto/index.en.html">Securing
Debian Manual</a>:</p>
<ul>
<li><a href="https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html#s-bios-boot">4.3 Change the BIOS (again)</a></li>
<li><a href="https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html#s-lilo-passwd">4.4 Set a LILO or GRUB password</a></li>
<li><a href="https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html#s-kernel-initramfs-prompt">4.5 Disable root prompt on the initramfs</a></li>
<li><a href="https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html#s-kernel-root-prompt">4.6 Remove root prompt on the kernel</a></li>
</ul>
<p>After discussing the topic with initramfs-tools maintainers today, Guilhem and
me (the cryptsetup maintainers) finally decided to not change any defaults and
just add a 'sleep 60' after the maximum allowed attempts were reached.</p>
<h3>2. <em>tries=n</em> option ignored, local brute-force slightly cheaper</h3>
<p>Apart from the issue of a rescue shell being spawned, Hector and Ismael also
discovered a programming bug in the cryptsetup initramfs integration. This bug
in the <em><a href="https://anonscm.debian.org/cgit/pkg-cryptsetup/cryptsetup.git/tree/debian/initramfs/cryptroot-script">cryptroot initramfs local-top
script</a></em>
allowed endless retries of passphrase input, ignoring the <em>tries=n</em> option of
<em>crypttab</em> (and the default of 3). As a result, theoretically unlimited
attempts to unlock encrypted disks were possible when processed during
initramfs stage. The attack vector here was that local brute-force attacks are
a bit cheaper. Instead of having to reboot after max tries were reached, one
could go on trying passwords.</p>
<p>Even though efficient brute-force attacks are mitigated by the
<a href="https://en.wikipedia.org/wiki/PBKDF2">PBKDF2</a> implementation in cryptsetup,
this clearly is a <em>real bug</em>.</p>
<p>The reason for the bug was twofold:</p>
<ul>
<li><p>First, the condition in <em>setup_mapping()</em> responsible for making the
function fail when the maximum amount of allowed attempts is reached,
was never met:</p>
<p><div class="highlight-bash"><pre class="hl">setup_mapping<span class="hl opt">()</span>
<span class="hl opt">{</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl slc"># Try to get a satisfactory password $crypttries times</span>
count<span class="hl opt">=</span><span class="hl num">0</span> <br />
<span class="hl kwa">while</span> <span class="hl opt">[</span> <span class="hl kwd">$crypttries</span> <span class="hl opt">-</span>le <span class="hl num">0</span> <span class="hl opt">]</span> || <span class="hl opt">[</span> <span class="hl kwd">$count</span> <span class="hl opt">-</span>lt <span class="hl kwd">$crypttries</span> <span class="hl opt">];</span> <span class="hl kwa">do</span>
<span class="hl kwb">export</span> CRYPTTAB_TRIED<span class="hl opt">=</span><span class="hl str">"</span><span class="hl ipl">$count</span><span class="hl str">"</span>
count<span class="hl opt">=</span>$<span class="hl opt">((</span> <span class="hl kwd">$count</span> <span class="hl opt">+</span> <span class="hl num">1</span> <span class="hl opt">))</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl kwa">done</span>
<span class="hl kwa">if</span> <span class="hl opt">[</span> <span class="hl kwd">$crypttries</span> <span class="hl opt">-</span>gt <span class="hl num">0</span> <span class="hl opt">] && [</span> <span class="hl kwd">$count</span> <span class="hl opt">-</span>gt <span class="hl kwd">$crypttries</span> <span class="hl opt">];</span> <span class="hl kwa">then</span>
message <span class="hl str">"cryptsetup: maximum number of tries exceeded for</span> <span class="hl ipl">$crypttarget</span><span class="hl str">"</span>
<span class="hl kwb">return</span> <span class="hl num">1</span>
<span class="hl kwa">fi</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl opt">}</span>
</pre></div></p>
<p>As one can see, the while loop stops when <code>$count -lt $crypttries</code>.
Thus the second condition <code>$count -gt $crypttries</code> is never met. This
can easily be fixed by decreasing <em>$count</em> by one in case of a successful
unlock attempt along with changing the second condition to <code>$count -ge
$crypttries</code>:</p>
<p><div class="highlight-bash"><pre class="hl">setup_mapping<span class="hl opt">()</span>
<span class="hl opt">{</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl kwa">while</span> <span class="hl opt">[</span> <span class="hl kwd">$crypttries</span> <span class="hl opt">-</span>le <span class="hl num">0</span> <span class="hl opt">]</span> || <span class="hl opt">[</span> <span class="hl kwd">$count</span> <span class="hl opt">-</span>lt <span class="hl kwd">$crypttries</span> <span class="hl opt">];</span> <span class="hl kwa">do</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl slc"># decrease $count by 1, apparently last try was successful.</span>
count<span class="hl opt">=</span>$<span class="hl opt">((</span> <span class="hl kwd">$count</span> <span class="hl opt">-</span> <span class="hl num">1</span> <span class="hl opt">))</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl kwa">done</span>
<span class="hl kwa">if</span> <span class="hl opt">[</span> <span class="hl kwd">$crypttries</span> <span class="hl opt">-</span>gt <span class="hl num">0</span> <span class="hl opt">] && [</span> <span class="hl kwd">$count</span> <span class="hl opt">-</span>ge <span class="hl kwd">$crypttries</span> <span class="hl opt">];</span> <span class="hl kwa">then</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl kwa">fi</span>
<span class="hl opt">[</span>...<span class="hl opt">]</span>
<span class="hl opt">}</span>
</pre></div></p>
<p>Christian Lamparter already <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=646083">spotted this bug</a>
back in October 2011 and provided a <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?att=1;bug=646083;filename=fixfax;msg=5">(incomplete) patch</a>,
but back then I even managed to merge the patch <a href="https://anonscm.debian.org/cgit/pkg-cryptsetup/cryptsetup.git/diff/debian/initramfs/cryptroot-script?id=5e32f13d9245bf54b6205429358963304d9e969f">in an improper way</a>,
making it even more useless: The patch by Christian forgot to decrease
<em>$count</em> by one in case of a successful unlock attempt, resulting in
warnings about maximum tries exceeded even for successful attemps in
some circumstances. But instead of adding the decrease myself and
keeping the (almost correct) condition <em>$count -eq $crypttries</em> for
detection of exceeded maximum tries, I changed back the condition to
the wrong original <em>$count -gt $crypttries</em> that again was never met.
Apparently I didn't test the fix properly back then. I definitely should
do better in future!</p></li>
<li><p>Second, <a href="https://anonscm.debian.org/cgit/pkg-cryptsetup/cryptsetup.git/commit/?id=ffcd152a091b84c6ccd2d8668d94c7f74ff55f0a">back in December 2013</a>,
I added a <em><a href="https://anonscm.debian.org/cgit/pkg-cryptsetup/cryptsetup.git/tree/debian/initramfs/cryptroot-script-block">cryptroot initramfs local-block script</a></em>
as suggested by Goswin von Brederlow in order to fix <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=678692">bug #678692</a>.
The purpose of the <em>cryptroot initramfs local-block script</em> is to invoke
the <em>cryptroot initramfs local-top</em> script again and again in a loop. This
is required to support complex block device stacks.</p>
<p>In fact, the numberless options of stacked block devices are one of the
biggest and most inglorious reasons that the cryptsetup initramfs
integration scripts became so complex over the years. After all we need
to support setups like <em>rootfs on top of LVM with two separate encrypted
PVs</em> or <em>rootfs on top of LVM on top of dm-crypt on top of MD raid</em>.</p>
<p>The problem with the <em>local-block</em> script is that exiting the
<em>setup_mapping()</em> function merely triggers a new invocation of the very
same function.</p>
<p>The guys who discovered the bug suggested a simple and good solution to
this bug: When maximum attempts are detected (by second condition from
above), the script sleeps for 60 seconds. This mitigates the brute-force
attack options for local attackers - even rebooting after max attempts
should be faster.</p></li>
</ul>
<h2 id="About_disclosure.2C_wording_and_clickbaiting">About disclosure, wording and clickbaiting</h2>
<p>I'm happy that Hector and Ismael brought up the topic and made their argument
about the security impacts of an initramfs rescue shell, even though I have
to admit that I was rather astonished about the fact that they got a CVE
assigned.</p>
<p>Nevertheless I'm very happy that they informed the Security Teams of Debian and
Ubuntu prior to publishing their findings, which put me in the loop in turn.
Also Hector and Ismael were open and responsive when it came to discussing
their proposed fixes.</p>
<p>But unfortunately the way they advertised their finding was not very helpful.
They announced a speech about this topic at the DeepSec 2016 in Vienna with
the headline <em><a href="https://www.deepsec.net/speaker.html#PSLOT280">Abusing LUKS to Hack the System</a></em>.</p>
<p>Honestly, this headline is missleading - if not wrong - in several ways:</p>
<ul>
<li>First, the whole issue is <em>not</em> about LUKS, neither is it about cryptsetup
itself. It's about <em>Debians integration of cryptsetup into the initramfs</em>,
which is a compeletely different story.</li>
<li>Second, the term <em>hack the system</em> suggests that an exploit to break into
the system is revealed. This is not true. The device encryption is not
endangered at all.</li>
<li>Third - as shown above - very special prerequisites need to be met in order
to make the mere existance of a LUKS encrypted device <em>the</em> relevant fact
to be able to spawn a rescue shell during initramfs.</li>
</ul>
<p>Unfortunately, the way this issue was published lead to even worse articles
in the tech news press. Topics like <em><a href="http://betanews.com/2016/11/15/linux-security-bug-cryptsetup-luks/">Major security hole found in Cryptsetup
script for LUKS disk encryption</a></em>
or <em><a href="https://www.bleepingcomputer.com/news/security/linux-flaw-allows-root-shell-during-boot-up-for-luks-disk-encrypted-systems/">Linux Flaw allows Root Shell During Boot-Up for LUKS Disk-Encrypted
Systems</a></em> suggest that a <em>major</em> security vulnerabilty
was revealed and that it compromised the protection that cryptsetup respective
LUKS offer.</p>
<p>If these articles/news did anything at all, then it was <a href="http://www.saout.de/pipermail/dm-crypt/2016-November/005445.html">causing damage to the
cryptsetup project</a>,
which is <em>not</em> affected by the whole issue <em>at all</em>.</p>
<p>After the cat was out of the bag, Marco and Ismael aggreed that the way the
news picked up the issue was suboptimal, but I cannot fight the feeling that
the over-exaggeration was partly intended and that clickbaiting is taking
place here. That's a bit sad.</p>
<h2 id="Links">Links</h2>
<ul>
<li><a href="http://hmarco.org/bugs/CVE-2016-4484/CVE-2016-4484_cryptsetup_initrd_shell.html">Hector Marco & Ismael Ripoll: CVE-2016-4484: Cryptsetup Initrd root Shell</a></li>
<li><a href="http://seclists.org/oss-sec/2016/q4/427">Public announcement by Hector Marco of CVE-2014-4484 on oss-security</a></li>
<li><a href="http://www.saout.de/pipermail/dm-crypt/2016-November/005432.html">Discussion about CVE-2016-4484 on dm-crypt@saout.de</a></li>
</ul>