Most notably, Rust released a patch version 1.77.2 just for the fix of this security issue and published a security advisory describing the problem. For reference, here's the diff between Rust 1.77.1 and 1.77.2, which shows how they fixed it: https://github.com/rust-lang/rust/compare/1.77.1...1.77.2
But it's not specific to Rust. It's more of a general problem on Windows that is not very well known. The problem is that batch files (.bat or .cmd) are executed via cmd.exe, which has different escaping rules than most programs, and this distinction is apparently often overlooked by standard library implementations in many programming languages. As a result, executing batch files via these standard APIs from the particular programming language and passing arguments originating from user input has been found to be often unsafe, because these APIs will fail to escape some special characters properly for cmd.exe and thus there's a good chance that the user can achieve command injection.
I did a quick test to check if KSV is affected (because I knew that kaitai-struct-compiler uses a .bat launcher on Windows). For testing purposes, I added a debug print of ARGVhere first, so that it's clear that the improper escaping we'll see really occurs inside KSV, not in the shell:
--- i/bin/ksdump
+++ w/bin/ksdump
@@ -70,6 +70,8 @@ if ARGV.size < 2
exit 1
end
+pp ARGV
+
c = Kaitai::Struct::Visualizer::KSYCompiler.new(options)
app = Kaitai::Struct::Visualizer::Parser.new(c, ARGV[0], ARGV[1..-1], options)
exc = app.load
We can also add some more debug prints in ksy_compiler.rb:
--- i/lib/kaitai/struct/visualizer/ksy_compiler.rb
+++ w/lib/kaitai/struct/visualizer/ksy_compiler.rb
@@ -83,6 +83,7 @@ module Kaitai::Struct::Visualizer
# @return [String] Main class name, or nil if were errors
def compile_and_load(fns, code_dir)
log = compile_formats_to_output(fns, code_dir)
+ pp log
load_ruby_files(fns, code_dir, log)
end
If you run this in a cmd.exe session (obviously, you need ruby and kaitai-struct-compiler in PATH), the Calculator app will appear, so we indeed have command injection:
C:\temp\kaitai_struct\visualizer>ruby bin\ksdump sample.bin 'hello" & calc.exe .ksy'
["sample.bin", "hello\" & calc.exe .ksy"]
["--ksc-json-output", "--debug", "-t", "ruby", "-d", "C:/Users/pp/AppData/Local/Temp/d20240411-20136-nb8x10", "hello\" & calc.exe .ksy"]
{"hello\""=>{"errors"=>[{"file"=>"hello\"", "path"=>[], "message"=>"hello\" (The filename, directory name, or volume label syntax is incorrect)"}]}}
C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:151:in `block in load_ruby_files': undefined method `[]' for nil:NilClass (NoMethodError)
if log_fn['errors']
^^^^^^^^^^
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:149:in `each'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:149:in `each_with_index'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:149:in `load_ruby_files'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:87:in `compile_and_load'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:63:in `block in compile_formats'
from C:/Ruby32-x64/lib/ruby/gems/3.2.0/gems/tmpdir-0.2.0/lib/tmpdir.rb:99:in `mktmpdir'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:63:in `compile_formats'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/ksy_compiler.rb:35:in `compile_formats_if'
from C:/temp/kaitai_struct/visualizer/lib/kaitai/struct/visualizer/parser.rb:25:in `load'
from bin/ksdump:77:in `<main>'
Note that KSC clearly only received hello" as the file name - the & calc.exe .ksy part was interpreted by cmd.exe, even though the pp args debug print proves that the Open3.capture3 Ruby function was called with expected arguments.
If we invoke KSC directly, no command injection occurs (so the problem is not in the .bat launcher, for example):
After checking the Ruby docs, it seems that Open3.capture3 simply isn't designed to handle any escaping or sanitization at all. Following the links in the documentation starting from Open3.capture3, we get to Kernel#system, which has even a warning about command injection:
This method has potential security vulnerabilities if called with untrusted input; see Command Injection.
So we should either use a different function (I don't know if there is a safe alternative with escaping in the Ruby standard library), or do the escaping/sanitization ourselves.
Recently (at the time of writing in April 2024) there has been a report about improper escaping of command-line arguments on Windows when executing batch files affecting many programming languages: https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
Most notably, Rust released a patch version 1.77.2 just for the fix of this security issue and published a security advisory describing the problem. For reference, here's the diff between Rust 1.77.1 and 1.77.2, which shows how they fixed it: https://github.com/rust-lang/rust/compare/1.77.1...1.77.2
But it's not specific to Rust. It's more of a general problem on Windows that is not very well known. The problem is that batch files (
.bat
or.cmd
) are executed viacmd.exe
, which has different escaping rules than most programs, and this distinction is apparently often overlooked by standard library implementations in many programming languages. As a result, executing batch files via these standard APIs from the particular programming language and passing arguments originating from user input has been found to be often unsafe, because these APIs will fail to escape some special characters properly forcmd.exe
and thus there's a good chance that the user can achieve command injection.I did a quick test to check if KSV is affected (because I knew that
kaitai-struct-compiler
uses a.bat
launcher on Windows). For testing purposes, I added a debug print ofARGV
here first, so that it's clear that the improper escaping we'll see really occurs inside KSV, not in the shell:We can also add some more debug prints in
ksy_compiler.rb
:before the invocation of KSC
after the invocation of KSC to print the JSON output
If you run this in a
cmd.exe
session (obviously, you needruby
andkaitai-struct-compiler
in PATH), the Calculator app will appear, so we indeed have command injection:Note that KSC clearly only received
hello"
as the file name - the& calc.exe .ksy
part was interpreted bycmd.exe
, even though thepp args
debug print proves that theOpen3.capture3
Ruby function was called with expected arguments.If we invoke KSC directly, no command injection occurs (so the problem is not in the
.bat
launcher, for example):After checking the Ruby docs, it seems that
Open3.capture3
simply isn't designed to handle any escaping or sanitization at all. Following the links in the documentation starting fromOpen3.capture3
, we get toKernel#system
, which has even a warning about command injection:Open3.capture3
:Open3.popen3
:Process.spawn
:Kernel#system
:So we should either use a different function (I don't know if there is a safe alternative with escaping in the Ruby standard library), or do the escaping/sanitization ourselves.