Windows 和 Linux 路径查找的差异
Windows 和 Linux 在查找命令路径的手段是完全不一样的,比如在 cmd 中执行“notepad”时,Windows 的搜索路径顺序是:当前目录 -> PATH 环境变量的目录列表。
实验1,一段 Process Monitor 捕获的记录:
18:34:23.7807382 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened (1) 18:34:23.7808239 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.* NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.* 18:34:23.7809035 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:34:23.7810305 cmd.exe 5628 CreateFile C:\Windows\System32 SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:34:23.7810729 cmd.exe 5628 QueryDirectory C:\Windows\System32\notepad.* SUCCESS FileInformationClass: FileFullDirectoryInformation, Filter: notepad.*, 2: notepad.exe 18:34:23.7811148 cmd.exe 5628 CloseFile C:\Windows\System32 SUCCESS 18:34:23.7812160 cmd.exe 5628 CreateFile C:\Windows\System32 SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:34:23.7812530 cmd.exe 5628 QueryDirectory C:\Windows\System32\notepad.COM NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.COM 18:34:23.7813641 cmd.exe 5628 CloseFile C:\Windows\System32 SUCCESS 18:34:23.7814800 cmd.exe 5628 CreateFile C:\Windows\System32 SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened (2) 18:34:23.7815201 cmd.exe 5628 QueryDirectory C:\Windows\System32\notepad.EXE SUCCESS FileInformationClass: FileFullDirectoryInformation, Filter: notepad.EXE, 2: notepad.exe
当前 cmd 工作目录在 C:\Users\lu4nx,执行 notepad:
- 由于没带扩展名,Windows 会自动匹配扩展名:
- 位置(1)中,先在当前目录用通配符(notepad.*)匹配,未匹配上,跳过;
- 在 C:\Windows\System32 中用通配符匹配,匹配到 C:\Windows\System32\notepad.EXE(见位置(2))。
如果当前目录中用通配符匹配上了呢?实验2,当前目录存在文件名 notepad.png 时:
18:58:00.8207071 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.* SUCCESS FileInformationClass: FileFullDirectoryInformation, Filter: notepad.*, 2: notepad.png 18:58:00.8207533 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8208713 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8209130 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.COM NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.COM 18:58:00.8209460 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8210505 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8210882 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.EXE NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.EXE 18:58:00.8211163 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8212405 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8212768 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.BAT NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.BAT 18:58:00.8213042 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8213997 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8214374 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.CMD NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.CMD 18:58:00.8214819 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8216048 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8216800 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.VBS NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.VBS 18:58:00.8217083 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8218071 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8218426 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.VBE NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.VBE 18:58:00.8218694 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8219637 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8219986 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.JS NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.JS 18:58:00.8220247 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8221176 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8221520 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.JSE NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.JSE 18:58:00.8221786 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8222737 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8223078 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.WSF NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.WSF 18:58:00.8223335 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8224279 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8224623 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.WSH NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.WSH 18:58:00.8224890 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS 18:58:00.8225823 cmd.exe 5628 CreateFile C:\Users\lu4nx SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened 18:58:00.8226454 cmd.exe 5628 QueryDirectory C:\Users\lu4nx\notepad.MSC NO SUCH FILE FileInformationClass: FileFullDirectoryInformation, Filter: notepad.MSC 18:58:00.8226775 cmd.exe 5628 CloseFile C:\Users\lu4nx SUCCESS
notepad.* 匹配成功之后,会补充后缀名来逐个判断,而这些后缀名都是固定的,如果手上有 XP 的源码,可以参考 base/cmd/cdata.c:
TCHAR PathExtDefaultStr[] = TEXT(".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS");
对于想深入寻找路径机制的,可以挂调试器调试 cmd.exe 的 SearchForExecutable 函数,也可以看泄露源码的 base/cmd/cext.c 中的该函数的具体实现。
Linux 中就没这么复杂了,比如执行 emacs 命令,shell 直接从 PATH 环境变量设定的路径逐个检查即可,具体实现也可以参考 bash 的源码 findcmd.c:
search_for_command (pathname, flags) const char *pathname; int flags; { char *hashed_file, *command, *pathlist; int temp_path, st; SHELL_VAR *path; /* ... 省略 ...*/ path = find_variable_tempenv ("PATH"); temp_path = path && tempvar_p (path); /* ... 省略 ...*/ }
两个系统在命令查找上有着截然不同的实现方式,Windows 优先查找当前目录、依赖扩展名和环境变量;而 Linux 只依赖环境变量,在未理解两系统差异的情况下容易写出不安全的代码,比如为了实现跨平台,有这么一段代码:
import os # ... 省略 ... os.system("update")
本意是利用系统自身机制,可以在 Linux 中执行 update 命令,在 Windows 中执行 update.exe;但因为 Windows 中当前目录的优先级高于 PATH 环境变量,而执行目录又是不可控的,容易导致恶意劫持,比如当前目录中恰好有一个 update.cmd 的恶意文件,update.cmd 会优先被执行。
最近 Go 语言就修复了一个类似漏洞(CVE-2021-3115,补丁参考:https://github.com/golang/go/commit/46e2e2e9d99925bbf724b12693c6d3e27a95d6a0) ,Windows 中 go(如 go get) 在调用 cgo 时没有验证 cgo 的路径,会导致命令劫持、远程 RCE。