Skip to content

Commit 63014d3

Browse files
squeek502alexrp
authored andcommitted
Add test to ensure the BatBadBut mitigation handles trailing . and space safely
Context: - https://blog.rust-lang.org/2024/09/04/cve-2024-43402.html - rust-lang/rust#129962 Note that the Rust test case for this checks that it executes the batch file successfully with the proper mitigation in place, while the Zig test case expects a FileNotFound error. This is because of a PATHEXT optimization that Zig does, and that Rust doesn't do because Rust doesn't do PATHEXT appending (it only appends .exe specifically). See the added comment for more details.
1 parent 2210c4c commit 63014d3

File tree

1 file changed

+25
-0
lines changed
  • test/standalone/windows_bat_args

1 file changed

+25
-0
lines changed

test/standalone/windows_bat_args/test.zig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ pub fn main() anyerror!void {
7373
try testExec(allocator, &.{ "\"hello^\"world\"", "hello &echo oh no >file.txt" }, null);
7474
try testExec(allocator, &.{"&whoami.exe"}, null);
7575

76+
// Ensure that trailing space and . characters can't lead to unexpected bat/cmd script execution.
77+
// In many Windows APIs (including CreateProcess), trailing space and . characters are stripped
78+
// from paths, so if a path with trailing . and space character(s) is passed directly to
79+
// CreateProcess, then it could end up executing a batch/cmd script that naive extension detection
80+
// would not flag as .bat/.cmd.
81+
//
82+
// Note that we expect an error here, though, which *is* a valid mitigation, but also an implementation detail.
83+
// This error is caused by the use of a wildcard with NtQueryDirectoryFile to optimize PATHEXT searching. That is,
84+
// the trailing characters in the app name will lead to a FileNotFound error as the wildcard-appended path will not
85+
// match any real paths on the filesystem (e.g. `foo.bat .. *` will not match `foo.bat`; only `foo.bat*` will).
86+
//
87+
// This being an error matches the behavior of running a command via the command line of cmd.exe, too:
88+
//
89+
// > "args1.bat .. "
90+
// '"args1.bat .. "' is not recognized as an internal or external command,
91+
// operable program or batch file.
92+
try std.testing.expectError(error.FileNotFound, testExecBat(allocator, "args1.bat .. ", &.{"abc"}, null));
93+
const absolute_with_trailing = blk: {
94+
const absolute_path = try std.fs.realpathAlloc(allocator, "args1.bat");
95+
defer allocator.free(absolute_path);
96+
break :blk try std.mem.concat(allocator, u8, &.{ absolute_path, " .. " });
97+
};
98+
defer allocator.free(absolute_with_trailing);
99+
try std.testing.expectError(error.FileNotFound, testExecBat(allocator, absolute_with_trailing, &.{"abc"}, null));
100+
76101
var env = env: {
77102
var env = try std.process.getEnvMap(allocator);
78103
errdefer env.deinit();

0 commit comments

Comments
 (0)