photo of devloop

devloop :: blog

sécurité informatique, programmation, Linux et plus encore

Subscribe to RSS feed

CTF Stripe 2012 : Solution du level 5

Voici l'énoncé du level :

Many attempts have been made at creating a federated identity system for the web (see OpenID, for example). However, none of them have been successful. Until today.

The DomainAuthenticator is based off a novel protocol for establishing identities. To authenticate to a site, you simply provide it username, password, and pingback URL. The site posts your credentials to the pingback URL, which returns either "AUTHENTICATED" or "DENIED". If "AUTHENTICATED", the site considers you signed in as a user for the pingback domain.

You can check out the Stripe CTF DomainAuthenticator instance here: [lien retiré] We've been using it to distribute the password to access Level 6. If you could only somehow authenticate as a user of a level05 machine...

To avoid nefarious exploits, the machine hosting the DomainAuthenticator has very locked down network access. It can only make outbound requests to other stripe-ctf.com servers. Though, you've heard that someone forgot to internally firewall off the high ports from the Level 2 server.

J'ai perdu un temps précieux sur ce niveau alors que c'était tout bête :-/ Cela dis je ne sais pas pour combien de personnes ça a sauté aux yeux dès le début. Dans la page du level on a un formulaire très simple :
<form action="" method="POST">
<p>Pingback URL: <input type="text" name="pingback" /></p>
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="Submit"></p>
</form>
Une fois soumis, le serveur envoit le username et le password via REST (comprendre un HTTP POST classique) vers l'adresse de pingback. Si cette page de pingback retour la chaine AUTHENTICATED (sans lettre de chaque côté) alors on est authentifié comme un utilisateur du domaine correspondant à l'url de pingback.

Exemple : j'upload un fichier contenant ".AUTHENTICATED." sur le serveur du level 2. Je place l'url du fichier dans le champ pingback et je renseigne un utilisateur et un mot de passe bidon. J'obtiens alors "You are authenticated as bidule@level02.stripe-ctf.com".

Toute la difficulté du level se tient dans les expressions régulières qui limitent les serveurs à utiliser.
La première (ALLOWED_HOSTS = /\.stripe-ctf\.com$/) nous permet d'accéder aux divers serveurs de Stripe.
Mais seulement la seconde plus restrictive (PASSWORD_HOSTS = /^level05-\d+\.stripe-ctf\.com$/) nous permet d'obtenir le password pour débloquer le niveau.

La partie du code qui nous concerne :
post '/*' do
  pingback = params[:pingback]
  username = params[:username]
  password = params[:password]

  pingback = "http://#{pingback}" unless pingback.include?('://')

  host = URI.parse(pingback).host
  unless host =~ ALLOWED_HOSTS
    return "Host not allowed: #{host}" \
           " (allowed authentication hosts are #{ALLOWED_HOSTS.inspect})"
  end

  begin
    body = perform_authenticate(pingback, username, password)
  rescue StandardError => e
    return "An unknown error occurred while requesting #{pingback}: #{e}"
  end

  if authenticated?(body)
    session[:auth_user] = username
    session[:auth_host] = host
    return "Remote server responded with: #{body}." \
           " Authenticated as #{username}@#{host}!"
  else
    session[:auth_user] = nil
    session[:auth_host] = nil
    sleep(1) # prevent abuse
    return "Remote server responded with: #{body}." \
           " Unable to authenticate as #{username}@#{host}."
  end
end
Quand on fait un GET, la vraible host est extraite de la session et si elle match l'expression régulère PASSWORD_HOSTS alors on obtient le password.

J'ai joué longtemps avec la ligne URI.parse(pingback).host afin de voir si je pouvais former une adresse qui satisfait tous les critères et dont j'ai le contrôle... en vain.

Pour résoudre ce level, il fallait penser comme dans le film Inception (un rêve dans un rêve etc) ou Existenz (si vous préférez) ou récursivité (si ça vous parle plus). Il fallait aussi trouver que le tableau params récupère aussi bien les variables envoyées par POST que par GET.

Comme solution, on passe l'url de pingback suivante dans le formulaire :
https://level05-2.stripe-ctf.com/dossier-du-level5/?pingback=https://level02-3.stripe-ctf.com/dossier-du-level2/uploads/mon-fichiertxt&username=plop&password=plop
avec un user/mdp bidon.

Le serveur du level5 envoie ces données à lui-même. Il extrait les données et fait une nouvelle requête vers le serveur du level02 qui répond AUTHENTICATED grâce à notre fichier.
Cette réponse remonte d'un cran dans les requêtes pour parvenir à la première instance du level 5. On a bien le message AUTHENTICATED venant du serveur du level 5. Le password est alors affiché.

Je me demande si le cerveau humain n'a pas quelques problèmes avec la notion de récursivité p

CTF Stripe 2012 : Solution du level 4

, ,

Voici le scénario du level 4 :

The Karma Trader is the world's best way to reward people for good deeds: https://level04-2.stripe-ctf.com/dossier-du-level4/. You can sign up for an account, and start transferring karma to people who you think are doing good in the world. In order to ensure you're transferring karma only to good people, transferring karma to a user will also reveal your password to him or her.

The very active user karma_fountain has infinite karma, making it a ripe account to obtain (no one will notice a few extra karma trades here and there). The password for karma_fountain's account will give you access to Level 5.



On a donc un site où des utilisateurs peuvent s'échanger du "karma". Le don de karma à un utilisateur provoque l'envoi vers cet utilisateur de notre mot de passe.
L'objectif est donc de faire en sorte que l'utilisateur karma_fountain, dont le mot de passe est celui du level, nous donne un peu de son karma smile

Le script utilisé est en Ruby via Sinatra qui est un framework web qui ressemble fortement à ce qu'on a vu plus tôt en Python.
Après quelques tests on s'aperçoit vite le SQL est blindé par le module de base de données Sequel.
Quand aux sessions, c'est comme pour Flask : une signature est présente et se base sur une graine générée aléatoirement par OpenSSL :-(

Une seule solution ? Le XSS ! De plus un indice dans la page nous incite à utiliser cette technique : la présence de jQuery dans la page alors qu'il n'est pas utilisé.

On ne peut pas jouer sur beaucoup de champs. Après quelques tests on voit que le password n'est pas filtré.
Si on donne quelques points de karma à karma_fountain alors il recevra notre mot de passe "spécial" et bing ! smile

Pour cela je créé un utilisateur 'test' avec le mot de passe suivant :
<script>$.post("./transfer",{to:"test",amount:"1"});</script>

Quand après j'envoie un point de karma à ma cible, le code javascript provoque dans son navigateur le don d'un point de karma à test (moi) et donc le password du level ;-)

Un mélange sympa de XSS et de CSRF smile

CTF Stripe 2012 : Solution du level 3

,

La description du level :

After the fiasco back in Level 0, management has decided to fortify the Secret Safe into an unbreakable solution (kind of like Unbreakable Linux). The resulting product is Secret Vault, which is so secure that it requires human intervention to add new secrets.

A beta version has launched with some interesting secrets (including the password to access Level 4)


La page web du level est on ne peut plus simple : un formulaire POST à destination de la page /login qui reçoit un champ username et un champ password. Une fois connecté, on nous affiche nos secrets enregistrés.
On nous donne aussi des infos sur les autres utilisateurs du système :
  • bob: Stores the password to access level 04
  • eve: Stores the proof that P = NP
  • mallory: Stores the plans to a perpetual motion machine

Le code serveur n'est pas en PHP mais en Python (via le framework Flask).
Le système de session de Flask utilise un mécanisme de signature donc il ne faut pas chercher à casser le cookie.

Notre seule chance est de parvenir à nous authentifier comme étant bob afin d'obtenir le password du level 04.
On dispose de la structure de la base de données sqlite3 :
CREATE TABLE users (
  id VARCHAR(255) PRIMARY KEY AUTOINCREMENT,
  username VARCHAR(255),
  password_hash VARCHAR(255),
  salt VARCHAR(255)
);

ainsi que du code source, en particulier pour le login :
@app.route('/login', methods=['POST'])
def login():
    username = flask.request.form.get('username')
    password = flask.request.form.get('password')

    if not username:
        return "Must provide username\n"

    if not password:
        return "Must provide password\n"

    conn = sqlite3.connect(os.path.join(data_dir, 'users.db'))
    cursor = conn.cursor()

    query = """SELECT id, password_hash, salt FROM users
               WHERE username = '{0}' LIMIT 1""".format(username)
    cursor.execute(query)

    res = cursor.fetchone()
    if not res:
        return "There's no such user {0}!\n".format(username)
    user_id, password_hash, salt = res

    calculated_hash = hashlib.sha256(password + salt)
    if calculated_hash.hexdigest() != password_hash:
        return "That's not the password for {0}!\n".format(username)

    flask.session['user_id'] = user_id
    return flask.redirect(absolute_url('/'))

On remarque la requête SQL servant à l'authentification :

SELECT id, password_hash, salt FROM users WHERE username = USERNAME LIMIT 1


On a le contrôle sur la valeur USERNAME. On peut donc injecter du SQL.

Il faut qu'à la fin de notre requête, le user_id retourné soit celui de bob. On dispose de l'user_id 5, celui de bob est donc le 2.
Pour en arriver là il faut aussi que le hash sha256 du mot de passe fourni dans le formulaire concaténé au salt de la base de donnée correspond au hash stocké dans la base.

Pour celà on doit se servir du mot clé UNION. Pour cela on renseigne le champ username avec :
bob' union select 2, '18496197305510df22af763507c99219ea08e08414383ae1abf1cb156d961a03', '' from users-- 
et on passe "plop" comme mot de passe.

Cela provoque la requête SQL suivante :
SELECT id, password_hash, salt
FROM users
WHERE username = 'bob' union
select 2, '18496197305510df22af763507c99219ea08e08414383ae1abf1cb156d961a03', ''
from users

Comme le script ne récupère que le premier résultat (cursor.fetchone()) et que 2 vient avant 5, le user_id 2 est retourné.
Quand au hash, vous aurez compris qu'il correspond au sha256 de "plop" smile

CTF Stripe 2012 : Solution du level 2

, ,

La description de ce level est la suivante :

You are now on Level 2, the Social Network. Excellent work so far! Social Networks are all the rage these days, so we decided to build one for CTF. Please fill out your profile. You may even be able to find the password for Level 3 by doing so.

La page en question est un bête formulaire d'upload en PHP qui vous propose d'uploader une image pour votre profil. Une fois une image uploadée, son path depuis le web est placé dans une variable de session et affiché à chaque fois que vous vous rendez sur votre profil. L'objectif du challenge est d'obtenir le contenu du fichier password.txt qui est un cran au dessus (dans l'arborescence) des fichiers uploadés. Comme souvent (mais je ne suis pas le seul), j'ai cherché une difficulté qui n'y était pas. Voici le début du code :
<?php
  session_start();

  if ($_FILES["dispic"]["error"] > 0) {
    echo "<p>Error: " . $_FILES["dispic"]["error"] . "</p>";
  }
  else
  {
    $dest_dir = "uploads/";
    $dest = $dest_dir . basename($_FILES["dispic"]["name"]);
    $src = $_FILES["dispic"]["tmp_name"];
    if (move_uploaded_file($src, $dest)) {
      $_SESSION["dispic_url"] = $dest;
      chmod($dest, 0644);
      echo "<p>Successfully uploaded your display picture.</p>";
    }
  }
J'ai d'abord cherché un moyen de bypasser basename() en forgeant mes requêtes HTTP et en passant un path qui remonte l'arborescence (voir mon article sur les scripts d'upload). Mais le basename() est costaud. Captain Obvious à la rescousse ! Une fois qu'on a compris sa bétise on a aussi compris que le script d'upload accepte TOUT type de fichier bigsmile Du coup il suffit d'uploader un script PHP avec ce contenu :
<?php readfile("../password.txt"); ?>
Et de se rendre sur le script PHP. A noter que le level2 a une grande importance dans le CTF dans sa globalité car il servira de "maison" à chaque participant.

CTF Stripe 2012 : Solution du level 1

,

Le level 1 ne m'as pas pris plus de quelques minutes là encore. De quoi prendre le temps de lire l'énoncé et le code p La description est la suivante :
Excellent, you are now on Level 1, the Guessing Game. All you have to do is guess the combination correctly, and you'll be given the password to access Level 2! We've been assured that this level has no security vulnerabilities in it (and the machine running the Guessing Game has no outbound network connectivity, meaning you wouldn't be able to extract the password anyway), so you'll probably just have to try all the possible combinations. Or will you...?
Le code fournit est un script PHP seul (pas de template) et très court. Voici le contenu du body :
  <body>
    <h1>Welcome to the Guessing Game!</h1>
    <p>
      Guess the secret combination below, and if you get it right,
      you'll get the password to the next level!
    </p>
    <?php
      $filename = 'secret-combination.txt';
      extract($_GET);
      if (isset($attempt)) {
        $combination = trim(file_get_contents($filename));
        if ($attempt === $combination) {
          echo "<p>How did you know the secret combination was" .
               " $combination!?</p>";
          $next = file_get_contents('level02-password.txt');
          echo "<p>You've earned the password to the access Level 2:" .
               " $next</p>";
        } else {
          echo "<p>Incorrect! The secret combination is not $attempt</p>";
        }
      }
    ?>
    <form action="#" method="GET">
      <p><input type="text" name="attempt"></p>
      <p><input type="submit" value="Guess!"></p>
    </form>
  </body>
Vous avez peut-être remarqué tout de suite la faille en lisant le code. Si ce n'est pas le cas, rendez-vous sur la page de description de la fonction extract().
Cette fonction fait une instanciation des variables PHP "à l'ancienne" comme quand tout était des super-globales.
Par conséquent tous les paramètres passés en URL correspondront à une variable PHP ou écrasera les variables existantes se trouvant avant le extract().

Ici, une seule variable peut être écrasée et nous amener à la variable $next contenant le mot de passe du level, c'est la variable $filename.

Si on passe la query-string ?attempt=&filename= (variables vides) alors file_get_contents() va échouer et affecter une chaine vide à $combination.
Le script vérifie ensuite que $combination et $attempt (que l'on contrôle aussi) sont égaux. C'est le cas car ils sont tous les deux vides !

Du coup on obtient le sésame pour le niveau suivant. Un bête problème de logique.

CTF Web Stripe 2012 : Solution du level 0

, ,

La société Stripe a organisé un concours de sécurité informatique qui s'est tenu du 22 au 29 aout 2002.
Ce challenge de type "Capture The Flag" est composé de 9 niveaux numérotés de 0 à 8 est orienté sur l'exploitation de vulnérabilités web (XSS, SQL injection, CSRF forgery etc)

J'ai participé à ce concours et j'ai atteint le dernier niveau sans pouvoir le compléter, je n'ai donc pas capturé le drapeau mais je vais vous donner mes solutions des 8 niveaux que j'ai pu résoudre. Pour le dernier level, je posterai sans doute un lien vers la solution la plus intéressante que j'aurais trouvé sur le web.

Entrons dans le vif du sujet avec le niveau 0, résolu en quelques minutes.

On a affaire à une page web générée par node.js et comme pour tous les levels on dispose du code source. Je ne "pratique" pas personnellement node.js mais comme c'est basé sur du javascript, la compréhension du code est pour le moins simple.

D'un côté on trouve le code node.js et de l'autre un fichier de template qui est parsé pour afficher la page finale.
Je ne vous donne pas le code source dans sa totalité, faute de place mais seulement des extraits ainsi que la description officielle du niveau :

We'll start you out with Level 0, the Secret Safe. The Secret Safe is designed as a secure place to store all of your secrets. It turns out that the password to access Level 1 is stored within the Secret Safe. If only you knew how to crack safes...


Le principe est simple : la page sur laquelle on est dispose d'un formulaire (POST) dans lequel on peut rentrer une clé et son secret. Une fois ces données soumises, on peut récupérer plus tard les clés/secret que l'on a renseigné en se rendant simplement sur la page (GET).

Pour compliquer la chose, comme il n'y a pas de notion de session ni d'utilisateur, les clés soumises ne sont pas enregistrées telles quelles mais préfixées par un namespace que l'utilisateur choisi lui-même (3ème champ du formulaire).
Ainsi si on rentre la clé "toto" et que le namespace en cours est "web", la clé ajoutée en base de données sera "web.toto" en raison d'une concaténation comme indiqué dans le code source du script node :

app.post('/*', function(req, res) {
  var namespace = req.body['namespace'];
  var secret_name = req.body['secret_name'];
  var secret_value = req.body['secret_value'];

  var query = 'INSERT INTO secrets (key, secret) VALUES (? || "." || ?, ?)';
  db.run(query, namespace, secret_name, secret_value, function(err) {
     if (err) throw err;

           res.header('Content-Type', 'text/html');
           res.redirect(req.path + '?namespace=' + namespace);
         });
});


Quand au code d'affichage des secrets il est simple :
app.get('/*', function(req, res) {
  var namespace = req.param('namespace');

  if (namespace) {
    var query = 'SELECT * FROM secrets WHERE key LIKE ? || ".%"';
    db.all(query, namespace, function(err, secrets) {
             if (err) throw err;

             renderPage(res, {namespace: namespace, secrets: secrets});
           });
  } else {
    renderPage(res, {});
  }
});


Le système de base de données utilisé ici est du sqlite3. Le point d'interrogation qui apparait dans la requête SQL sera remplacé par le namespace. A cette requête est concaténé le caractère '.' ainsi qu'un wildcard pour récupérer toutes les clés du namespace.

Pour résoudre le challenge (et récupérer les clés de tous les namespaces), il suffit sur la page d'affichage de passer le caractère % (le wildcard) comme valeur de namespace dans l'URL (?namespace=%) ce qui donne la requête suivante :
SELECT * FROM secrets WHERE key LIKE "%.%"

et affiche tous les secrets enregistrés quelque soit le namespace (et donc le password pour débloquer le level smile )

Ajouter la commande which à Kippo

, ,

L'absence de la commande bash which dans le honeypot Kippo m'énervait depuis déjà un moment car c'est une commande de base et son absence permet de détecter très facilement qu'on ne se trouve pas sur un vrai environnement Linux.

Cela n'embête toutefois pas les pseudo-pirates qui s'y connectent puisqu'il me semble qu'aucun n'a eu recours à cette commande jusqu'à présent (ce qui prouve bien leur méconnaissance de Linux).

J'ai pris le temps de rajouter cette commande à Kippo en éditant le fichier kippo/commands/base.py pour ajouter ces lignes :
class command_which(HoneyPotCommand):
    def call(self):
        path_dirs = ['/bin', '/usr/bin', '/sbin', '/usr/sbin']
        for arg in self.args:
            found = False
            for p in path_dirs:
                if self.fs.exists("%s/%s" % (p, arg)):
                  self.writeln("%s/%s" % (p, arg))
                  found = True
                  break
            if not found:
              self.writeln("which: no %s in (%s)" % (arg, ":".join(path_dirs)))
commands['which'] = command_which


Cela intéressera peut-être d'autres utilisateurs de ce honeypot...
A ce propos j'ai aussi mis en place un serveur FTP acceptant les connexions anonymes uniquement pour voir si cela attire des "tagueurs"... mais pour le moment je n'ai observé que des attaques brute-force (alors que la bannière indique connexion anonyme uniquement, no-comment...)

Pthreads-win32 et fuite de mémoire sur le tas

Un petit post pour indiquer que récemment j'ai du me servir de la bibliothèque pthreads-win32 sous MingW.

Cette librairie de threads est tout simplement un wrapper pour les fonctions de l'API Windows (SetEvent, EnterCriticalSection, CreateSemaphore etc) permettant de travailler avec des fonctions de type pthread_mutex_lock, pthread_cond_signal etc.

C'est utile pour les programmeurs qui souhaitent porter rapidement du code threadé Linux vers Windows mais pour avoir bidouillé un peu avec cette librairie et malgré ce qu'en disent certain, j'ai constaté des fuites de mémoire sur le tas qui peuvent devenir très gênantes pour peu que le code ait beaucoup de travail à effectuer.

Vraisemblablement l'auteur n'a pas prévu certains cas d'utilisation de sa librairie (ce qui est difficilement envisageable avec ce type de wrapper) et des malloc (comprendre HeapAlloc, LocalAlloc... Windows oblige) doivent faire des allocations sur le heap sans jamais être libérés (free).

A raison d'environ 4ko perdus à chaque boucle de thread, on peut facilement mettre en rade une machine vieillissante...
La programmation de threads sous Windows est loin d'être désagréable et s'avérera moins agressive avec votre RAM p

Victime de vol d'identité

On peut se passionner pour la sécurité informatique, être très concerné sur les problématiques de la vie privée, faire attention aux informations que l'on expose sur Internet, être de nature prudente... on n'en reste pas moins exposé aux attaques.

Il y a un an, j'étais la victime d'une attaque informatique non ciblée par l'ouverture d'un simple fichier PDF. L'expérience a toutefois été enrichissante techniquement.
Tout dernièrement, c'est un expert en sécurité informatique qui faisait les frais d'une tentative d'escroquerie et de piratage par le biais d'un scam téléphonique.

Récemment j'ai vécu le genre d'expérience qui vous font dire "Monde de merde", quand bien même vous auriez la classe américaine.

L'histoire commence pour moi un week-end. Le matin, je me connecte au service en ligne de ma banque et j'ai une fâcheuse surprise, le genre à vous mettre direct nervous breakdown, comme quand vous allez à la boulangerie et que la personne derrière vous rafle la dernière ouiche lorraine...

Un retrait d'une somme importante a été fait sur mon compte courant. Sur le coup je suis plutôt dans l'incompréhension et j'essaye de me rappeler à quoi cette somme peut bien correspondre, en vain.
Le libellé de cette opération bancaire est aussi surprenant car il n'est pas inscrit "virement", ni "retrait DAB" mais uniquement "retrait".
Sur le coup, je ne m'inquiète pas plus que ça et je me promets de contacter ma banque le lendemain pour éclaircir l'origine de cette opération.

C'est quand je suis allé voir, juste après, l'état de mon livret A que j'ai compris tout de suite que des opérations frauduleuses avaient eu lieu. Deux retraits à un intervalle d'une semaine, effectués dans un patelin où jamais je n'ai mis les pieds, qui plus est sur un compte sur lequel il n'y a d'habitude jamais aucune opération.

Au total c'est l'équivalent de deux mois de salaire envolés.

D'abord on a une réaction d'incompréhension. On se demande comment de telles opérations ont pu avoir lieu et on a beau esquisser plusieurs scénarios, il reste toujours des inconnues.
Ensuite, on a une réaction bien humaine mais sans doute stupide, de se demander qui peut nous en vouloir à ce point pour faire une telle chose. On se sent un peu comme un pinguino qu'on aurait lâché en plein milieu de la jungle.

La vérité bien sûr c'est que les personnes coupables de vol d'identité n'ont généralement aucun contact avec leurs victimes à l'instar du botmaster qui propage ses virus et ne verra jamais les siennes, mis à part peut-être dans un tribunal.
Il est même probable que ces criminels ne sachent finalement rien de plus que le nom de leur victime.
On sait que ce type d'escroquerie peut arriver à tout le monde, même à Paul Allen, le co-fondateur de Microsoft.

Parce que c'est bien de vol d'identité qu'il s'agit : une personne s'est présentée au guichet d'un établissement de ma banque et est parvenu à deux reprises à retirer de l'argent en se faisant passer pour moi.

Bien-sûr, il y a des mécanismes de sécurité (en particulier la vérification de l'identité de la personne qui se présente) qui auraient du bloquer cette attaque et qui n'ont pas fonctionné à ces moments pour des raisons que j'ignore.
Ensuite, il y a un fait marquant dans cette affaire : comment l'auteur du crime a t-il pu récupérer le numéro du livret A alors que seuls ma banque et moi en connaissons l'existence ?
Si j'admets qu'un numéro derrière un CC peut facilement faire l'objet de fuites, que ce soit par le vol d'information chez un tiers (vol d'un RIB ou vol d'un dossier client que ce soit physiquement ou par ordinateur), la fuite d'un numéro de compte alors qu'il n'est utilisé par aucun tiers me laisse perplexe.

La seule explication plausible semble être l'interception d'un relevé bancaire directement dans la boîte aux lettres.
La boîte aux lettres est sans aucun doute le maillon faible, le Saint-Graal de l'usurpation d'identité avec le trashing. Les cas de vol de chéquiers depuis une boîte aux lettres sont aussi légion.

Reste la question du bypass de l'identification au guichet. Pour cela les hypothèses qui me viennent à l'esprit sont les suivantes :
  • La personne est parvenu à produire une fausse pièce d'identité. Ce serait se donner beaucoup de mal pour une somme toute relative (du moins j'espère que ce n'est pas aussi simple)
  • La personne au guichet est peu regardante et le voleur en a profité, peut-être en embrouillant l'employé, peut-être en fournissant un courrier intercepté en guise de preuve
  • La personne au guichet était complice. Je ne sais pas qu'elles vérifications sont faites lors de l'embauche des guichetiers dans les établissement bancaires mais si elles existent elles doivent être loin de celles faites pour les convoyeurs de fonds. Est-ce qu'une vérification du casier judiciaire a au moins lieu ? Ces personnes manipulent pourtant l'argent de tout un chacun
  • Personne ne s'est réellement présenté au guichet. C'est l'employé de la banque qui se sert allégrement dans les comptes
  • La personne au guichet a fait une erreur. Il n'y a pas de coupable. Elle a juste débité le mauvais compte. Clairement sur deux comptes différents et à deux reprises ça me ferait bien rire
  • La personne au guichet ne s'est pas tout à fait trompée. Elle a eu affaire à un homonyme. Vu que je doit en avoir autant en France que notre ex-président de la république ça me fait bien rire aussi


Il reste toutefois une hypothèse que je me dois de citer si je vais au bout de ma réflexion (et par respect pour Arthur Conan Doyle) : je pourrais être le coupable.
C'est vrai, rien ne m'empêche de me rendre dans un bled éloigné, d'effectuer un retrait, de laisser intentionnellement une signature invalide et de faire opposition plus tard et réclamer le remboursement auprès de ma banque.

Après cette histoire, j'ai été remboursé mais je dois avouer que la banque ne m'a réclamé aucune preuve de mon innocence si ce n'est le récépissé de plainte en commissariat.
Clairement, si elle l'avait fait ça m'aurait sorti de mes gonds. Je suis un client de longue date et je n'ai jamais eu de problèmes avec ma banque. Qui plus est, elle est fautive dans la majorité des hypothèses.
Mais quelque part elle aurait été dans son droit. Peut-être pas légalement (je ne sais pas quelle est la législation sur ce point) mais sur le fond c'est son droit : à partir du moment où je suis remboursé, la perte est la sienne. Je n'aurais eu aucun mal à fournir ces preuves mais l'opinion que j'ai de ma banque en aurait pâti.

Compte tenu des circonstances, la banque a été relativement rapide à me rembourser et j'en suis satisfait. Je souligne toutefois l'absence de procédure interne de l'établissement pour gérer ce type de crise, du moins en ce qui concerne les employés en agence. Les personnes dans les centres de gestion sont plus habitués à ce type de problème, seulement ce ne sont pas nos premiers interlocuteurs.

J'ai effectué il y a longtemps un stage dans une banque et des procédures de tout type j'en a vu un paquet, certaines expliquant même comment réagir face à un braquage... Il est étonnant que des procédures n'existent pas pour les vols d'identité qui sont malheureusement de plus en plus courants.

La conclusion que je donnerais à cette affaire c'est qu'à l'instar d'Orson Welles, j'aime pas trop les voleurs et les fils de pute.

VXcloud v2 : Mise à jour de l'antivirus sur le cloud pour Linux

, , , ...

Aujourd'hui j'ai pris le temps de mettre à jour mon script VXcloud dont la dernière version date de presque un an.

Parmi les changements, la correction d'un bug dans l'exploration récursive des dossiers et le passage à la nouvelle version de l'API VirusTotal.

Grâce à cette nouvelle API la soumission des hashs est plus rapide : 4 hashs MD5 sont envoyés dans une même requête HTTP smile

Aussi, pour chaque binaire détecté comme dangereux ou suspicieux par au moins un des antivirus, le taux de détection sera affiché (du style 25/40 antivirus détectent un danger).
Le taux de détection était déjà présent pour la base de données de la Team-Cymru mais non intégré pour VirusTotal.

Et enfin les résultats sont plus lisibles.

Vous pouvez télécharger cette nouvelle version ici.