execute external program in lua without userinput as arguments in lua
up vote
1
down vote
favorite
I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' '
and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var'
as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']]
– so nested strings – and program
parses "$My Title$"
(with escape sequences) differently than '$My Title$'
(as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'
. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My
will be replaced with an empty string, because the environment does not know any variable named $My
and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
security lua user-input
add a comment |
up vote
1
down vote
favorite
I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' '
and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var'
as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']]
– so nested strings – and program
parses "$My Title$"
(with escape sequences) differently than '$My Title$'
(as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'
. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My
will be replaced with an empty string, because the environment does not know any variable named $My
and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
security lua user-input
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' '
and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var'
as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']]
– so nested strings – and program
parses "$My Title$"
(with escape sequences) differently than '$My Title$'
(as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'
. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My
will be replaced with an empty string, because the environment does not know any variable named $My
and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
security lua user-input
I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' '
and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var'
as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']]
– so nested strings – and program
parses "$My Title$"
(with escape sequences) differently than '$My Title$'
(as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'
. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My
will be replaced with an empty string, because the environment does not know any variable named $My
and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
security lua user-input
security lua user-input
asked Nov 10 at 23:30
sivizius
341112
341112
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\]', '\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '''arg 1''' '''$arg2''' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2
will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?"
will NOT be expanded too (unless you explicitly set expand=true
).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '''set title '''''''$My Title$'''''' arg1 arg2'
e.g.execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute/bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error:set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd
– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between/bin/bash
and-c
? My code can't produce such output.
– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the-c
is indeed after/bin/bash
. and this was with the old version of your code
– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\]', '\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '''arg 1''' '''$arg2''' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2
will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?"
will NOT be expanded too (unless you explicitly set expand=true
).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '''set title '''''''$My Title$'''''' arg1 arg2'
e.g.execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute/bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error:set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd
– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between/bin/bash
and-c
? My code can't produce such output.
– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the-c
is indeed after/bin/bash
. and this was with the old version of your code
– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
add a comment |
up vote
2
down vote
accepted
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\]', '\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '''arg 1''' '''$arg2''' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2
will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?"
will NOT be expanded too (unless you explicitly set expand=true
).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '''set title '''''''$My Title$'''''' arg1 arg2'
e.g.execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute/bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error:set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd
– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between/bin/bash
and-c
? My code can't produce such output.
– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the-c
is indeed after/bin/bash
. and this was with the old version of your code
– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\]', '\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '''arg 1''' '''$arg2''' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2
will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?"
will NOT be expanded too (unless you explicitly set expand=true
).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '''set title '''''''$My Title$'''''' arg1 arg2'
local safe_unquoted = "^[-~_/.%w%%+,:@^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\]', '\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '''arg 1''' '''$arg2''' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2
will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?"
will NOT be expanded too (unless you explicitly set expand=true
).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '''set title '''''''$My Title$'''''' arg1 arg2'
edited Nov 11 at 16:51
sivizius
341112
341112
answered Nov 11 at 7:43
Egor Skriptunoff
17k12453
17k12453
e.g.execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute/bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error:set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd
– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between/bin/bash
and-c
? My code can't produce such output.
– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the-c
is indeed after/bin/bash
. and this was with the old version of your code
– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
add a comment |
e.g.execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute/bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error:set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd
– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between/bin/bash
and-c
? My code can't produce such output.
– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the-c
is indeed after/bin/bash
. and this was with the old version of your code
– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
e.g.
execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute /bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error: set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd– sivizius
Nov 11 at 11:52
e.g.
execute_commands({"echo", [[set my '$program$'; cd "finished";]]})
will execute /bin/bash 'echo '\''set my '\''\'\'''\''$program$'\''\'\'''\''; cd "finished";'\'''-c
which result in this error: set sh: 1: cd: can't cd to finished sh: 1: \: not found
; it was never intended to execute cd– sivizius
Nov 11 at 11:52
@sivizius - How a text appears inserted between
/bin/bash
and -c
? My code can't produce such output.– Egor Skriptunoff
Nov 11 at 12:23
@sivizius - How a text appears inserted between
/bin/bash
and -c
? My code can't produce such output.– Egor Skriptunoff
Nov 11 at 12:23
just copy-paste fail, the
-c
is indeed after /bin/bash
. and this was with the old version of your code– sivizius
Nov 11 at 12:25
just copy-paste fail, the
-c
is indeed after /bin/bash
. and this was with the old version of your code– sivizius
Nov 11 at 12:25
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
This solution works, but I do not like it. I am used to have a real argument list. This solution adds some layers just to removed again by bash later. I still have some security concerns with this. But thank you, at least, this helps somehow.
– sivizius
Nov 11 at 12:47
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
@sivizius - Can you give an example of "security concerns" with this solution?
– Egor Skriptunoff
Nov 12 at 7:21
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53244449%2fexecute-external-program-in-lua-without-userinput-as-arguments-in-lua%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown