Les modules `DL` et `Fiddle` de Ruby comportaient une vulnérabilité mettant
en œuvre une chaîne de caractère « contaminée » (*tainted string*, marquée
avec la méthode `Object#tain`). Des appels systèmes pouvaient être fait en
utilisant de telles chaînes de caractères, ignorant le niveau `$SAFE` de
Ruby. Cette vulnérabilité porte l'identifiant CVE-2013-2065.

## Explications et impact

Les fonctions natives exposées à Ruby via `DL` ou `Fiddle` ne vérifiaient pas
la valeur du drapeau `taint` des objets manipulées. Cela pouvait causer des
situations où des chaînes de caractères marquées comme contaminées étaient
tout de même acceptées comme des données valides pour des exceptions de type
`SecurityError`.

Un exemple de code utilisant `DL` illustrant cette vulnérabilité :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">my_function</span><span class="p">(</span><span class="n">user_input</span><span class="p">)</span>
  <span class="n">handle</span>    <span class="o">=</span> <span class="no">DL</span><span class="p">.</span><span class="nf">dlopen</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
  <span class="n">sys_cfunc</span> <span class="o">=</span> <span class="no">DL</span><span class="o">::</span><span class="no">CFunc</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">handle</span><span class="p">[</span><span class="s1">'system'</span><span class="p">],</span> <span class="no">DL</span><span class="o">::</span><span class="no">TYPE_INT</span><span class="p">,</span> <span class="s1">'system'</span><span class="p">)</span>
  <span class="n">sys</span>       <span class="o">=</span> <span class="no">DL</span><span class="o">::</span><span class="no">Function</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">sys_cfunc</span><span class="p">,</span> <span class="p">[</span><span class="no">DL</span><span class="o">::</span><span class="no">TYPE_VOIDP</span><span class="p">])</span>
  <span class="n">sys</span><span class="p">.</span><span class="nf">call</span> <span class="n">user_input</span>
<span class="k">end</span>

<span class="vg">$SAFE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">my_function</span> <span class="s2">"uname -rs"</span><span class="p">.</span><span class="nf">taint</span></code></pre></figure>

Un autre exemple, avec `Fiddle` :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">my_function</span><span class="p">(</span><span class="n">user_input</span><span class="p">)</span>
  <span class="n">handle</span>    <span class="o">=</span> <span class="no">DL</span><span class="p">.</span><span class="nf">dlopen</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
  <span class="n">sys</span> <span class="o">=</span> <span class="no">Fiddle</span><span class="o">::</span><span class="no">Function</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">handle</span><span class="p">[</span><span class="s1">'system'</span><span class="p">],</span>
                          <span class="p">[</span><span class="no">Fiddle</span><span class="o">::</span><span class="no">TYPE_VOIDP</span><span class="p">],</span> <span class="no">Fiddle</span><span class="o">::</span><span class="no">TYPE_INT</span><span class="p">)</span>
  <span class="n">sys</span><span class="p">.</span><span class="nf">call</span> <span class="n">user_input</span>
<span class="k">end</span>

<span class="vg">$SAFE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">my_function</span> <span class="s2">"uname -rs"</span><span class="p">.</span><span class="nf">taint</span></code></pre></figure>

Tous les rubyistes utilisant une version affectée par cette vulnérabilité
sont invités à mettre à jour Ruby, ou à utiliser le palliatif décrit
ci-après.

Veuillez notez que le correctif n'empêche pas l'utilisation d'offset
numériques, car les nombres ne peuvent être marqués par `Object#taint`. Dans
l'exemple suivant :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">my_function</span><span class="p">(</span><span class="n">input</span><span class="p">)</span>
  <span class="n">handle</span>    <span class="o">=</span> <span class="no">DL</span><span class="p">.</span><span class="nf">dlopen</span><span class="p">(</span><span class="kp">nil</span><span class="p">)</span>
  <span class="n">sys</span> <span class="o">=</span> <span class="no">Fiddle</span><span class="o">::</span><span class="no">Function</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">handle</span><span class="p">[</span><span class="s1">'system'</span><span class="p">],</span>
                          <span class="p">[</span><span class="no">Fiddle</span><span class="o">::</span><span class="no">TYPE_VOIDP</span><span class="p">],</span> <span class="no">Fiddle</span><span class="o">::</span><span class="no">TYPE_INT</span><span class="p">)</span>
  <span class="n">sys</span><span class="p">.</span><span class="nf">call</span> <span class="n">input</span>
<span class="k">end</span>

<span class="vg">$SAFE</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">user_input</span> <span class="o">=</span> <span class="s2">"uname -rs"</span><span class="p">.</span><span class="nf">taint</span>
<span class="n">my_function</span> <span class="no">DL</span><span class="o">::</span><span class="no">CPtr</span><span class="p">[</span><span class="n">user_input</span><span class="p">].</span><span class="nf">to_i</span></code></pre></figure>

l'adresse mémoire est utilisée sans qu'il soit possible de déterminer si
elle est valide ou non. Dans ce cas, il est recommandé de vérifier l'état
de la donnée en entrée avant de passer l'adresse mémoire :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">user_input</span> <span class="o">=</span> <span class="s2">"uname -rs"</span><span class="p">.</span><span class="nf">taint</span>
<span class="k">raise</span> <span class="k">if</span> <span class="vg">$SAFE</span> <span class="o">&gt;=</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">user_input</span><span class="p">.</span><span class="nf">tainted?</span>
<span class="n">my_function</span> <span class="no">DL</span><span class="o">::</span><span class="no">CPtr</span><span class="p">[</span><span class="n">user_input</span><span class="p">].</span><span class="nf">to_i</span></code></pre></figure>

## Palliatif

Si vous êtes dans l'impossibilité de mettre à jour Ruby, il est tout de même
possible de palier cette vulnérabilité avec le *monkey patching* suivant :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">class</span> <span class="nc">Fiddle::Function</span>
  <span class="k">alias</span> <span class="ss">:old_call</span> <span class="ss">:call</span>
  <span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
    <span class="k">if</span> <span class="vg">$SAFE</span> <span class="o">&gt;=</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="n">args</span><span class="p">.</span><span class="nf">any?</span> <span class="p">{</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="p">.</span><span class="nf">tainted?</span> <span class="p">}</span>
      <span class="k">raise</span> <span class="no">SecurityError</span><span class="p">,</span> <span class="s2">"tainted parameter not allowed"</span>
    <span class="k">end</span>
    <span class="n">old_call</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span></code></pre></figure>

## Versions affectées

* Toutes les versions de la branche 1.9 avant la 1.9.3-p426
* Toutes les versions de la branche 2.0 avant la 2.0.0-p195
* Les versions de développement avant la révision 40728

La branche 1.8 n'est pas concernée.

## Remerciements

Merci à Vit Ondruch pour avoir mis en lumière cette vulnérabilité.

## Historique

* Initialement publié le 14 mai 2013 à 13:00:00 (UTC)
