-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Closed
Labels
area-System.IOquestionAnswer questions and provide assistance, not an issue with source code or documentation.Answer questions and provide assistance, not an issue with source code or documentation.
Milestone
Description
I have inspected the Windows and the Linux code and found both to be not armored against directory traversal switcheroo.
Exploit path:
- Create a folder in the root directory with a bunch of files and subdirectories
- Create a subdirectory with a large tree that mimics the contents of the directory you want the administrator to destroy
- Wait for the administrator to attempt the delete. This is best done with FileSystemWatcher
- Swap the subdirectory with a symbolic link to the target.
In the Linux world, the system calls openat() and unlinkat() were added to deal with this specific situation. The shortest correct sequence of system calls would be as follows:
int RemoveDirectoryTree(const char *path)
{
struct statbuf basestat;
int handle = open(path, 0));
fstat(handle, &basestat);
if (RecursiveDestroy(handle, &basestat)) { close(handle); return -1; }
close(handle);
return rmdir(path);
}
int RecursiveDestroy(int handle, struct statbuf *pstat)
{
struct old_linux_dirent de;
while (sys_readdir(handle, &de, 1)) {
again:
int h2 = openat(handle, de.de_name, 0)); /* bombs on certain kinds of nodes returning -1 but we don't have to care */
int openaterrno = (h2 >= 0) ? 0 : errno;
if (!(unlinkat(handle, de, 0)) {
if (errno == EISDIR) {
if (h2 < 0 && openaterrno == ENFILE) return -1; // Out of handles
if (h2 < 0) goto again; // node changed out from under us -- HANDLE IT
struct statbuf newstat;
fstat(handle, &newstat);
if (newstat.st_dev != pstat->st_dev) {
errno = EBUSY; // mount point
close(h2);
return -1;
}
if (newstat.st_ino != pstat->st_ino) {
close(h2);
goto again; // node changed out from under us -- HANDLE IT
}
if (RecursiveDestroy(handle, newstat)) { close(h2); return -1; }
} else {
close(h2);
return -1;
}
}
if (h2 >= 0) close(h2);
}
return 0;
}
There are equivalent calls in the NT Native API to do this on Windows. I have been unable to find any way at all to do this win Win32 calls.
So you might think you can just document to not do this, but Powershell on Linux uses .NET Core and .NET Framework on Windows has exactly the same bug.
Metadata
Metadata
Assignees
Labels
area-System.IOquestionAnswer questions and provide assistance, not an issue with source code or documentation.Answer questions and provide assistance, not an issue with source code or documentation.