This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author radoslaw.zarzynski
Recipients radoslaw.zarzynski
Date 2012-06-18.11:57:45
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1340020668.92.0.801725909503.issue15100@psf.upfronthosting.co.za>
In-reply-to
Content
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
History
Date User Action Args
2012-06-18 11:57:49radoslaw.zarzynskisetrecipients: + radoslaw.zarzynski
2012-06-18 11:57:48radoslaw.zarzynskisetmessageid: <1340020668.92.0.801725909503.issue15100@psf.upfronthosting.co.za>
2012-06-18 11:57:48radoslaw.zarzynskilinkissue15100 messages
2012-06-18 11:57:46radoslaw.zarzynskicreate