#514015 cdebootstrap - packages with symlinks to dirs (libc6) can lead to writes outside the new root

Package:
cdebootstrap
Source:
cdebootstrap
Description:
Bootstrap a Debian system
Submitter:
Bastian Blank
Date:
2017-11-17 12:30:08 UTC
Severity:
important
#514015#5
Date:
2009-02-03 12:57:08 UTC
From:
To:
Packages with absolute symlinks to dirs like libc6 on amd64, ppc64 and
s390x can lead to overwrites of files outside of the new root.

Bastian

#514015#12
Date:
2009-02-03 13:20:11 UTC
From:
To:
Both cdebootstrap and debootstrap uses plain tar to extract packages the
first time.

If one package (lib6) contains the symlink /lib64 -> /lib, another
package (in this case libattr1) which includes files in /lib64, will
be extracted into the host system and overwrite files there, as tar
follows the symlinks.

Bastian

#514015#19
Date:
2009-02-05 17:20:24 UTC
From:
To:
Hi,

just as an exercise what might be done to fix this:
It would seem that the options are
- not fix it (for now),
- find something in current tar that works (I didn't),
- switch off tar,
- try to do something with tar
  (new upstream - vs. release: ugh - has a "apply to symlink target"
   --transform flag, but that also might need post-processing of
   symlinks at end of bootstrapping),
- try to do some ld-preload(?) trickery for changing the way symlinks
  are read,
- try to find some way to chroot before calling tar, one possibility
  would be copying required files to some temp CWD, make tar look there
  for the libraries, chrooting to the target and calling tar from (the
  unchanged) CWD,
- add chroot option to tar (see patch).

It would seem that in terms of total invasiveness, the last option is
minimal, but it might be unwise to add options without consulting
upstream (however, it once upstream agrees to have an option with a
given spec, one could implement that locally).

Of course, the patch introduces new strings (translator-:( ), but they
only shown with the new option.

But then, this is just an idea, I'm sure you'll come up with a good, and
probably better solution.

Kind regards

T.

#514015#24
Date:
2009-02-05 17:44:25 UTC
From:
To:
How about just fixing the offending package to use a relative symlink,
like /lib64 -> lib?  This is well within policy since they're adjacent
objects in the same directory, and is less likely to break things or be
a pain to maintain over time than any of the options above.  I can't
think of any negative consequence to this change, offhand?

Adding a Debian-specific option to tar would certainly not be my first
choice.

Bdale

#514015#29
Date:
2009-02-05 17:58:05 UTC
From:
To:
There is nothing.

I intend to replace tar with a custom unpack routine in cdebootstrap.

What would dpkg do with the symlinks in the the unpack phase?

fakechroot have to do this someway, but I was unable to grok that code
yet.

Rather hacky. Would mean to include mklibs-copy or similar
functionality.

This is a root only option.

Bastian

#514015#34
Date:
2009-02-05 18:02:12 UTC
From:
To:
This would only affect one part of the problem. But this bug is about
the problem that it will write outside of the new root if it encounters
symlinks not that they currently exist.

Bastian

#514015#39
Date:
2009-02-05 21:49:08 UTC
From:
To:
Hi,

thanks for your replies. Again, this is mostly because I needed some
idle occupation and am not into sudoku. Apologies for presenting it a
way that looked like a proposal.

Bdale Garbee wrote:

Well, having 1000 maintainers do the right thing is difficult, as proven
by the release process.

Other options do exist, my list probably is not exhaustive, but I would
think that they cost a lot of code with a lot of opportunity to add bugs
where things work now and it might be interesting to have an upper bound
for how involved a minimal fix is.

Thinking on a larger scale, what we actually want to do here is unpack a
tarball into a chroot. I venture that this is a reasonable use case of
tar and that it might just be possible to try to have upstream agree to
have such an option in principle so that there is a plan to get rid of
the debian-specificity of the option.

Bastian Blank wrote:

TBH, I'm not that impressed by the implementation of ar (or a subset) in
dpkg and am not sure that reimplementing tar extraction it is a good
path to take when I would think that an option for tar to do the right
thing (whether internally munging symlinks when resolving paths or using
chroot) might have a general use beyond bootstrapping Debian systems.

I have not tested it, but I would venture the same thing (calling tar,
ergo having the same problem) from when I last looked at it.

Well, you'd have to intercept all the relevant calls (for
dereferencing/reading symlinks). Seems doable, but IMO tar would be the
better place. But if the masses want funny business, so be it.

...

Yes. It was my impression that (c)debootstrap are supposed to be called
by root. At least I always used to call them as root when I was a developer.

Kind regards

T.

#514015#44
Date:
2009-02-06 08:57:36 UTC
From:
To:
dpkg does not call tar, it has it own tar implementation (which explains
why we don't support yet POSIX tar archives inside .deb). I'm not sure how
it behaves in the situation that matters for this bug.

Try it with dpkg --root=/target/dir/ …

Cheers,

#514015#49
Date:
2009-02-06 09:55:18 UTC
From:
To:
Thanks for stepping in to correct me here. dpkg does have its own tar, only dpkg-deb calls tar.

From looking at the code, it does the right thing.

Kind regards

T.

#514015#54
Date:
2009-02-07 16:18:10 UTC
From:
To:
No, it does not.

| $ dpkg-deb -c ../test.deb
| drwxr-x--- waldi/waldi       0 2009-02-07 17:09 ./
| drwxr-x--- waldi/waldi       0 2009-02-07 17:09 ./lib64/
| -rw-r----- waldi/waldi       5 2009-02-07 17:09 ./lib64/test
| $ l -ad lib*
| drwxr-x--- 2 waldi waldi 4096  7. Feb 17:09 lib/
| lrwxrwxrwx 1 waldi waldi    4  7. Feb 17:09 lib64 -> /lib/
| $ sudo dpkg --root . -i ../test.deb
| (Reading database ... 3 files and directories currently installed.)
| Preparing to replace test 1 (using ../test.deb) ...
| Unpacking replacement test ...
| Setting up test (1) ...
| $ l lib*/test /lib/test
| -rw-r----- 1 waldi waldi 5  7. Feb 17:09 lib64/test
| -rw-r----- 1 waldi waldi 5  7. Feb 17:09 /lib/test

Bastian

#514015#61
Date:
2009-09-22 19:24:22 UTC
From:
To:
* Bdale Garbee (bdale@gag.com) [090922 19:14]:

I don't see a case where a package needs a absolute symlink. If there
is no use case, it'd be easy to just stop such packages in dak from
entering the archive.


Cheers,
Andi

#514015#68
Date:
2009-12-06 15:08:13 UTC
From:
To:
Hi,

The attached patch makes cdebootstrap chroot to the target directory
before running tar, which should prevent it from following symlinks
out of the target directory.  I've verified that it works on i386.
I've tried to ensure that it will work on other architectures, but I
haven't tested it on any.

It requires that ldd be available, which may be a problem for
debian-installer.

#514015#73
Date:
2010-01-08 00:45:34 UTC
From:
To:
I doesn't. It calls /bin/sh within the chroot, which of course fails
when installing for a different architecture.

This is easily reproduced:

cdebootstrap -aarmel squeeze squeeze

#514015#78
Date:
2010-01-24 09:43:05 UTC
From:
To:
severity 514015 important
severity 514016 important
thanks

While there are many good arguments made here about why (c)debootstrap
should handle these symlinks safely, fundamentally, if you can install two
packages at the same time that disagree about whether a given path is a
symlink or a directory, there's a bug in those packages.  All co-installable
packages need to agree on symlinks and directories, otherwise installing a
package that uses a symlink, followed by installing a package that uses a
directory, then removing the package that shipped the symlink will leave
orphaned files on the filesystem that dpkg won't be able to find anymore.
And using relative symlinks instead of absolute symlinks won't fix this,
either.

So *that* bug should be treated as RC, wherever it occurs, but I don't think
we should treat this bug in *debootstrap as RC.

#514015#85
Date:
2017-11-16 18:41:47 UTC
From:
To:
This bug can ruin the host! Steps to reproduce.
* Host should be squeeze amd64. I used absolutely fresh squeeze with few packages. It have normal squeeze's debootstrap 1.0.26+squeeze1
* Run in it:
# debootstrap --variant=minbase squeeze /tmp/target http://archive.debian.org/debian
# debootstrap --variant=minbase wheezy /tmp/target http://deb.debian.org/debian
* The first debootstrap was OK, the second debootstrap stopped after this:
===
I: Extracting debianutils...
I: Extracting diffutils...
I: Extracting dpkg...
I: Extracting e2fslibs...
I: Extracting e2fsprogs...
I: Extracting libcomerr2...
I: Extracting libss2...
I: Extracting libc-bin...
I: Extracting libc6...
===
* After this point nearly any command doesn't work AT HOST!!! After this point the dynamic linker is not available. For example, /bin/true gives this: "bash: /bin/true: No such file or directory". "ldconfig" as root fixes the situation.

But this quiet possible the user simply would not guess he should type "ldconfig". Moreover, I think if he has no already opened root shell, he cannot open it (I think "sudo" will not work and I think attempting to log in in /dev/tty1 will not work, too). So, this is quiet possible the user will simply power off the computer. And then (I think) he will unable to boot the computer anymore. Yes, this is still possible to restore the computer (I think something like "linux /vmlinuz rw init=/sbin/ldconfig" from GRUB), but this is possible the user will not guess the right command. So he will simply reinstall OS.

So, this is absolutely critical bug, which can force average user to reinstalling OS. Moreover, in the time while the dynamic linker is missing, other running programs may experience data loss. So, this is critical data-loss bug!

You may say that this has little probability that someone will run debootstrap two times in same dir with different debian releases. Yes, this has little probability. But I run into this problem once. And this is not important to speak about probabilities when the consequences are such bad (I mean OS reinstalling).

The same bug is reproducible with squeeze's cdebootstrap (i. e. 0.5.7).

The same bug is reproducible with squeeze's dpkg-deb (dpkg 1.15.11).

Okey, how I run into this problem? Well, in fact I was developing my own debootstrap replacement. And I occasionally did run it in one directory two times. Then all commands stopped to work. Fortunately I had root shell opened and fortunately I was smart enough to type "ldconfig" into it. Then I checked that same problem applies not only my program, but also to original debootstrap.

Okey, how to reproduce with without risk of crashing your host? Well, let's assume you has host A with any OS. Create chroot environment B with squeeze using debootstrap. Then chroot into it, create dir C and run debootstrap two times on it as I described above. This will crash B, but not A.

It seems the bug is not reproducible when host is something newer than squeeze. So, I did not open separate bug report and posted my problem here. But the root cause of the issue is bugs #514015 / #514016, so these bugs (#514015 / #514016) should be fixed. It is possible that #514015 / #514016 for some reason will cause some another critical "reinstall this OS" bug.

One possible fix for my problem: check (in both debootstrap and cdebootstrap) that target is empty or non-existent.

Now some more info about my bug. This is part of file hierarchy after first debootstrap:

/lib
/lib/ld-2.11.3.so
/lib/ld-linux-x86-64.so.2 -> ld-2.11.3.so
/lib64 -> /lib
/tmp/target/lib
/tmp/target/lib/ld-2.11.3.so
/tmp/target/lib/ld-linux-x86-64.so.2 -> ld-2.11.3.so
/tmp/target/lib64 -> /lib

This is contents of libc6_2.13-38+deb7u10_amd64.deb from wheezy (it will be extracted during second debootstrap):

/lib
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 -> ld-2.11.3.so
/lib/x86_64-linux-gnu/ld-2.13.so
/lib64
/lib64/ld-linux-x86-64.so.2 -> /lib/x86_64-linux-gnu/ld-2.13.so

Now second debootstrap extracts libc6_2.13-38+deb7u10_amd64.deb into /tmp/target. Debootstrap tries to put package's "/lib64/ld-linux-x86-64.so.2" into system's "/tmp/target/lib64/ld-linux-x86-64.so.2", but "/tmp/target/lib64" is symlink to /lib, so debootstrap writes to host's /lib64/ld-linux-x86-64.so.2 . So, now host's /lib64/ld-linux-x86-64.so.2 is symlink and it points to file /lib/x86_64-linux-gnu/ld-2.13.so , which is non-existent on host. So, now host's dynamic linker name ( /lib64/ld-linux-x86-64.so.2 , hardcoded into nearly all dynamic binaries) is symlink to non-exist file.

==
Askar Safin
http://vk.com/safinaskar

#514015#90
Date:
2017-11-16 20:57:43 UTC
From:
To:
Also, FreeBSD's tar has option "--chroot". And (if I remember correctly) it is used in installation process. (Also, please see my previous letter in this bug report if you missed it.)


==
Askar Safin
http://vk.com/safinaskar

#514015#95
Date:
2017-11-17 12:27:00 UTC
From:
To:
And I still think that debootstrap and cdebootstrap should check whether target is empty as a safety measure in any case.

Also, I thought bug I reported still applies to modern hosts. If you have modern host (say, stretch) and perform double debootstrap (one squeeze and one wheezy) into same dir, this (as I thought) will write to host's /lib/ld-linux-x86-64.so.2 . Fortunately, this will not cause any bad consequences.

But now I performed tests. I used stretch host, stretch's debootstrap 1.0.89 and stretch's cdebootstrap 0.7.7+b1. And I see no bug. When that libc6 extracts during second (c)debootstrap, symlink /tmp/target/lib64 is replaced with regular dir /tmp/target/lib64 and then (c)debootstrap writes to this dir. So, there is no (even harmless) leak to host. Maybe tar changed?

So, it seems that the bug #514015/#514016 is fixed. But I am not sure, it is possible that my observation is due to special structure of Debian packages. What if some malformed untrusted Debian packages may still cause leaked files?

==
Askar Safin
http://vk.com/safinaskar