Sometimes, as a developer you need to execute commands in the OS. How can you achieve that in Erlang and Elixir, and how can you do it securely?

Here you will find information that may be of interest both from a development and an attack points of view.

OS command execution

os module

The simplest way to execute something in the OS is to use the cmd function.

os:cmd("cat /etc/passwd").

The response will be a string from stdout or stderr. There is no separation between commands and arguments.

The insecure analog in Elixir is :os.cmd/1,2

:os.cmd(:"cat /etc/passwd")

open_port of erlang module


Command = "ls > ls.results",
PortSettings = [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof, binary],
Port = open_port({spawn, Command}, PortSettings).

open_port with spawn starts an external program specified in a tuple.

For external programs, PATH is searched (or an equivalent method is used to find programs, depending on the OS). This is done by invoking the shell on certain platforms. The first space-separated token of the command is considered as the name of the executable (or driver). This makes this option unsuitable for running programs with spaces in filenames or directory names.

The insecure analog in Elixir is with :spawn as a first parameter.{:spawn, "cat /etc/passwd"}, [:binary])


Command = "ls",
PortSettings = [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof, binary],
Port = open_port({spawn_executable, Command}, PortSettings).

{spawn_executable, FileName} works like {spawn, FileName}, but only runs external executables. FileName in its whole is used as the name of the executable, including any spaces. If arguments are to be passed, the PortSettings args can be used.

The shell is usually not invoked to start the program, it is executed directly. PATH (or equivalent) is not searched. To find a program in PATH to execute, use os:find_executable/1.

The secure analog in Elixir is with :spawn_executable as a first parameter, and any arguments passed using the :args option.

path = System.find_executable("cat")
port ={:spawn_executable, path}, [:binary, args: ["/etc/passwd"]])

Also in Elixir, you can use System.cmd/2,3:

System.cmd("cat", ["/etc/passwd"])


It works like {spawn, Command}, but demands the first (space-separated) token of the command to be the name of a loaded driver. If no driver with that name is loaded, a badarg error is raised.

Code execution

erl_eval module

The erl_eval module functions allow you to execute Erlang code. You have to use expressions with functions mentioned above to execute code in OS.

The following are two expressions that execute ls in the operating system.

Expression = {call,1,{remote,1,{atom,1,os},{atom,1,cmd}},[{string,1,"ls"}]}.

{ok, Tokens, _} = erl_scan:string("os:cmd(\"ls\")."),
{ok, Expressions} = erl_parse:parse_exprs(Tokens).


erl_eval:expr(Expression, []).


erl_eval:exprs(Expressions, []).


erl_eval:expr_list(Expressions, []).

Similar Elixir functions in the Code module are :eval_string, :eval_file, and :eval_quoted.

Code.eval_string(":os.cmd(:\"cat /etc/passwd\")", [])

There are also :eval_string and :eval_file in Embedded Elixir.