[AMG]: An injection attack is the substitution of executable code into an expression. A system is vulnerable to injection attacks when it reparses substitution results. Tcl normally does not reparse substitution results, but several Tcl commands internally perform parsing and substitution on their arguments, after Tcl has already parsed and substituted them, so in effect there is [double substitution]. Injection attacks often allow users to execute arbitrary code in a privileged context. Here's an example value of $exploit that can be used in the following code snippets. Don't use it; typing this in is playing with fire! Executing it is suicidal, so let's be safe by not even typing it. ====== set exploit {[exec rm -rf /]} ====== Here's a safer version for testing: ====== set exploit {[error PWNED!]} ====== **[after]** ====== after 0 puts $exploit ;# vulnerable after 0 "puts $exploit" ;# vulnerable after 0 {puts $exploit} ;# safe, but relies on $exploit being available later after 0 [list puts $exploit] ;# safe, records current value of $exploit ====== The first two cases are practically the same. The only difference is when the concatenation happens. In the first case, [[after]] [concat]s its last two arguments to produce the script [http://www.tcl.tk/man/tcl8.6/TclCmd/after.htm#M6]. In the second case, Tcl concats "puts" and the value of "$exploit" to produce the script, then passes it as a single argument to [[after]]. The first two cases are vulnerable to injection attacks because of the double substitution. $exploit is substituted before being passed to [[after]], so [[after]]'s argument is "puts [[exec rm -rf /]]" or whatever. This argument is later executed as a script, at which time it is substituted a second time. As a side effect, all your files get deleted. The third case is safe because substitution only happens once. "puts $exploit" is passed verbatim to [[after]], with no substitution. [[after]] later executes it, and the single round of substitution produces the script "puts {[[exec rm -rf /]]}" which is in fact harmless. The fourth case uses [list] to insert proper quoting of $exploit. **[apply]** ====== apply "{exploit} {puts $exploit}" $exploit ;# vulnerable apply {{exploit} {puts $exploit}} $exploit ;# safe ====== **[catch]** ====== catch "puts $exploit" ;# vulnerable catch {puts $exploit} ;# safe ====== **[dict filter]** ====== dict filter {a 1} script {k v} "puts $exploit; lindex 1" ;# vulnerable dict filter {a 1} script {k v} {puts $exploit; lindex 1} ;# safe ====== **[dict for]** ====== dict for {k v} {a 1} "puts $exploit" ;# vulnerable dict for {k v} {a 1} {puts $exploit} ;# safe ====== **[eval]** ====== eval puts $exploit ;# vulnerable eval "puts $exploit" ;# vulnerable eval {puts $exploit} ;# safe ====== **[expr]** ====== expr 2 + 2 == $exploit ;# vulnerable expr "2 + 2 == $exploit" ;# vulnerable expr {2 + 2 == $exploit} ;# safe ====== Always [brace your expr-essions]!! **[subst]** ====== subst "this is vulnerable to the $exploit" subst {this is immune to the $exploit} ====== The -novariables and -nobackslashes options can't be used to completely deny access to variables and backslash substitutions if -nocommands is not given. (Passing all three options means that [[subst]] will simply return its argument, same as single-argument [[[lindex]]].) This is because command substitution (script substitution) is allowed to internally use variables and backslashes. **[switch]** Here's a different kind of attack, where a user-supplied value gets interpreted as an option, resulting in an error. Depending on how the program is written, the error may in turn create other problems, such as denial of service . ====== set exploit -crash switch $exploit A {puts "got A"} default {puts "didn't get A"} ;# vulnerable switch $exploit {A {puts "got A"} default {puts "didn't get A"}} ;# safe switch -- $exploit A {puts "got A"} default {puts "didn't get A"} ;# safe switch -- $exploit {A {puts "got A"} default {puts "didn't get A"}} ;# safe ====== Always using -- is the recommended solution. [RS] calls it "the switch to end all switches"; see ['--' in Tcl]. :^) However, it's not required when using switch's single body form, wherein all the cases are grouped into a single word [http://www.tcl.tk/man/tcl8.6/TclCmd/switch.htm#M11]. This is because this form of switch always treats the second-to-last argument as the match word. Hmm, I think I might stop using -- since it's not really necessary... ''more to come...'' **Others** Here's a very famous injection attack: [http://imgs.xkcd.com/comics/exploits_of_a_mom.png] [http://xkcd.com/327/] Sanitizing database inputs is more of a band-aid than anything; it doesn't address the root of the problem, which is reparsing substitution results. Instead use prepared statements. This page has many non-[SQL] examples of what are essentially prepared statements, for example "`expr {2 + 2 == $exploit}`". Here, the "statement" (math expression) is a single word which gets [bytecode]-compiled (prepared), and [expr] always gets the same argument into which it performs variable substitution. [DKF]: If you're feeling paranoid about this sort of thing, you'll also use a [safe interpreter] and only expose exactly those operations that you wish to support via [alias]es (e.g., no general database queries, but rather just prepared queries where ''you'' control the SQL and the attacker can only specify values).