[erlang-questions] 'ssh' security issue

OvermindDL1 <>
Tue Dec 10 23:26:54 CET 2013


Be calling the option with Exec added to it with a fixed
handle_ssh_option test to test for if a fun as well.

On Tue, Dec 10, 2013 at 3:15 PM, OvermindDL1 <> wrote:
> Nevermind, and for future documentation I found this in ssh_cli.erl:
> ```
> start_shell(_ConnectionHandler, Cmd, #state{exec={M, F, A}} = State) ->
>     Group = group:start(self(), {M, F, A++[Cmd]}, [{echo, false}]),
>     State#state{group = Group, buf = empty_buf()};
> start_shell(ConnectionHandler, Cmd, #state{exec=Shell} = State) when
> is_function(Shell) ->
>
>     ConnectionInfo = ssh_connection_handler:info(ConnectionHandler,
>                                                  [peer, user]),
>     {ok, User} =
>         proplists:get_value(user, ConnectionInfo),
>     ShellFun =
>         case erlang:fun_info(Shell, arity) of
>             {arity, 1} ->
>                 fun() -> Shell(Cmd) end;
>             {arity, 2} ->
>                 fun() -> Shell(Cmd, User) end;
>             {arity, 3} ->
>                 [{_, PeerAddr}] =
>                     proplists:get_value(peer, ConnectionInfo),
>                 fun() -> Shell(Cmd, User, PeerAddr) end;
>             _ ->
>                 Shell
>         end,
>     Echo = get_echo(State#state.pty),
>     Group = group:start(self(), ShellFun, [{echo,Echo}]),
>     State#state{group = Group, buf = empty_buf()}.
> ```
> The exec property of the state record is set on init, so I figured I
> could just pass in a fun instead like I can the shell option:
> ```
> {error,{eoptions,{exec,#Fun<msw_ssh_server.2.72054487>}}
> ```
> Nope.  But yet it looks like it should accept a fun fine, so... what
> is going on.
> I take a look at where ssh_cli is called and found this in
> ssh_connection_handler.erl:
> ```
>     Shell = proplists:get_value(shell, Opts),
>     Exec = proplists:get_value(exec, Opts),
>     CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell]}),
>     State#state{starter = Pid, connection_state = Connection#connection{
>                                                     cli_spec = CliSpec,
>                                                     exec = Exec,
>                                                     system_supervisor
> = SystemSup,
>
> sub_system_supervisor = SubSystemSup,
>
> connection_supervisor = ConnectionSup
>                                                    }}.
> ```
> So, it seems the exec is set here as an option to the connection
> state, and the exec is filtered on this in ssh.erl:
> ```
>
> handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module),
>                                                             is_atom(Function) ->
>
>     Opt;
> ```
> And that looks like it should work, but wait, remember what I said
> about exec only being set on init in ssh_cli.erl, and look at the line
> above that sets the default ssh_cli option:
> ```
>     CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell]}),
> ```
> Hmm, so it never sets the exec option in the ssh_cli, if it did then
> it should actually be calling:
> ```
>     CliSpec = proplists:get_value(ssh_cli, Opts, {ssh_cli, [Shell, Exec]}),
> ```
> Yet it is not.  So I removed my shell and exec options from ssh:daemon
> and replaced it with a single option of:
> ```
>     {ssh_cli, {ssh_cli, [
>         fun(User, PeerAddr) -> msw_ssh_server_shell:start(User, PeerAddr) end,
>         fun(Cmd, User, PeerAddr) -> msw_ssh_server_shell:exec(Cmd,
> User, PeerAddr) end]}},
> ```
> And badda bing it works.  I am not sure if this is the 'proper' way to
> do it (and I would love to learn of a proper way if it exists), but it
> works with both setting a shell, exec, and getting the User and
> PeerAddress in both cases as well.
>
> On Tue, Dec 10, 2013 at 2:26 PM, OvermindDL1 <> wrote:
>> Another question while I am at it with this exec option, the 'shell'
>> option gets a User and PeerAddr passed in, any way to do that with
>> exec?  If I can at least get the User then I can allow them to issue
>> one-off commands this way too, which would be quite useful for remote
>> scripting.
>>
>> On Tue, Dec 10, 2013 at 2:22 PM, OvermindDL1 <> wrote:
>>> Ah all you are amazing, I never saw that option in the docs indeed and
>>> looks like it should do what I want.  Thanks much for all the help!
>>>
>>> On Tue, Dec 10, 2013 at 10:20 AM, Jakob Cederlund <> wrote:
>>>> Actually, the sample cli module works quite all right. The problem is that
>>>> the default implementation in ssh_cli for the "exec" thing in ssh is
>>>> actually to execute it (using erl_scan and erl_eval and stuff). There is an
>>>> undocumented option to ssh (actually to the ssh_cli module) that can be used
>>>> to customize this. The option {exec, {M, F, []}} takes an exported function
>>>> (M:F/1) that is called with the parameters given to the ssh commands as a
>>>> string. This function should spawn a process that writes the desired output
>>>> on stdout.
>>>>
>>>> So to avoid the strange eval phenomenon, and provide another function that
>>>> just echoes the parameters back, you can write a module x:
>>>> -module(x).
>>>> -export([exec/1]).
>>>> exec(A) -> spawn(fun() -> io:format("~p\n", [A]), exit(normal) end).
>>>>
>>>> and specify the function x:exec/1 as a call-back for the exec option:
>>>> B=ssh_sample_cli:listen(8323, [{subsystems, []}, {exec, x, exec, ""]).
>>>>
>>>> And then when you do:
>>>>> ssh -p 8323 to.the.host 'lists:reverse("test").'
>>>>
>>>> You get back:
>>>> "list:reverse(\"test\")."
>>>>
>>>> Hope this helps. (And sorry for the mess…)
>>>> /Jakob
>>>>
>>>>
>>>>
>>>> 2013/12/10 Ingela Andin <>
>>>>>
>>>>> Hi!
>>>>>
>>>>> The CLI example in the SSH application must be seen as a hack. We intend
>>>>> to clean it up and
>>>>> extend the SSH documentation, when it gets prioritized I can not say. Well
>>>>> anyway your CLI
>>>>> implementation must take care of SSH exec request as well.  You can also
>>>>> look at the ssh_cli.erl  module.
>>>>> If I remember correctly there was a bug, before ssh-2.1.7, with regards to
>>>>> the exec request  so that  it was not forwarded to CLI process but rather
>>>>> always interpreted in the erlang shell environment.
>>>>>
>>>>> Regards Ingela Erlang/OTP team - Ericsson AB
>>>>>
>>>>>
>>>>>
>>>>> 2013/12/7 OvermindDL1 <>
>>>>>>
>>>>>> Greetings,
>>>>>>
>>>>>> I am attempting to just create an SSH shell to connect to a system by
>>>>>> users so they can do commands without the web interface, and as such I
>>>>>> certainly do not want things like port forwarding or being able to run
>>>>>> arbitrary erlang code, however I do not seem to be able to disable
>>>>>> running arbitrary erlang code.  An example of the ssh_sample_cli
>>>>>> included with erlang:
>>>>>> """
>>>>>> $ erl
>>>>>> Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:8:8]
>>>>>> [async-threads:10] [hipe] [kernel-poll:false]
>>>>>>
>>>>>> Eshell V5.10.3  (abort with ^G)
>>>>>> 1> c(ssh_sample_cli).
>>>>>> ssh_sample_cli.erl:146: Warning: this expression will fail with a
>>>>>> 'badarith' exception
>>>>>> {ok,ssh_sample_cli}
>>>>>> 2> B=ssh_sample_cli:listen(8323, [{subsystems, []}]).
>>>>>> {ok,<0.67.0>}
>>>>>> """
>>>>>>
>>>>>> And from another shell/computer:
>>>>>> """
>>>>>> $ ssh -p 8321 to.the.host
>>>>>> 's password:
>>>>>> Enter command
>>>>>> CLI> help
>>>>>> CLI Sample
>>>>>> crash                  crash the cli
>>>>>> exit                   exit application
>>>>>> factors    <int>       prime factors of <int>
>>>>>> gcd        <int> <int> greatest common divisor
>>>>>> help                   help text
>>>>>> host                   print host addr
>>>>>> lcm        <int> <int> least common multiplier
>>>>>> prime      <int>       check for primality
>>>>>> primes     <int>       print all primes up to <int>
>>>>>> rho        <int>       prime factors using rho's alg.
>>>>>> self                   print my pid
>>>>>> user                   print name of user
>>>>>>
>>>>>> ---> ok
>>>>>> CLI> exit
>>>>>> ---> done
>>>>>> Connection to to.the.host closed.
>>>>>> """
>>>>>>
>>>>>> So far so good (the main program where I have this implemented has a
>>>>>> well running shell of its own), but lets try a couple other things:
>>>>>> """
>>>>>> $ sftp -P 8321 to.the.host
>>>>>> 's password:
>>>>>> subsystem request failed on channel 0
>>>>>> Connection closed
>>>>>> """
>>>>>>
>>>>>> Also good, no file transfers can be done since the option subsystem is
>>>>>> set to [], but notice:
>>>>>> """
>>>>>> $ ssh -p 8323 to.the.host 'lists:reverse("!?ti pots I od woh dna ereh
>>>>>> gnineppah si tahw woN").'
>>>>>> 's password:
>>>>>> "Now what is happening here and how do I stop it?!"
>>>>>> """
>>>>>>
>>>>>> So... I can still run arbitrary erlang commands, how do I stop this?
>>>>>> Unable to find an option to pass in or anything through a quick code
>>>>>> perusal to no avail.  Help?
>>>>>> _______________________________________________
>>>>>> erlang-questions mailing list
>>>>>> 
>>>>>> http://erlang.org/mailman/listinfo/erlang-questions
>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> erlang-questions mailing list
>>>>> 
>>>>> http://erlang.org/mailman/listinfo/erlang-questions
>>>>>
>>>>



More information about the erlang-questions mailing list