Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLIUtils.generateArgline does not quote IFS characters other than space #298

Closed
charles-dyfis-net opened this issue Feb 5, 2013 · 7 comments

Comments

@charles-dyfis-net
Copy link
Contributor

Newlines and tabs can be injected into generated commands unquoted. This results in direct security issues, particularly if an untrusted data source is used for options.

Consider a tool wherein a user-specified option can be one of a limited number of values, but wherein additional arguments are used to form an argv array (not an uncommon design pattern):

(CLIUtils/generateArgline "run-tool"
  (into-array String ["-o" "safety-checked-arg\tunchecked-arg\tanother-unchecked-arg"]))

This generates the following command:

run-tool -o safety-checked-arg unchecked-arg another-unchecked-arg

...or:

(CLIUtils/generateArgline "run-tool"
  (into-array String ["safety-checked-arg'\r\nrm -rf /\r\n'"]))

...which generates:

run-tool 'safety-checked-arg'
rm -rf /
''
@charles-dyfis-net
Copy link
Contributor Author

This issue goes well beyond failing to quote non-space characters. We also don't quote quotes (so the user can, SQL-injection-style, remove themselves from same):

(CLIUtils/generateArgline "run-tool" (into-array String ["''$(rm -rf /)''"]))
run-tool ''$(rm -rf /)''

(Notably, paired single quotes cancel each other out, so ''foo'' is exactly equivalent to foo).

@charles-dyfis-net
Copy link
Contributor Author

(Not sure how the issue was closed, btw -- that looks like it was a PEBKAC).

@charles-dyfis-net
Copy link
Contributor Author

Another permutation of this issue, this one verified remotely exploitable: Glob characters are not expanded.

If * is specified as a parameter, rather than quoted and passed as a literal to the command being run, this is expanded to a list of matching files by the remote shell.

@charles-dyfis-net
Copy link
Contributor Author

One item which substantially complicates developing a satisfactory patch for this issue:

Replacing the current behavior of CLIUtils.testGenerateArgline() with completely faithful conversion from String[] to quoted argument list would break behavior which is currently validated by the unit test suite.

Consider the following example:

/* current test-suite code, from TestExecTool.testGenerateArgline() */
assertEquals("invalid",
   "test 1 2 \"34\"",
   CLIUtils.generateArgline("test", new String[]{"1", "2", "\"34\""}));

Since the input array contains literal quotes, an algorithm which faithfully represented its input array within its output would have different behavior:

/* proposed test validating more defensible behavior */
assertEquals("invalid",
   "test 1 2 '\"34\"'",
   CLIUtils.generateArgline("test", new String[]{"1", "2", "\"34\""}));

...thereby representing the exact user-provided input, quotes intact.

(Handling single quotes or mixed quotes gets a little more interesting -- but I have a working, tested implementation; it isn't submitted as a pull request only due to the conflict with preexisting semantics).

charles-dyfis-net added a commit to charles-dyfis-net/rundeck that referenced this issue Feb 5, 2013
This is the (very) beginning of a fix to rundeck#298.

Unfortunately, doing this in a way that doesn't compromise backwards
compatibility is out-of-scope, and will probably call for a multi-phase
implementation -- for instance, adding a per-job flag to specify
compatible or safe quoting, providing warnings and (eventually)
deprecation notices to users with jobs using unsafe quoting, and (again,
eventually) flipping the default.
@gschueler
Copy link
Member

Hi Charles,

In examining the usage of the CLIUtils, the code actually has a few different use-cases for it:

  1. allow conversion between list of strings to a single string and back
    • used to e.g. take CLI input from "dispatch" and pass it as a single string to the server
    • used to take a set of option->value pairs at job execution time, and store it as a single string in the DB, and later convert back to key/values
  2. generate the argument list to pass to shell commands, from input which is a single string with embedded property references

In addition, I'm not even sure what kind of quoting is necessary to solve the same potential problem in a Windows environment.

We need to refactor the usage of CLIUtils a bit more to solve shell quoting issues and remove conflation between its use-cases.

So I propose we do something like this:

  • move the "safe" quoting to a Unix-shell specific class for generating the exec arguments
    • use this class for quoting arguments to execute on unix nodes
  • move/revamp the CLIUtils for the conflated "argLine" split and generate which is used internally by rundeck
  • either keep the status-quo, or enhance the argLine split/generate to be better.

thoughts?

also, thanks for bringing this to light and kicking off the beginning of a fix :)

@charles-dyfis-net
Copy link
Contributor Author

In an ideal world, we'd enhance the argLine split/generate to be able to unambiguously represent any valid C string (ie. anything not containing a NUL) as an argument. Not implementing this restricts the user without any obvious benefit.

However -- that's not a trivial amount of work. I'm not familiar with any existing parser for either POSIX sh or zsh command lines implemented for Java, certainly not enough to vouch for completeness or safety. (Python has its excellent shlex module, but it's not trivial to port). [Aside: zsh was mentioned because its semantics are closer to those of rundeck's argLine format as documented and used than those of POSIX sh; in zsh, parameter expansion results are not string-split or glob-expanded, so ${foo} will always become exactly one parameter, rather than an unknown number depending on its contents].

@gschueler
Copy link
Member

need to add documentation about quoting arguments in exec strings

gschueler added a commit to gschueler/rundeck that referenced this issue Apr 29, 2013
For rundeck#298

Arguments to script/script-file/script-url were not being quoted.

Additionally, arguments with spaces were not being quoted.

If arguments have spaces even if not containing expansion variables then
they are quoted.
gschueler added a commit to gschueler/rundeck that referenced this issue Apr 29, 2013
* describe quoting rules for parsing command string and script args.
* describe escaping for option values in commands and args
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants