diff options
-rw-r--r-- | doc/security.mdwn | 20 | ||||
-rwxr-xr-x | ikiwiki | 10 |
2 files changed, 27 insertions, 3 deletions
diff --git a/doc/security.mdwn b/doc/security.mdwn index 63d140ec5..c7a6fcd69 100644 --- a/doc/security.mdwn +++ b/doc/security.mdwn @@ -141,6 +141,22 @@ into the repo. ikiwiki uses File::Find to traverse the repo, and does not tell it to follow symlinks, but it might be possible to race replacing a directory with a symlink and trick it into following the link. -Also, if someone checks in a symlink to /etc/passwd, ikiwiki would read and publish that, which could be used to expose files a committer otherwise wouldn't see. +Also, if someone checks in a symlink to /etc/passwd, ikiwiki would read and +publish that, which could be used to expose files a committer otherwise +wouldn't see. -To avoid this, ikiwiki will avoid reading files that are symlinks, and uses locking to prevent more than one instance running at a time. The lock prevents one ikiwiki from running a svn up at the wrong time to race another ikiwiki. So only attackers who can write to the working copy on their own can race it. +To avoid this, ikiwiki will skip over symlinks when scanning for pages, and +uses locking to prevent more than one instance running at a time. The lock +prevents one ikiwiki from running a svn up at the wrong time to race +another ikiwiki. So only attackers who can write to the working copy on +their own can race it. + +## symlink + cgi attacks + +Similarly, a svn commit of a symlink could be made, ikiwiki ignores it +because of the above, but the symlink is still there, and then you edit the +page from the web, which follows the symlink when reading the page, and +again when saving the changed page. + +This was fixed by making ikiwiki refuse to read or write to files that are +symlinks, combined with the above locking. @@ -152,6 +152,10 @@ sub htmlpage ($) { #{{{ sub readfile ($) { #{{{ my $file=shift; + if (-l $file) { + error("cannot read a symlink ($file)"); + } + local $/=undef; open (IN, "$file") || error("failed to read $file: $!"); my $ret=<IN>; @@ -162,6 +166,10 @@ sub readfile ($) { #{{{ sub writefile ($$) { #{{{ my $file=shift; my $content=shift; + + if (-l $file) { + error("cannot write to a symlink ($file)"); + } my $dir=dirname($file); if (! -d $dir) { @@ -1334,7 +1342,7 @@ sub cgi_editpage ($$) { #{{{ ! length $form->field('content')) { my $content=""; if (exists $pagesources{lc($page)}) { - $content=readfile("$config{srcdir}/$pagesources{lc($page)}"); + $content=readfile("$config{srcdir}/$pagesources{lc($page)}"); $content=~s/\n/\r\n/g; } $form->field(name => "content", value => $content, |