[New-bugs-announce] [issue15100] Race conditions in shutil.copy, shutil.copy2 and shutil.copyfile

Radoslaw A. Zarzynski report at bugs.python.org
Mon Jun 18 13:57:48 CEST 2012


New submission from Radoslaw A. Zarzynski <radoslaw.zarzynski at student.put.poznan.pl>:

shutil.copy and shutil.copy2 first copy a file content and afterwards
change permissions of a destination file. Unfortunately, the sequence isn't atomical and may lead to disclosure of matter of any file that is being duplicated.
            
Additionally, shutil.copyfile procedure seems to have a problem with symlinks that could result in the corruption of content of any file on filesystem (in favorable conditions).

Some functions from shutil module that depend on copy[2] (and thus copyfile) are vulnerable too.
For example, shutil.move is using copy2 when os.rename fails because of file transfer between filesystems.

I have attached listing from strace(1) system utility below that illustrates the disclosure problem.

# ls -l ./shutil_test
-r-------- 1 root root 10 06-18 11:42 shutil_test

# strace -- python -c "import shutil; shutil.move('./shutil_test', '/tmp')"
<many irrelevant lines>
open("./shutil_test", O_RDONLY)         = 3
fstat(3, {st_mode=S_IFREG|0400, st_size=10, ...}) = 0
open("/tmp/shutil_test", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
fstat(3, {st_mode=S_IFREG|0400, st_size=10, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd82e13e000
read(3, "blablabla\n", 16384)           = 10
read(3, "", 12288)                      = 0
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd82e13d000
read(3, "", 16384)                      = 0
write(4, "blablabla\n", 10)             = 10
close(4)                                = 0
munmap(0x7fd82e13d000, 4096)            = 0
close(3)                                = 0
munmap(0x7fd82e13e000, 4096)            = 0
stat("./shutil_test", {st_mode=S_IFREG|0400, st_size=10, ...}) = 0
utimes("/tmp/shutil_test", {{1340012952, 0}, {1340012539, 0}}) = 0
chmod("/tmp/shutil_test", 0400)         = 0

Quick fix for the first issue could rely on os.umask but much more elegant and composite solution might use combination of os.open, os.fchmod and os.fdopen instead of open(dst, 'wb') in shutil.copyfile procedure which additionally rectifies the problem with symlink attack.
However, I am not sure that the last one is portable and won't mess with another code.
I have prepared *untested* patches for both propositions.

Best regards,
Radoslaw A. Zarzynski

----------
components: Library (Lib)
files: python_shutil_copyfile.diff
keywords: patch
messages: 163096
nosy: radoslaw.zarzynski
priority: normal
severity: normal
status: open
title: Race conditions in shutil.copy, shutil.copy2 and shutil.copyfile
type: security
versions: Python 2.7, Python 3.2
Added file: http://bugs.python.org/file26042/python_shutil_copyfile.diff

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue15100>
_______________________________________


More information about the New-bugs-announce mailing list