Archive pour la catégorie ‘Securité’

Installation d’un d?tecteur d’intrusions SNORT

Jeudi 22 septembre 2005

Ce document va tenter d’expliquer les diff?rentes ?tapes pour mettre en place le d?tecteur d’intrusions SNORT à partir des sources. Un d?tecteur d’intrusions s’appelle aussi "IDS" pour Intrusion Detection System. SNORT est un système de d?tection d’intrusions r?seau en OpenSource, capable d’effectuer l’analyse du trafic en temps r?el.

source:lea-linux.org
2003, Julien Lecubin
dernière modification le 30/04/2003.

Installation de l’IDS SNORT


Introduction

On l’utilise en g?n?ral pour d?tecter une vari?t? d’attaques et de scans tels que des d?bordements de tampons, des scans de ports furtifs, des attaques CGI, des scans SMB, des tentatives d’identification d’OS, et bien plus.

Avant de commencer l’installation de SNORT, vous devez avoir install? :

PACKAGES REMARQUES
MySQL La base de donn?es MySQL
MySQL-client La partie cliente de mysql (connexion BD)
php-mysql le module php de mysql
Apache Le serveur web Apache
mod_php Le module php pour Apache
libpcap/libpcap0-devel Librairie utilis?e par SNORT pour capturer les paquets (rpm t?l?chargeable sur rpmfind.net)
gcc indispensable pour compiler les sources de SNORT
Si vous n’avez pas encore install? le trio Apache/PHP/MySQL, il y a un article sur Lea vous expliquant comment le faire. C’est ici.

Les ?tapes pour l’installation de SNORT sont les suivantes :

  • Installation de l’outil SNORT
  • Installation des règles SNORT
  • Liaison Mysql et SNORT
  • Mise en place de ACID (Interface php pour visualiser les logs SNORT)

Installation de SNORT

T?l?chargez la dernière release de SNORT à l’adresse suivante : http://www.SNORT.org/dl . La compilation de ce programme reste traditionnelle :

COMMANDES REMARQUES
cd /usr/local/snort
tar -xvzf SNORT-1.9.*.tar.gz D?compacte l’application
./configure –with-mysql=/usr/lib/mysql Retirez l’argument –with-mysql si vous ne souhaitez pas rediriger les logs SNORT vers une base de donn?es mysql *
make Compilation
make install Installation
Pour l’argument –with-mysql, vous pouvez l’adapter si vous utilisez une base de donn?es autre que MySQL :

  • –with-odbc=$PATH_ODBC : pour une base de donn?es Microsoft SQL server
  • –with-postgresql=$PATH_POSTGRE : pour une base PostegreSQL
  • –with-oracle=$ORACLE_HOME : pour une base de donn?es Oracle.

Installation des règles SNORT

Maitenant, il faut t?l?charger les règles de SNORT. En effet, SNORT utilise des règles pour d?tecter les intrusions. Il existe aujourd’hui environ 1200 règles diff?rentes. Ces règles se caract?risent par un ensemble de fichiers (ftp.rules, p2p.rules,telnet.rules etc…). Vous devez t?l?chargez les sources de ces règles à l’adresse suivante :
http://www.SNORT.org/dl/signatures

Cr?ez le r?pertoire de configuration SNORT, et installez-y les règles :

COMMANDES REMARQUES
mkdir /etc/snort Cr?ation du r?pertoire contenant la configuration SNORT
cp /usr/local/snort*/etc/snort.conf /etc/snort Copie du fichier de config snort dans /etc/snort
cp snortrules.tar.gz /etc/snort Mise en place des règles dans le r?pertoire de configuration SNORT
cd /etc/snort On se place dans le r?pertoire de configuration SNORT
tar -xvzf snortrules.tar.gz D?compactage des règles
Les règles SNORT sont alors plac?es dans le r?pertoire /etc/snort/rules.

Maintenant, Il faut ?diter le fichier de configuration snort (/etc/snort/snort.conf) et sp?cifier le r?seau sur lequel l’IDS travaille. Il faut pour cela modifier la variable HOME_NET :

var HOME_NET [10.1.1.0/24] # SNORT travaille sur le r?seau 10.1.1.0
var HOME_NET (10.1.1.0/24,192.168.1.0/24] # Si votre carte r?seau possède 2 alias
Dans le fichier de configuration de SNORT (/etc/snort/snort.conf), vous avez toute une s?rie de include. Il s’agit des règles utilis?es par SNORT pour d?tecter d’?ventuelles intrusions. Il y a des règles de telnet, ICMP, FTP, … Bref, commentez celles que vous ne voulez pas et d?commentez celles qui vous parait utile. Conseil : D?commentez les règles ICMP, car elles ne cessent pas de vous remonter des alarmes très souvent inutiles.

Pour des explications plus d?taill?es concernant les règles SNORT, allez voir ici.

Lancement de SNORT

Deux possibilit?s s’offrent à nous. Soit vous lancez SNORT tout seul, et dans ce cas, il g?nerera ces logs dans un fichier plat. Soit vous d?cidez de l’interfacer avec une base de donn?es. Suivant le cas, SNORT ne se lancera pas de la même façon.

Sans Mysql :
/usr/local/snort*/src/snort -c /etc/snort/snort.conf -i eth0 -D
Avec Mysql :
/usr/local/snort*/src/snort -c /etc/snort/snort.conf
Remarque : Si vous souhaitez interfacer SNORT avec une base de donn?es, ne lancez pas SNORT avec l’argument -L qui sp?cifie l’emplacement des logs.

Lier les logs SNORT avec MySQL

Maintenant, nous allons ?diter le fichier de configuration de SNORT afin de lui indiquer qu’il faut rediriger les logs dans une base de donn?es (ici MySQL). Avec vos yeux de lynx, retrouvez la ligne suivante dans le fichier de configuration SNORT /etc/snort/snort.conf :

#output database:log,mysql,user=root password=test dbname=SNORT host=localhost
D?commentez et modifiez cette ligne par :

output database:log,mysql,user=user_snort password=snort_pwd dbname=snort host=localhost
Ici, l’utilisateur MySQL acc?dant à la base de donn?es s’appelle "user_snort", son password associ? est "snort_pwd", le nom de la base MySQL utilis?e par snort est "snort" et la machine qui fait tourner la base Mysql est la même que celle où SNORT tourne.

Cr?ation de la base de donn?es SNORT

Au pr?alable, assurez-vous d’avoir install? :

PACKAGES REMARQUES
MySQL-client-* partie cliente de MySQL
MySQL-devel-*  
Astuce : La commande "rpm -qa | grep client" vous permet de v?rifier que votre station Linux possède bien ces packages install?s.

Suivez alors les instructions suivantes :

COMMANDES REMARQUES
cd /usr/local/snort*/contrib on se place à l’endroit du fichier contenant les tables SQL de SNORT
mysql -u root -p Connexion à la base de donn?es en tant qu’administrateur (au passage, si ce n’est pas encore fait, d?finissez un password pour l’administrateur de la base par la commande ’set password for root@localhost=PASSWORD(‘totomdp’);
create database SNORT; Cr?ation de la base de donn?es SNORT
use mysql; On se place ici pour cr?er l’utilisateur MySQL qui g?rera la base de donn?es snort
insert into user values(‘localhost’, ‘user_snort’, password(’snort_pwd’), ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’, ‘Y’,  »,  »,  »,  », ‘Y’, ‘Y’, ‘Y’); Cr?ation utilisateur MySQL "user_snort". Attention le nombre de ‘Y’ d?pend de votre version de MySQL. (faites un select * from user; pour voir combien il faut en mettre)
grant ALL PRIVILEGES ON SNORT.* TO user_snort@localhost IDENTIFIED BY ’snort_pwd’ WITH GRANT OPTION; Attribution des droits de la base "snort" à l’utilisateur "user_snort"
flush privileges; Recharge les tables de droits pour prendre en compte les nouvelles modifications
use snort; on se place dans la base où l’on veut cr?er les tables pour SNORT
Source create_mysql Cr?ation des tables pour SNORT
V?rifiez que les tables sont bien cr??es. Allez voir dans /var/lib/mysql/snort et vous y verrez tout un tas de fichiers correspondant au nom des tables de la base de donn?es SNORT (il doit y avoir 3 fichiers par tables).

Lancez SNORT. D?sormais, SNORT envoie les informations dans la base de donn?es (astuce : installez PhpMyAdmin, et v?rifiez la taille de la base de donn?es SNORT. Si tout fonctionne, vous la voyez augmenter si bien ?videmment il y a du trafic !).

Installation/Configuration ACID

ACID est une interface PHP qui permet de visualiser les remont?es d’alarmes g?n?r?es par SNORT. Cette partie sous-entend que vous avez une base de donn?es qui r?cupère les informations envoy?es par SNORT. Avant de suivre l’installation de cette application, assurez-vous d’avoir t?l?charg? :

  • Adodb : Contient des scripts PHP g?n?riques de gestion de bases de donn?es. L’installer dans la racine d’apache (/var/www/html/adodb par exemple)
  • PHPlot : librairie de scripts PHP utilis?e par ACID pour pr?senter graphiquement certaines donn?es statistiques (optionnel)
Le t?l?chargement de ACID se fait ici. Imaginons que la racine de votre serveur web est /var/www/html. Installez ACID dans la racine d’apache :

COMMANDES REMARQUES
cd /var/www/html Placez-vous dans la racine du serveur web
tar -xvzf acid* D?compactage de ACID
tar -xvzf adodb* D?compactage de AdoDB
tar -xvzf phplot* D?compactage de PHPlot
vi /var/www/html/acid/acid_conf.php Renseignez les champs suivants :
  • $DBlib_path="../adodb";
  • $Chartlin_path="../phplot";
  • alert_dbname="snort"
  • alert_host="localhost"
  • alert_user="user_snort"
  • alert_password="snort_pwd"
Voilà, maintenant vous pouvez v?rifier que ACID est bien configur? (allez voir sur http://localhost/acid). Si vous le souhaitez, L’accès peut se faire via certificat SSL de manière à crypter l’?change entre vous et le d?tecteur d’intrusions.

Sachez que ce document a pour but de vous apporter quelques ?l?ments de r?ponse concernant l’installation et la configuration de l’IDS SNORT. Il est loin d’être parfait. Vos remarques sont les bienvenues. Je pr?vois de modifier le pr?sent document suivant les remarques que vous y apporterez.

Pour me contacter : guitarparts chez fr point st

Le nouveau num?ro 5/2005 (12) de hakin9 disponible !

Mardi 13 septembre 2005

Au sommaire :

* Pharming – attaques DNS cache poisoning,

* S?curit? pour le système Voice over IP – protocoles SIP et RTP,

* Signature ?lectronique gratuite,

* Attaques exploitant les failles dans le modèle de s?curit? Java VM,

* Techniques des injections SQL sophistiqu?es,

* Optimisation des shellcodes sous Linux.
* L’interview avec Dan J. Bernstein!


Sur le CD
: des tutoriaux + de 20 ebooks


site officiel
: hakin9.org/fr

Introduction ? la cryptographie

Mercredi 24 août 2005

Par Godrik (godrik-article@mandragor.org)

Pourquoi une introduction à la cryptologie? Tout simplement parce que trop de notion de cryptographie sont inconnues même des informaticiens. Pour d?finir les termes peu faciles à comprendre pour un n?ophyte. Pour poser les bases qui me serviront pour ma suite d’articles sur la cryptographie. Mais surtout parce que ça me fait plaisir!

Introduction à la cryptologie

Par Godrik (godrik-article@mandragor.org)

Pourquoi une introduction à la cryptologie? Tout simplement parce que trop de notion de cryptographie sont inconnues même des informaticiens. Pour d?finir les termes peu faciles à comprendre pour un n?ophyte. Pour poser les bases qui me serviront pour ma suite d’articles sur la cryptographie. Mais surtout parce que ça me fait plaisir!

Remerciement

Je veux avant d’?crire une seule ligne sur la cryptologie, remercier Jacques Patarin qui m’a enseign? la cryptologie lorsque j’?tais en licence à Versailles.

Avant de commencer

Plusieurs termes sont à d?finir avant toutes choses:

  • chiffrer: action de coder un message.
  • d?chiffrer: action de d?coder un message.
  • d?crypter: action de d?chiffrer un message lorsque l’on est cens? pouvoir le faire.
  • message: une valeur num?rique (la plupart du temps, elle sera en base 2).
  • clair: un message dans sa verion non chiffr?.
  • chiffr?: la version crypt? d’un clair.
  • hash?(condens? en français): version condens?e d’un message (il y aura un article sur le hashage).
  • cl?: valeur(s) qui servira(ont) à chiffrer et/ou à dechiffrer un message.
Les pr?noms suivants seront toujours utilis?s dans les mêmes conditions:

  • Alice: la personne qui chiffre le message.
  • Bob: la personne qui d?chiffre le message.
  • Charli: la personne qui cherchera à d?crypter le message. Je l’appellerai aussi "l’ennemi".
La cryptologie est une science math?matique et donc pr?cise. Toute v?rit? sera dites dans l’absolue. Lorsque l’on chiffrera un message, on enverra Y = F(M, K) ou F est la fonction (algorithme) de cryptage utilis?, M le clair, Y le chiffr?, K la cle.

Je vais vous pr?senter les diff?rente notions de cryptographie tel qu’elles ont ?t? d?couverte. Nous commencerons donc par le chiffrement de Vernam qui est un modèle th?orique très contraignant. Nous verrons par la suite quels ont ?t? les solutions apport?es par les cryptologue au diff?rent problèmes.

Le seul chiffrement parfait: le chiffrement de Vernam

Ce chiffrement est aussi appel? OTP (comme One Time Pad).

OTP est un algorithme de chiffrement qui utilise une cl? parfaitement al?atoire de la même taille que le message.

C’est le seul chiffrement qui est inconditionnellement sur, c’est-à-dire que l’algorithme n’est pas d?cryptable même si l’ennemi possède une force de calcul infinie et dont on peut prouver la s?curit?.

Cet algorithme possède un enorme problème. La cl? fait la même taille que le message; Ce qui veut dire que si l’on veut envoyer un message de 1Go, il faut que les deux partis soient d’accord à l’avance sur une cl? de 1Go. Pour que la s?curit? soit absolue, il faut donc que le partage de cl? soit fait physiquement (pas question de la faire passer sur Internet). De plus la cl? doit être r?ellement al?atoire (et donc ing?nèrable). Cet etait utilis? par les groupes militaires pour leur sous-marin. Avant qu’il ne parte en mer, on lancait une pièce dont le "pile ou face" donnait un bit de cl?, on recommencait l’op?ration un milliard de fois. On stockait le tout sur des disque dur que l’on rentrait dans le sous-marin et une copie que l’on gardait à terre. Et il peut arriver que l’on ait utilis? tout le disque dur, au quel cas on ne peut plus rien chiffrer.

Cet exemple semble être ceui qui montre le mieux les problèmes li?s à cette algorithme. C’est en r?ponse à cette contrainte et en comprenant qu’il faudrait sacrifi? la s?curit? absolue que la notion de standard de s?curit? a ?t? invent?.

Le standard de s?curit? et la cryptographie à cl? priv?

La cryptologie moderne n’utilisera jamais de système inconditionnellement sur parcequ’ils sont trop contraignants. On pr?fèrera utiliser des systèmes prouv?s (ou conjectur?s) "incassables" à moins de faire un nombre inimaginable de calculs. Ce nombre est appel? standard de s?curit?. En 2003 il vaut 280; En 1976 il n’?tait que de 256.

On ?value ce chiffre en MIPS-an. 1 MIPS-an ?quivaut à une machine qui ferai un million d’op?rations à la seconde pendant un an. 280 calculs correspondent à 1 milliard de GIPS-an (Milliard d’instructions) soit un millard de PC a 1Ghz qui tournent sur une ann?e (On ne rigole pas avec la s?curit?!).

Les premières exploitation du standard de s?curit? ont ?t? fait à partir d’algorithme à cl? priv?(aussi dit sym?trique).

La cl? est dites priv?s car elle doit rester secrète (tout comme pour OTP); la compromission de la cl? entraine le compromission de la s?curit? de l’algorithme. C’est pour cela que par couple de personne qui s’?change des message, il doit exister une cl? priv?. Si l’on recoit un message crypt?, on sait qui l’a ?mis (puisque seul cette personne est cens? avoir la cl?).

De la cl? priv? à la cl? publique

La cl? priv? impose de lourde contrainte bien qu’elle soit globalment rapide à calculer. En effet elle impose que sur un reseau de 10 personnes, on a donc 10*9/2 = 45 cl? priv? (Une cl? par couple de personne)… Ce qui est ?norme et ?volue de façon exponentiel.

C’est en r?ponse à ce problème qu’est n?e la cryptographie à cl? publique (aussi dites asym?trique). Elle repose sur le principe que la cl? est s?par? en deux parties: la partie publique et la partie priv?. Les variables publiques permettent de chiffr? un message mais sont insuffisante pour le d?chifr?. Si l’on dispose de la partie priv? de la cl?, on d?chiffrera le message très simplement. L’outil math?matique exploit? par la cryptographie à cl? publique sont les fonction a trappe (trap-door function en anglais).

Pour qu’une personne puisse recevoir des message chiffr?, elle publiera ces variables publiques, mais conservera ces variables priv?s. Ainsi tout le monde pourra lui ?crire, mais seul elle pourra lire les message.

La cryptographie asym?trique provoque l’?mergence de deux autre domaines: la signature et l’authentification. En effet, puisque tout le monde peut envoyer un message chiffr?, il faut pouvoir identifier l’origine du message. Si l’identification est asynchrone (comme l’envoie d’un e-mail) on parlera de signature; mais dans le cas d’une communication synchrone (session telnet), on parlera d’authentification.

En chiffrement publique, c’est la personne qui d?chiffrera le message qui aura un secret. En authentification et en signature ce sera le prouveur qui aura un secret; et il dira : "Si j’ai pu emmetre cette signature c’est que je dispose d’un secret que vous n’avez pas, je suis donc celui que je pr?tend être.".

Using Process Infection to Bypass Windows Software Firewalls

Vendredi 1 juillet 2005
  [0x01] introduction  [0x02] how software firewalls work  [0x03] process Infection without external .dll  [0x04] problems of implementation  [0x05] how to implement it  [0x06] limits of this implementation  [0x07] workaround: another infection method  [0x08] conclusion  [0x09] last words

  [0x0A] references

  [0x0B] injector source code

                           ==Phrack Inc.==

Volume 0x0b, Issue 0x3e, Phile #0x0d of 0x10

|=--=[ Using Process Infection to Bypass Windows Software Firewalls ]=--=|
|=-----------------------------------------------------------------------=|
|=---------------------------=[ rattle ]=--------------------------------=|

-[0x01] :: introduction --------------------------------------------------

This entire document refers to a feature of software firewalls
available for Windows OS, which is called "outbound detection".
This feature has nothing to do with the original idea of a
firewall, blocking incomming packets from the net: The outbound
detection mechanism is ment to protect the user from malicious
programs that run on his own computer - programs attempting to
communicate with a remote host on the Internet and thereby
leaking sensible information. In general, the outbound detection
controls the communication of local applications with the
Internet.

In a world with an increasing number of trojan horses, worms
and virii spreading in the wild, this is actually a very handy
feature and certainly, it is of good use. However, ever since
I know about software firewalls, I have been wondering whether
they could actually provide a certain level of security at all:
After all, they are just software supposed protect you against
other software, and this sounds like bad idea to me.

To make a long story short, this outbound detection can be
bypassed, and that's what will be discussed in this paper.
I moreover believe that if it is possible to bypass this one
restriction, it is somehow possible to bypass other restrictions
as well. Personal firewalls are software, trying to control
another piece of software. It should in any case be possible
to turn this around by 180 degrees, and create a piece of
software that controls the software firewall.

Also, how to achieve this in practice is part of the discussion
that will follow: I will not just keep on talking about abstract
theory. It will be explained and illustrated with sample source
code how to bypass a software firewall by injecting code to a
trusted process. It might be interesting to you that the method
of runtime process infection that will be presented and explained
does not require an external DLL - the bypass can be performed
by a stand-alone and tiny executable.

Thus, this paper is also about coding, especially Win32 coding.
To understand the sample code, you should be familiar with
Windows, the Win32 API and basic x86 Assembler. It would also be
good to know something about the PE format and related things,
but it is not necessary, as far as I can see. I will try to
explain everything else as precisely as possible.

Note: If you find numbers enclosed in normal brackets within
the document, these numbers are references to further sources.
See [0x0A] for more details.

-[0x02] :: how software firewalls work -----------------------------------

Of course, I can only speak about the software firewalls I have
seen and tested so far, but I am sure that these applications
are among the most widely used ones. Since all of them work in a
very similar way, I assume that the concept is a general concept
of software firewalls.

Almost every modern software firewall provides features that
simulate the behaviour of hardware firewalls by allowing the
user to block certain ports. I have not had a close look on
these features and once more I want to emphasize that breaking
these restrictions is outside the scope of this paper.

Another important feature of most personal firewalls is the
concept of giving privileges and different levels of trust to
different processes that run on the local machine to provide a
measure of outbound detection. Once a certain executable creates
a process attempting to access the network, the executable file
is checksummed by the software firewall and the user is prompted
whether or not he wants to trust the respective process.

To perform this task, the software firewall is most probably
installing kernel mode drivers and hooks to monitor and intercept
calls to low level networking routines provided by the Windows OS
core. Appropriately, the user can trust a process to connect() to
another host on the Internet, to listen() for connections or to
perform any other familiar networking task. The main point is: As
soon as the user gives trust to an executable, he also gives
trust to any process that has been created from that executable.
However, once we change the executable, its checksum would no
longer match and the firewall would be alerted.

So, we know that the firewall trusts a certain process as long as
the executable that created it remains the same. We also know that
in most cases, a user will trust his webbrowser and his email
client.

-[0x03] :: process Infection without external .dll -----------------------

The software firewall will only calculate and analyze the checksum
for an executable upon process creation. After the process has
been loaded into memory, it is assumed to remain the same until it
terminates.

And since I have already spoken about runtime process infection,
you certainly have guessed what will follow. If we cannot alter
the executable, we will directly go for the process and inject
our code to its memory, run it from there and bypass the firewall
restriction.

If this was a bit too fast for you, no problem. A process is
loaded into random access memory (RAM) by the Windows OS as soon
as a binary, executable file is executed. Simplified, a process
is a chunk of binary data that has been placed at a certain
address in memory. In fact, there is more to it. Windows does a
lot more than just writing binary data to some place in memory.
For making the following considerations, none of that should
bother you, though.

For all of you who are already familiar with means of runtime
process infection - I really dislike DLL injection for this
purpose, simply because there is definitely no option that could
be considered less elegant or less stealthy.

In practice, DLL injection means that the executable that
performs the bypass somehow carries the additional DLL it
requires. Not only does this heaviely increase the size of the
entire code, but this DLL also has to be written to HD on the
affected system to perform the bypass. And to be honest - if
you are really going to write some sort of program that needs
a working software firewall bypass, you exactly want to avoid
this sort of flaws. Therefore, the presented method of runtime
process infection will work completely without the need of any
external DLL and is written in pure x86 Assembly.

To sum it all up: All that is important to us now is the ability
to get access to a process' memory, copy our own code into that
memory and execute the code remotely in the context of that
process.

Sounds hard? Not at all. If you have a well-founded knowledge of
the Win32 API, you will also know that Windows gives a programmer
everything he needs to perform such a task. The most important
API call that comes to mind probably is CreateRemoteThread().
Quoting MSDN (1):

The CreateRemoteThread function creates a thread that
runs in the address space of another process.

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

Great, we can execute code at a certain memory address inside
another process and we can even pass one DWORD of information as
a parameter to it. Moreover, we will need the following 2 API
calls:

VirtualAllocEx()
WriteProcessMemory()

they give us the power to inject our own arbitrary code to the
address space of another process - and once it is there, we will
create a thread remotely to execute it.

To sum everything up: We will create a binary executable that
carries the injection code as well as the code that has to be
injected in order to bypass the software firewall. Or, speaking
in high-level programming terms: We will create an exe file that
holds two functions, one to inject code to a trusted process
and one function to be injected.

-[0x04] :: problems of this implementation -------------------------------

It all sounds pretty easy now, but it actually is not. For
instance, you will barely be able to write an application in C
that properly injects another (static) C function to a remote
process. In fact, I can almost guarantee you that the remote
process will crash. Although you can call the relevant API calls
from C, there are much more underlying problems with using a
high level language for this purpose. The essence of all these
problems can be summed up as follows: compilers produce ASM code
that uses hardcoded offsets. A simple example: Whenever you use
a constant C string, this C string will be stored at a certain
position within the memory of your resulting executable, and any
reference to it will be hardcoded. This means, when your process
needs to pass the address of that string to a function, the
address will be completely hardcoded in the binary code of your
executable.

Consider:

void main() {
printf("Hello World");
return 0;
}

Assume that the string "Hello World" is stored at offset 0x28048
inside your executable. Moreover, the executable is known to
load at a base address of 0x00400000. In this case, the binary
code of your compiled and linked executable will somewhere refer
to the address 0x00428048 directly.

A disassembly of such a sample application, compiled with Visual
C++ 6, looks like this:

00401597 ...
00401598 push 0x00428048 ; the hello world string
0040159D call 0x004051e0 ; address of printf
0040159E ...

What is the problem with such a hardcoded address? If you stay
inside your own address space, there is no problem. However ...
once you move that code to another address space, all those
memory addresses will point to entirely different things. The
hello world string in my example is more than 0x20000 = 131072
bytes away from the actual program code. So, if you inject that
code to another process space, you would have to make sure that
at 0x00428048, there is a valid C string ... and even if there
was something like a C string, it would certainly not be
"Hello World". I guess you get the point.

This is just a simple example and does not even involve all the
problems that can occur. However, also the addresses of all
function calls are hardcoded, like the address of the printf
function in our sample. In another process space, these
functions might be somewhere else or they could even be missing
completely - and this leads to the most weird errors that you
can imagine. The only way to make sure that all the addresses
are correct and that every single CPU instruction fits, we have
to write the injected code in ASM.

Note: There are several working implementations for an outbound
detection bypass for software firewalls on the net using a
dynamic link library injection. This means, the implementation
itself consists of one executable and a DLL. The executable
forces a trusted process to load the DLL, and once it has been
loaded into the address space of this remote process, the DLL
itself performs any arbitrary networking task. This way to bypass
the detection works very well and it can be implemented in a high
level language easiely, but I dislike the dependency on an
external DLL, and therefore I decided to code a solution with one
single stand-alone executable that does the entire injection by
itself. Refer to (2) for an example of a DLL injection bypass.

Also, LSADUMP2 (3) uses exactly the same measure to grab
the LSA secrets from LSASS.EXE and it is written in C.

-[0x05] :: how to implement it -------------------------------------------

Until now, everything is just theory. In practice, you will
always encounter all kinds of problems when writing code like
this. Furthermore, you will have to deal with detail questions
that have only partially to do with the main problem. Thus,
let us leave the abstract part behind and think about how to
write some working code.

Note: I strongly recommend you to browse the source code in
[A] while reading this part, and it would most definitely be a
good idea to have a look at it before reading [0x0B].

First of all, we want to avoid as much hardcoded elements as
possible. And the first thing we need is the file path to the
user's default browser. Rather than generally refering to
"C:Program FilesInternet Exploreriexplore.exe", we will
query the registry key at "HKCRhtmlfileshellopencommand".

Ok, this will be rather easy, I assume you know how to query
the registry. The next thing to do is calling CreateProcess().
The wShowWindow value of the STARTUP_INFO structure passed to
the function should be something like SW_HIDE in order to keep
the browser window hidden.

Note: If you want to make entirely sure that no window is
displayed on the user's screen, you should put more effort
into this. You could, for instance, install a hook to keep all
windows hidden that are created by the process or do similar
things. I have only tested my example with Internet Explorer
and the SW_HIDE trick works well with it. In fact, it should
work with most applications that have a more or less simple
graphical user interface.

To ensure that the process has already loaded the most
essential libraries and has reached a generally stable state,
we use the WaitForInputIdle() call to give the process some
time for intialization.

So far, so good - now we proceed by calling VirtualAllocEx()
to allocate memory within the created process and with
WriteProcessMemory(), we copy our networking code. Finally,
we use CreateRemoteThread() to run that code and then, we only
have to wait until the thread terminates. All in all, the
injection itself is not all that hard to perform.

The function that will be injected can receive a single
argument, one double word. In the example that will be
presented in [0x0B], the injected procedure connects to
www.phrack.org on port 80 and sends a simple HTTP GET request.
After receiving the header, it displays it in a message box.
Since this is just a very basic example of a working firewall
bypass code, our injected procedure will do everything on its
own and does not need any further information.

However, we will still use the parameter to pass a 32 bit
value to our injected procedure: its own "base address". Thus,
the injected code knows at which memory address it has been
placed, in the conetxt of the remote process. This is very
important as we cannot directly read from the EIP register
and because our injected code will sometimes have to refer to
memory addresses of data structures inside the injected code
itself.

Once injected and placed within the remote process, the
injected code basically knows nothing. The first important
task is finding the kernel32.dll base address in the context
of the remote process and from there, get the address of the
GetProcAddress function to load everything else we need. I
will not explain in detail how these values are retrieved,
the entire topic cannot be covered by this paper. If you are
interested in details, I recommend the paper about Win32
assembly components by the Last Stage of Delirium research
group (4). I used large parts of their write-up for the
code that will be described in the following paragraphs.

In simple terms, we retrieve the kernel32 base address from
the Process Environment Block (PEB) structure which itself
can be found inside the Thread Environment Block (TEB). The
offset of the TEB is always stored within the FS register,
thus we can easiely get the PEB offset as well. And since
we know where kernel32.dll has been loaded, we just need to
loop through its exports section to find the address of
GetProcAddress(). If you are not familiar with the PE format,
don't worry.

A dynamic link library contains a so-called exports section.
Within this section, the offsets of all exported functions
are assigned to human-readable names (strings). In fact,
there are two arrays inside this section that interest us.
There are actually more than 2 arrays inside the exports
section, but we will only use these two lists. For the rest
of this paper, I will treat the terms "list" and "array"
equally, the formal difference is of no importance at this
level of programming. One array is a list of standard,
null-terminated C-strings. They contain the function names.
The second list holds the function entry points (the
offsets).

We will do something very similar to what GetProcAddress()
itself does: We will look for "GetProcAddress" in the first
list and find the function's offset within the second array
this way.

Unfortunately, Microsoft came up with an idea for their DLL
exports that makes everything much more complicated. This
idea is named "forwarders" and basically means that one DLL
can forward the export of a function to another DLL. Instead
of pointing to the offset of a function's code inside the DLL,
the offset from the second array may also point to a null-
terminated string. For instance, the function HeapAlloc() from
kernel32.dll is forwarded to the RtlAllocateHeap function in
ntdll.dll. This means that the alleged offset of HeapAlloc()
in kernel32.dll will not be the offset of a function that has
been implemented in kernel32.dll, but it will actually be the
offset of a string that has been placed inside kernel32.dll.
This particular string is "NTDLL.RtlAllocateHeap".

After a while, I could figure out that this forwarder-string
is placed immediately after the function's name in array #1.
Thus, you will find this chunk of data somewhere inside
kernel32.dll:

48 65 61 70 41 6C 6C 6F HeapAllo
63 00 4E 54 44 4C 4C 2E c.NTDLL.
52 74 6C 41 6C 6C 6F 63 RtlAlloc
61 74 65 48 65 61 70 00 ateHeap.

= "HeapAlloc

hakin9 3/2005 (10) le nouveau num?ro dans les kiosques

Lundi 6 juin 2005

Au sommaire du num?ro 3/2005 (10) de hakin9 :

* D?tecter le partage de connexion ill?gal
* Recherche et exploitation des bogues dans le code PHP
* Attaques par injection SQL avec PHP et MySQL
* M?thodes de dissimulation des modules du noyau dans Linux
* TEMPEST – ?missions compromettantes
* Honeypots – leurre contre les vers
* Prot?ger les logiciels Windows contre les pirates informatiques
* Conception de syst?mes de s?curit? physique

Sur le CD – Hakin9 Live – distribution Linux bootable contenant de nouveaux outils :
- honeyd
- AutoScan
- Rox
- AirCrack
- Ant
- tutoriaux pour les articles « Honeypots – leurre contre les vers » et « Attaques par injection SQL avec PHP et MySQL »

site officiel : http://www.hakin9.org/fr

Hakin9 3/2005 est sorti d?but mai !

Vendredi 20 mai 2005

Dans ce num?ro de Hakin9:

* D?tecter le partage de connexion ill?gal
* Recherche et exploitation des bogues dans le code PHP
* Attaques par injection SQL avec PHP et MySQL
* M?thodes de dissimulation des modules du noyau dans Linux
* TEMPEST – ?missions compromettantes
* Honeypots – leurre contre les vers
* Prot?ger les logiciels Windows contre les pirates informatiques
* Conception de syst?mes de s?curit? physique

Sur le CD Hakin9 Live – distribution Linux bootable contenant de nouveaux outils : honeyd, AutoScan, Rox, AirCrack, Ant, tutoriaux pour les articles « Honeypots – leurre contre les vers » et « Attaques par injection SQL avec PHP et MySQL »

Visitez le site de Hakin9 : www.hakin9.org/fr et t?l?chargez les articles en pdf.

A comparison of several host/file integrity checkers (scanners)

Lundi 28 février 2005

This study compares seven freely available (mostly open-source)
file/host integrity scanners (file integrity check
programs) with respect to the implementation of the core functionality,
i.e. questions like:

  • Can the program check all files that you may want to check ?
  • Can the program handle corner cases of the filesystem
    (that may e.g. result from an intrusion, or from simple errors of users) ?
  • Does the program warn about an incorrect configuration (which may
    cause it to check not in the way you intended) ?

The results presented here are based on test runs, and sometimes
also on investigation of the source code.
All test were performed on a Debian 3.0 Linux system.
In general, tests were performed only with console logging (stdout/stderr).

A comparison of several host/file integrity checkers (scanners)

By Rainer Wichmann

Caveat: The author of
this study is also the author of one of these file integrity scanners
(samhain). I.e. this study may be biased, because:

(a) the tests in this study are based on user feedback for samhain and
the authors personal opinion on what basic functionality a file
integrity scanner should provide, and

(b) a bug in samhain found during
this study was fixed (see the remark on samhain under
Remarks on individual programs).

If you think some of the results presented here are incorrect or outdated,
you are welcome to point out corrections.

The lack of a trademark sign does not imply the non-existence
of a trademark.

Content

What is the focus of this study ?
Explanation of table rows
Table of results

Remarks on individual programs
Relative speed
Logging options
Centralized management: osiris and samhain

What is the focus of this study ?

This study compares seven freely available (mostly open-source)
file/host integrity scanners (file integrity check
programs) with respect to the implementation of the core functionality,
i.e. questions like:

  • Can the program check all files that you may want to check ?
  • Can the program handle corner cases of the filesystem
    (that may e.g. result from an intrusion, or from simple errors of users) ?
  • Does the program warn about an incorrect configuration (which may
    cause it to check not in the way you intended) ?

The results presented here are based on test runs, and sometimes
also on investigation of the source code.
All test were performed on a Debian 3.0 Linux system.
In general, tests were performed only with console logging (stdout/stderr).

Thus, while some « features » of these programs
are mentioned that may be of interest for useability, the
focus of the study was on testing the scanner’s functionality,
not on listing and/or comparing their features.

Explanation of table rows

Version
The version number of the file integrity scanner.
Date
The release date of the file integrity scanner. For PGP
signed source code, this is the date of the PGP signature,
otherwise the date listed on the web site, or in the source.
PGP signature
Is the distributed source code PGP-signed ? If there
is no signature, it may be possible to put a trojan into
the source code (this has
happened in the past with several
high-profile security-related programs
)!
Language
The programming language of the file integrity scanner.
Required
Requirements (other than compiler or interpreter).
Log Options
What channels are supported for logging ?
DB sign/crypt
Does the scanner support signed or encrypted baseline databases ?
Conf sign/crypt
Does the scanner support signed or encrypted configuration files ?
Name Expansion
Does the scanner support expansion of file names (shell-style
globbing or regular expressions) in the configuration file ?
Duplicate Path
Does the scanner check the configuration file for duplicate
entries of files/directories (possibly with a different
checking policy for the duplicate) ? Strict checking of the
configuration file can help to avoid user errors.
PATH_MAX
Can the scanner handle a file whose path has the maximum
allowed length (4095 on Linux) ?
Root Inode
Can the scanner handle the « / » directory inode ?
This is the file with the shortest possible path, and also the
only one with a « / » in its filename, so
it may expose programming bugs (and you do want to check that
inode).
Non-printable
Can the scanner handle filenames with weird or non-printable
characters ? And if it can handle them internally, can it
report results in a useful way ?
Checked filenames were:

bash$ ls -l –quoting-style=c /
drwxr-xr-x 2 root root 4096 Feb 11 20:16 « 02020202″

As « 02″ is non-printable, incorrect reporting

will result in a report about removal of the root
directory (« / »), if this file is removed …

bash$ ls -l –quoting-style=c /opt
drwxr-xr-x 2 root root 1024 Feb 11 19:51 « this is_not_a_love_songwrong_filename »

As «  » is backspace, incorrect reporting
will result
in a report for the non-existing
file « this is_not_a_wrong_filename »

No User
Can the scanner handle files owned by a non-existing user
(UID with no entry in /etc/passwd) ?
No Group
Can the scanner handle files owned by a non-existing group
(GID with no entry in /etc/group) ?
Lock
Can the scanner handle files if another process has aquired a
mandatory (kernel-enforced) lock on it (yes, Linux has
that kind of locks) ?
It is possible to open() such a file for reading, but the read()
itself will block, so the scanner will hang indefinitely,
unless precautions are taken.
On Linux, mandatory locking requires a special mount option, thus
cannot usually be enforced by unprivileged users.
Race
File integrity scanners first lstat() a file to determine whether
it is a regular file, then open() it to read it for checksumming.
In between these two calls, a user with write access to the
directory may replace the file with a
named pipe. As a result, the open() call will block and the
scanner may hang indefinitely,
unless precautions are taken.
/proc
Is the scanner able to scan the /proc directory ?
On Linux, at least some files in /proc are
writeable and can be used to configure the kernel
at runtime, so you may want to check these files. However,
files in /proc may be listed with zero
filesize, even if you can read plenty of data from them.
Almost all scanners « optimize »

by not checksumming zero-length files, which
is incorrect in the
Linux /proc filesystem. Additionally, some files
may block on an attempt to read from them.

/dev
Has the scanner problems with the /dev directory ?
Crea/Del
Can the scanner report on missing (deleted) or newly created
files ?

Table of results (alphabetic order)

AIDE FCheck Integrit Nabou Osiris Samhain Tripwire
Version 0.10 2.07.59 3.02 2.4 4.0.5 1.8.4 2.3.1-2
Date Nov 30, 2003 May 03, 2001 Sep 08, 2002 Aug 30, 2004 Sep 27, 2004 Mar 17, 2004 Mar 04, 2001
PGP Signature YES NO YES YES YES YES NO
Language C Perl C Perl C C C++
Required libmhash md5sum (or md5) PARI/GP library + about 11 Perl modules OpenSSL 0.9.6j or newer GnuPG (only if signed config/database used)
Log Options stdout, stderr, file, file descriptor stdout, syslog stdout stdout, email central log server (email+file on server side) stderr, email, file, pipe, syslog, RDBMS, central log server, prelude, external script, IPC message queue stdout, file, email, syslog
DB sign/crypt NO NO NO sign NO sign sign+crypt
Conf sign/crypt NO NO NO NO NO sign sign+crypt
Name Expansion regex NO NO see remarks regex glob (shell-style) NO
Duplicate Path NO NO NO NO NO Warns Exits
PATH_MAX OK OK NO OK NO OK OK
Root Inode see remarks NO OK NO OK OK OK
Non-printable NO NO NO NO NO OK OK
No User OK OK OK see remarks OK OK OK
No Group OK OK OK see remarks OK OK OK
Lock OK Hangs Hangs Hangs Hangs Times out Hangs
Race Hangs Hangs Hangs Hangs Hangs OK Hangs
/proc NO NO Hangs Hangs NO OK NO
/dev OK OK OK OK OK OK OK
Crea/Del OK OK OK OK OK OK OK

Remarks on individual programs

AIDE

  • Segfaults on syntax error in config file (directory without
    policy).
  • When specifying the root directory, apparently ‘/.* R’ does
    not match ‘/’; ‘/$ R’ matches, but
    only if there is no other rule, so it’s useless (i.e. can’t
    check the root directory inode). Bug, or my misunderstanding of
    the regex syntax in the configuration file.
  • There is no tool to list the database (however, it is
    human-readable, not binary).

  • AIDE is the only scanner in this study that uses mmap() rather than
    read() to read a file. This is responsible for passing
    the ‘Lock’ test (the kernel denies mmapping a mandatorily locked file).
  • Judging from comments in the source code, AIDE
    tries to fix the ‘Race’ problem, but the solution does not work.
  • Omits checksum if file size is zero, which is incorrect for
    Linux /proc files.
  • For deleted / added files, only the path is printed.

FCheck

  • Not possible to define different policies (e.g. ignore size
    change for logfiles).

  • Omits checksum if file size is zero, which is incorrect for
    Linux /proc files.
  • Filenames in baseline database are not properly escaped, thus
    it is not possible to check files with non-printable characters.
    Some of them may even corrupt the baseline database (e.g.
    filenames with newlines).
  • No check on config file syntax is done. Duplicate entries
    are scanned twice, mis-typed directives (e.g. ‘Directoy =’
    instead of ‘Directory =’ are silently ignored).
  • No tool to dump/read the baseline database, which is barely
    human-readable.
  • If a directory is scanned recursively, the top level directory
    inode itself is never included. Thus it is impossible to check
    the root inode.

Integrit

  • You can have only one root directory in the config file, which
    makes it complicated to scan (only) some directories scattered
    over the file system. You need to run one integrit instance
    per root with different (per-root) configuration files.
  • Internally, all path names start with a double ‘/’.
    This may cause the observed ENAMETOOLONG error
    on valid long paths (?).
  • Usage is simple and straightforward. According to the
    documentation, the lack of
    features is intentional to simplify usage (which certainly is
    a valid argument, as long as one does not need advanced features).
  • Judging from comments in the source code, Integrit
    tries to fix the ‘Race’ problem, but the solution does not work.

  • Comment by Ed L Cashin (integrit developer):
    While there are integrit users who agree with you, I maintain that
    running integrit three times using three configuration files is
    cleaner and easier than running integrit once with one more cluttered
    configuration file. You can even take advantage of parallel I/O if
    the roots are on different devices.

Nabou

  • The script does not check whether a file is a socket, so
    it tries to checksum sockets (and hangs).
  • The config file syntax is somewhat apache-like, and
    easy to understand. Liked the config file syntax
    best of all tested programs.
  • Filename expansion (globbing, i.e. shell-style) is (only)
    supported for excluded files.
  • Nabou only prints user/group names. If there is no user (group) for
    a UID (GID), it will print whitespace
    rather than the numeric UID (GID).

  • With ‘use_ls’, nabou prints ls -l like line about matching files,
    but the file type is incorrect (e.g. devices are listed as
    regular files).
  • Dumping the database is possible (comma-separated format).

Osiris

  • Files with filename length of NAME_MAX are completely
    ignored (no database or log entries, except for the eventually
    modified
    timestamp of the parent directory).
  • Exclusion of subdirectories (option NoEntry) apparently
    does not work for the root directory (of course that could also
    be a user error on my side).
  • Omits checksum if file size is zero, which is incorrect for
    Linux /proc files.
  • For deleted / added files, only the path is logged.
  • The management command-line interface (CLI) has no support for
    non-printable chars (although ’space’ is accepted).
    This not only hides
    the true path for records, but makes the record details eventually
    unavailable (e.g. if a real path gets duplicated by
    ‘path + non-printable chars’), because the
    baseline database is binary (Berkeley DB) and only
    readable via the CLI (but see below).
  • There is no documented way to dump the baseline database to
    a human-readable format (format is Berkeley DB). Because details
    for new/missing files are not in the log, one has to lookup
    each record individually with the CLI, which is cumbersome.
    More precisely, there is a tool printdb in the
    src/tools/, which is not compiled by default
    (cd src/tools/ && make), and does not work as-is
    (edit printdb.c, remove code referencing « db2″ in main(),
    recompile,
    and use ‘printdb -a ‘ – not a big thing if you
    know C …).

Samhain

  • Suffers a bit from feature bloat, which causes probably a steeper
    learning curve than for other programs in this study.
  • In the ‘Lock’ test, samhain will timeout.
  • In the interest of full disclosure:
    version 1.8.3 had a bug with formatting of long reports that
    caused samhain to fail on tests with long paths. Version 1.8.4
    was fixed as a result of these tests.

Tripwire

  • The version 2.3.1-2 offered at
    sourceforge.net

    is not patched
    for the Email
    Reporting Format String Vulnerability
    published Jun 03, 2004.

  • The makefile cannot recognize that ‘make’ is GNU make, it insists
    on ‘gmake’. Made a symlink gmake->make to fix the problem.
  • Tripwire provides no details
    about modified/added/removed files, only path names,
    unless one uses twprint --report-level 4,
    which is pretty verbose.
  • Omits checksum if file size is zero, which is incorrect for
    Linux /proc files.
  • Apparently the open-source version of Tripwire has failed to attract
    any developer community. While it appears
    to be more solid than most other open-source integrity scanners
    in this study, I found the source code poorly commented and not
    particularly lucid.

Speed

Tests were performed under Debian 3.0 by checking two datasets, one
with 206 Mb, the other with 1.1 Gb. Absolute times depend on the
hardware – your mileage may vary.

All integrity checkers showed non-linear behaviour: the larger dataset
was checked with less speed (Mb / minute) than the smaller one.

Integrity checkers written in C
(AIDE, Integrit, Osiris, and Samhain) were I/O-limited (i.e. speed
was limited by the disk I/O), and
all were about equally fast (about 2 minutes for the small dataset,
18 minutes for the large one), with no significant
difference between database initialization and checking.

Tripwire (written in C++) was slower
(4 minutes / 26 minutes)
than AIDE, Integrit, and Samhain, again with no significant
difference between database initialization and checking.

For the smaller dataset, the two Perl scripts (FCheck and Nabou)
were faster than Tripwire, but slower than the C programs.

For the larger dataset, the performance of the Perl scripts
was much worse:
Nabou took 85 minutes to initialize the database,
and 41 minutes for checking.
FCheck (also Perl) needed 40 minutes for initializing, and 54 minutes
for checking.

Logging options

This is an overview over the logging options provided by
different scanners. This information is mostly taken from the
documentation, and usually not verified.

AIDE: reports can be printed to stdout, stderr,
plaintext file, or to an open file descriptor.
Any combination of these can be used, but the verbosity level cannot
be set individually.

Fcheck: reports are printed to stdout, and optionally
logged to syslog (via the logger standard utility).

Integrit: reports are printed to stdout.

Nabou: nabou prints reports to stdout, or sends
them via email.

Osiris: osiris clients only send scan results to the
central server, which
in turn logs reports to plaintext files and can send
emails.

Samhain: samhain can log to stdout, plaintext file,
and syslog. Also supported are: sending reports by email,
sending reports to a central server, inserting reports
into an RDBMS (MySQL, PostgreSQL, Oracle, or unixODBC), sending reports to
a Prelude IDS system, writing reports to a
named pipe, calling a user-defined external application to
process reports (e.g. to send an SMS to a mobile phone), and providing
reports via an IPC message queue.

For each supported logging facility, the level of logging can be
configured individually. Any combination of facilities can be used in
parallel.

Tripwire: tripwire prints reports on stdout, and
stores them in binary files. Optionally it can send reports
by email.
It can also log to
syslog (in a very terse way, where only the number of violations
are logged).

Centralized management: osiris and samhain

Osiris and samhain are special insofar as they are the only
host integrity scanners in this study that
provide built-in support for centralized logging and management.

Both systems are able to collect reports/data from clients on a central
server, and to store baseline databases and client configurations on
the central server. Configuration changes and updates of the baseline
database can be performed centrally rather than on individual hosts
monitored by the system.

General design differences: push vs. pull

Centralized logging and management requires a client/server system
where at least one side has to listen on the network for connections,
and thus is potentially vulnerable to remote attacks.

For osiris, scan requests are pushed from the central server to the
individual scanner clients. Thus the client, which needs root
privileges to open and checksum privileged files, also listens
on the network.
On Unix/Linux, this problem is mitigated by using
privilege separation (similar to OpenSSH): there is a
privileged process that only handles actions that require root
privileges, and an unprivileged (sub-)process that does most
of the work (including network connections).

However, on MS Windows, privilege separation is not supported.

Samhain works the other way: clients pull the baseline database from
the server, and return reports. I.e. here the server has an open
port, and the server does not need root privileges. Actually the
samhain server (called ‘yule’) will only run as an unprivileged
user (it drops root privileges if started with), and can be
chrooted.

Both samhain and osiris use encrypted client/server connections.

With osiris, (only) the server must authenticate to the client.
However, similar to samhain, osiris clients negotiate a shared
secret with the server that is kept in memory after startup, thus
attempts to replace the client can be detected once it has started.
Samhain
uses mutual authentication (where the client’s credentials are located
within the client executable). Upon successful authentication, a
shared secret is negotiated that is kept in memory.

With osiris, clients send back snapshots of the file system, which are
compared to the baseline on the server side, and stored in the same
location as the baseline database. Thus the server (which is potentially
vulnerable to malicious clients) needs write access to the directory
where baseline data is stored.

Samhain clients only send back reports on filesystem modifications.
These reports can be used to update the baseline database on the server
via the central management console. The server only needs read access
to the baseline data.

Additional features

In addition to file integrity checking, samhain can optionally
check for kernel rootkits, search the filesystem for SUID binaries,
check mount points (and their mount options), and
watch login/logout events.

Osiris can report if (and which) users/groups have been added to
/etc/passwd and/or /etc/groups. Also, it can
report on new kernel modules loaded (in a limited way, you can do that
with samhain by monitoring the checksum of /proc/modules).

Samhain offers a large choice of different logging facilities
(both on the client as well as on the server side) that
can optionally be used simultaneously. Osiris clients only report
to the central server, which in turn logs reports to files and
optionally can send email notifications.

Both samhain and osiris support a central management interface.
In the case of osiris, this is a command-line interface (CLI) that
is part of the osiris package. For samhain, the management interface
is a PHP web-based interface that is available as a separate
package (beltane).

Osiris supports MS Windows natively, while samhain requires a POSIX
emulation (like e.g. Cygwin).

M?canismes d’authentification HTTP/HTTPS

Vendredi 4 février 2005

Cette br?ve aborde les solutions mises en oeuvre dans les applications de type ? web ? pour assurer le service d’authentification.
Le m?canisme de transmission des accr?ditations du client vers le serveur est trait? essentiellement. Le processus de v?rification (aupr?s d’un fichier ? plat ?, index?, d’une base de donn?es ou encore d’un annuaire LDAP) ne sera g?n?ralement pas abord

M?canismes d’authentification HTTP/HTTPS


Cette br?ve aborde les solutions mises en oeuvre dans les applications de type
? web ? pour assurer le service d'authentification.
Le m?canisme de transmission des accr?ditations du client vers le serveur est
trait? essentiellement. Le processus de v?rification (aupr?s d'un fichier
? plat ?, index?, d'une base de donn?es ou encore d'un annuaire LDAP) ne sera
g?n?ralement pas abord?.

1. Authentification HTTP

Le protocole HTTP est un protocole client/serveur, sans ?tat. La sp?cification du protocole HTTP dans sa version 1.1 est d?crite dans la RFC 2616 [1]. Deux m?thodes d'authentification HTTP, dites Basic et Digest, sont d?crites dans la RFC 2617 [2]. Les accr?ditations sont transmises du client vers le serveur via les ent?tes HTTP, elles authentifient les requ?tes effectu?es par le client individuellement (lorsque le keep-alive est utilis?, les diff?rentes requ?tes effectu?es sur la m?me connexion TCP sont chacune authentifi?es).

1.1. Authentification HTTP Basic

1.1.1 Authentification du client

La m?thode d'authentification dite ? HTTP Basic ? ne met en oeuvre aucun service de confidentialit? quant ? la transmission des accr?ditations au serveur. Il s'agit d'une m?thode commun?ment employ?e, conjointement au protocole SSL/TLS. Le m?canisme est le suivant : a. Le client ?met une requ?te ? destination d'une ressource prot?g?e, le r?pertoire identifi? par l'URI /basic par exemple. La requ?te simple correspondante est : GET /basic/ HTTP/1.0 b. Le serveur r?pond avec le code d'erreur 401 (Unauthorized), et sa r?ponse comporte l'ent?te WWW-authenticate, sp?cifiant le type d'authentifcation attendue : HTTP/1.1 401 Authorization Required WWW-Authenticate: Basic realm="Basic realm" c. L'ent?te renvoy? par le serveur est interpr?t? par le navigateur. Celui-ci pr?sente une fen?tre ? l'utilisateur, l'invitant ? entrer ses accr?ditations. Le navigateur r?it?re la pr?c?dente requ?te, en int?grant l'ent?te Authorization, suivie de la concat?nation du nom d'utilisateur, de ":" et du mot de passe, le tout encod? en base64. Par exemple, pour la paire login/mot de passe dummy:secret : $ echo -n 'dummy:secret' | openssl enc -a -e ZHVtbXk6c2VjcmV0 La requ?te r?sultante est donc la suivante : GET /basic/ HTTP/1.0 Authorization: Basic ZHVtbXk6c2VjcmV0 Les accr?ditations sont donc transmises en clair (encod?es) ? _chacune_ des requ?tes (dans la mesure o? le protocole HTTP est dit ? sans ?tat ?). Il ne s'agit pas de chiffrement ! Les accr?ditations sont mises en cache par le navigateur, ?vitant ainsi au client d'avoir ? les fournir ? chacune des requ?tes ?mises. Cette m?thode d'authentification est donc particuli?rement sensible ? une ?coute du trafic. Si un service de confidentialit? est mis en oeuvre ? travers un m?canisme de chiffrement au niveau transport/session (SSL/TLS par exemple), seul un serveur malveillant pourra acc?der ? ces accr?ditations (si l'on exclue la faille des protocoles SSL/TLS permettant, par analyse temporelle pour un attaquant actif, de d?crypter un bloc de donn?es chiffr? suivant un algorithme sym?trique en mode CBC). d. Le serveur renvoie la page prot?g?e (HTTP/1.1 200 OK). Au niveau des journaux, la ligne suivante est g?n?r?e : 127.0.0.1 - dummy [30/Jan/2003:13:40:50 +0100] "GET /basic/ HTTP/1.0" 200 820 "-" " ""w3m/0.3.1+cvs-1.411"" Cette ligne indique que l'utilisateur 'dummy' a acc?d? avec succ?s au r?pertoire /basic (code 200). Remarques : ?lorsqu'un login apparemment inconnu appara?t dans les journaux, penser ? regarder le code de retour correspondant ?le mot de passe peut ?tre conserv? dans une base de donn?es, un annuaire LDAP etc. Il peut ?galement s'agir d'un mot de passe ? usage unique, de type S/KEY, PAM_OPIE etc. Seule est d?crite ici la m?thode employ?e par le client pour transmettre ses accr?ditations au serveur (usage des ent?tes HTTP en l'occurrence).

1.1.2 Suivi de session authentifi?e

Dans le cas de l'authentification de type ? Basic ?, le ? suivi ? de session est r?alis? par la retransmission syst?matique des accr?ditations (? chacune des requ?tes).

1.2. Authentification HTTP Digest

1.2.1 Authentification du client

La m?thode d'authentification dite ? HTTP Digest ? met en oeuvre un service de confidentialit? pour les accr?ditations, mais pas pour les donn?es transf?r?es (c?t? client, comme c?t? serveur). Elle introduit des m?canismes contre le rejeu, et prot?ge le client contre un serveur malveillant, ou potentiellement corrompu, dans la mesure o? les accr?ditations ne sont jamais connues par ce dernier. Il s'agit d'un m?canisme de type ? challenge/response ?, jou? pour chaque ressource demand?e : tr?s simplement, le serveur envoie un challenge au client, ce dernier r?pond par une valeur d?riv?e de ce challenge et d'un secret qu'il partage avec le serveur. Le serveur s'assure alors que le client poss?de effectivement le secret en calculant ? son tour la r?ponse et v?rifiant la coh?rence des deux. Les traces effectu?es dans cette br?ve ont ?t? r?alis?es suite ? l'utilisation du module mod_auth_digest d'Apache, et non du module standard mod_digest (qui met en oeuvre un support partiel de l'authentification digest). Le m?canisme, dans le cas le plus simple (authentification sans int?grit?), est le suivant : a. Comme pr?c?demment, le client ?met une requ?te ? destination d'une ressource prot?g?e, identifi?e par son URI : GET /digest/ HTTP/1.0 b. La r?ponse du serveur, l?g?rement ?pur?e, est comme suit : HTTP/1.1 401 Authorization Required WWW-Authenticate: Digest realm="Digest Realm", nonce="uD85Pg==a766f996fa716e4d4592943b5762c73958f0378b", algorithm=MD5, domain="/digest", qop="auth" Elle comporte, notamment : - un challenge (? nonce ?) - un identifiant d'algorithme (? algorithm ?) : la fonction de hachage MD5, dans ce cas Pour chaque page prot?g?e, le client va calculer une r?ponse ? partir de ces param?tres en utilisant son mot de passe. En pratique, le challenge n'est pas renouvel? ? chaque requ?te -- ce qui limite le non-rejeu. c. Le client calcule en r?ponse l'empreinte qui sera ?mise via l'ent?te Authorization, comme lors d'une authentification de type ? Basic ? : (la m?thode de calcul est donn?e dans la RFC 2617) : request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) <"> avec : KD (secret,data) = H(concat(secret, ":", data) ? H ?tant la fonction de hachage MD5 (et concat signifiant la concat?nation des cha?nes) A1 = unq(username-value) ":" unq(realm-value) ":" passwd A2 = Method ":" digest-uri-value Quelques remarques : - unq correpond ? ? unquoted ?. - le param?tre cnonce-value est choisi par le client (d'o? le pr?fixe 'c'). Dans ce cas, la valeur choisie par le navigateur donnera un champ unq(cnonce-value) ?gal ? be09d67c532a3a02. Le calcul effectu? par le client est donc comme suit (le nom d'utilisateur ?tant 'dummy', et le mot de passe correspondant 'secret') : * Pour H(A1) : $ echo -n 'dummy:Digest Realm:secret' | openssl md5 0c440e535a0afdc350f3f8ba0aa2f271 Remarque : cette empreinte est celle conserv?e c?t? serveur (dans le fichier .htaccess, g?n?r? par htdigest(1) pour un serveur Apache). Le serveur n'a donc pas la connaissance du mot de passe de l'utilisateur. La connaissance du mot de passe en clair n'est donc pas n?cessaire c?t? serveur -- au contraire de m?canismes d'authentification similaire de type apop. * Pour H(A2) : $ echo -n 'GET:/digest/' | openssl md5 9942091bc79111e32fecde3962416017 Remarque : la QUERY_STRING (cha?ne figurant apr?s '?') est prise en compte dans l'URI pour le calcul de l'empreinte ! Au final, la r?ponse est la suivante : $ echo -n '0c440e535a0afdc350f3f8ba0aa2f271:uD85Pg==a766f996fa716e4d4592943b5762c7395 8f0378b:00000001:be09d67c532a3a02:auth:9942091bc79111e32fecde3962416017' | openssl md5 Soit : 46f122dedae2a5f8ffbf82d6ad605304 La requ?te r?sultante est la suivante, avec la r?ponse dans le champ 'response' : GET /digest/ HTTP/1.1 Authorization: Digest username="dummy", realm="Digest Realm", nonce="uD85Pg==a766f996fa716e4d4592943b5762c73958f0378b", uri="/digest/", algorithm=MD5, response="46f122dedae2a5f8ffbf82d6ad605304", qop=auth, nc=00000001, cnonce="be09d67c532a3a02" d. Le serveur r?pond positivement, avec l'ent?te Authentication-Info : HTTP/1.1 200 OK Authentication-Info: rspauth="1d4d5a9d920fb22197471146c613e767", cnonce="be09d67c532a3a02", nc=00000001, qop=auth L'ent?te rspauth permet une authentification mutuelle. Le serveur prouve en effet par ce biais qu'il conna?t effectivement le mot de passe du client : le calcul effectu? est identique ? celui effectu? par le client, au d?tail pr?s que le param?tre A2 est pris ?gal ? ':/digest/', la m?thode n'est pas prise en compte. Remarque : le non-rejeu est situ? au niveau ? session HTTP ? (par abus de langage, session pendant laquelle est utilisable le ? nonce ?), et non au niveau ? requ?te HTTP ? : dans le cas du module Apache mod_auth_digest, la dur?e de vie du param?tre nonce est fix?e par le param?tre AuthDigestNonceLifetime. Au del? de cette dur?e, le nonce est renouvel?, et le client doit s'authentifier ? nouveau, et explicitement (le serveur lui renvoie un code d'erreur 401). On peut donc raisonnablement consid?rer que seul un service de confidentialit? des accr?ditations est mis en oeuvre, et non un v?ritable service de non-rejeu. Outre les ind?niables avantages qu'elle pr?sente en terme de confidentialit? des accr?ditations (vis ? vis d'un intercepteur comme d'un serveur malveillant, vers lequel le trafic aurait ?t? d?tourn?), la m?thode d'authentification HTTP ? Digest ? est surtout connue, ou notoire, pour l'incompatibilit? constat?e entre le serveur HTTP le plus r?pandu, Apache, d'une part, et le navigateur le plus r?pandu d'autre part, MSIE. Cette incompatibilit? tient ? une interpr?tation incorrecte de la RFC 2616 par MSIE (et IIS, par extension). Le court CGI qui suit permet d'illustrer cette constatation : $ cat ./digest/printenv.cgi #!/usr/bin/perl print "Content-Type: text/plain\n\n"; foreach $key (sort keys(%ENV)) { print "$key = $ENV{$key}\n"; } Une requ?te effectu?e par MSIE sur l'URI /digest/printenv.cgi avec la QUERY_STRING param=value donne le r?sultat, ?pur?, suivant : GET /digest/printenv.cgi?param=value HTTP/1.0 Authorization: Digest username="dummy", realm="Private Area", qop="auth", algorithm="MD5", uri="/digest/printenv.cgi", nonce="KPw3Pg==08fc61d5b52c87dbda7b038a1c741fd82ae12756", nc=00000007, cnonce="67fa5778e4fac113bc53ca089cf10fa6", response="5045459a94e92c1844e8e1e5a6309c3e" La r?ponse du serveur se solde par un ?chec (code d'erreur 400) : HTTP/1.1 400 Bad Request [...] <H1>Bad Request</H1> Your browser sent a request that this server could not understand.<P> Digest: uri mismatch - </digest/printenv.cgi> does not match request-uri &lt;/digest/printenv.cgi?param=value&gt;<P> Ainsi, MSIE calcule sa r?ponse en prenant comme valeur pour le param?tre digest-uri-value la variable REQUEST_URI (/digest/printenv.cgi?param=value), mais _sans_ la partie QUERY_STRING (param=value). Des CGI dont l'acc?s est restreint et requ?rent une authentification de type digest doivent par cons?quent recevoir leurs param?tres sur leur entr?e standard (pass?s dans le corps de la requ?te, via POST), et non plus via l'environnement par la variable QUERY_STRING. Une autre m?thode sugg?r?e [3] : utiliser la variable PATH_INFO. Cette variable d'environnement est initialis?e lorsqu'un ? chemin ? est concat?n? au CGI ? ex?cuter. Par exemple, la requ?te GET /digest/printenv.cgi/chemin/supplementaire?param=value va initialiser les variables PATH_INFO (/chemin/supplementaire) et QUERY_STRING (param=blah).

1.2.2 Suivi de session authentifi?e

Comme pr?c?demment, le suivi de session est r?alis? par la transmission, ? chaque requ?te, des accr?ditations. Ces derni?res sont transmises et calcul?es pour chaque requ?te, d?riv?es des param?tres d'authentification mis en cache par le navigateur.

2. Authentification HTTPS

2.1 Authentification du client

Le protocole HTTPS met en oeuvre des services d'authentification des parties (unilat?rale ou mutuelle, ? partir du protocole SSLv3), d'authenticit? et de confidentialit? des donn?es pendant toute une session SSL/TLS (la session ? multiplexant ? les connexions TCP et requ?tes HTTP associ?es). La m?thode d'authentification HTTP de type Basic est le plus souvent utilis?e conjointement ? SSL/TLS, afin d'assurer la confidentialit? des accr?ditations. Le protocole HTTPS introduit, par l'interm?diaire des protocoles SSLv3/TLS, une m?thode d'authentification du client par certificat X.509v3. Cette m?thode a ?t? tr?s bri?vement abord?e dans une pr?c?dente br?ve pr?sentant la biblioth?que OpenSSL (http://www.hsc.fr/ressources/breves/ssl_configuration.html ), le d?tail du m?canisme ne sera donc pas abord?. Dans ce cas, le serveur et l'application peuvent s'appuyer sur la session SSL/TLS mutuellement authentifi?e pour assurer un suivi de session authentifi?e de l'utilisateur, au niveau applicatif par cons?quent et non plus au niveau HTTP.

2.2 Suivi de session authentifi?e

Dans le cas d'une authentification du client par certificat X.509, un certain nombre de param?tres discriminants permettent d'identifier l'utilisateur authentifi?. Ces param?tres discriminants sont, ? titre d'exemple, les champs DN (distinguishedName), CN (commonName) ou encore l'adresse de courrier ?lectronique d?riv?s du certificat pr?sent? lors de la n?gociation des param?tres de la session SSL/TLS. La liste des variables d'environnement initialis?es par mod_ssl, d?rivant du certificat client, figurent ? l'URL suivante : http://www.modssl.org/docs/2.8/ssl_reference.html Dans le cas o? le client n'est pas authentifi? par certificat X.509, le param?tre discriminant, dont pourrait ?tre d?riv? un identifiant de session au niveau de l'application, ne peut plus plus provenir du certificat client. Les seuls param?tres ? candidats ? sont ceux n?goci?s lors du handshake SSL/TLS, et plus particuli?rement le session_id sur 32 bits (variable d'environnement SSL_SESSION_ID pour mod_ssl, initialis? par le serveur). Le standard RFC2246 (? The TLS Protocol Version 1.0 ?) [4] stipule, ? propos de la dur?e de vie de cet identifiant : ? An upper limit of 24 hours is suggested for session ID lifetimes, since an attacker who obtains a master_secret may be able to impersonate the compromised party until the corresponding session ID is retire ?. La limite sup?rieure de dur?e de vie de cet identifiant est donc propos?e ? 24 heures - ce qui est a priori largement suffisant pour suivre une session authentifi?e. Cependant, certaines versions d'Internet Explorer (IE 5.0/5.01(SP1)/5.5(SP1) 9x/NT4) ren?gocient ce param?tre toutes les 2 minutes (Q265369) [5]. Ce comportement peut ?tre modifi? dans la base de registres : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL La clef ClientCacheTime est positionn?e ? 120000 (ms) par d?faut. Le suivi de session fond? sur le session_id SSL/TLS uniquement est donc, malheureusement, inexploitable en pratique. La solution, lorsque l'authentification par certificat client n'est pas mise en oeuvre, repose donc sur les m?canismes usuels utilis?s dans HTTP - qui seront bri?vement abord?s par la suite, ave la notion d'identifiant de session authentifi?e (cookie ou autre). Remarque : outre le suivi de session authentifi?e au niveau ? applicatif ?, ce renouvellement fr?quent du session_id SSL/TLS pose le probl?me de l'?quilibrage de charge sur des flux HTTPS (pour s'appuyer sur d'autres param?tres que l'adresse IP notamment). ? cet effet, la biblioth?que OpenSSL 0.9.7 introduit la fonction SSL_CTX_set_generate_session_id(), permettant au serveur HTTPS de g?n?rer des sessions id SSL dont le pr?fixe est statique et exploitable au niveau de l'?quipement de r?partition de charge, pour peu que celui-ci permette un tel param?trage.

3. Authentification ? applicative ?

3.1 Authentification du client

L'authentification entendue au sens ? applicatif ? consiste en la v?rification par l'application elle-m?me des accr?ditations pr?sent?es par le client. Il ne s'agit plus, pour cette derni?re, de s'appuyer sur une authentification r?alis?e au niveau HTTP ou SSL/TLS, par l'interm?diaire des variables d'environnement initialis?es par le serveur par exemple : REMOTE_USER pour une authentification HTTP, SSL_CLIENT_DN pour une authentification par certificat client etc. Les accr?ditations ne sont donc plus transmises via les ent?tes HTTP (m?thodes d'authentification HTTP pr?c?demment d?crites), mais par un m?canisme indiqu? par la ? requ?te simple ? de la requ?te. La m?thode utilis?e peut ?tre GET (via la QUERY_STRING, concat?n?e ? l'URI) ou POST (dans le corps de la requ?te), indiff?remment. Remarque : il est clair que les langages de scripts, comme PHP par exemple, permettent ?galement de mettre en oeuvre des m?canismes d'authentification de type HTTP ? Basic ?, dans la mesure o? ils peuvent ?galement lire/?crire les requ?tes/r?ponses. Les accr?ditations sont g?n?ralement transmises via un tunnel chiffr?/authentifi? unilat?ralement (SSL/TLS avec authentification du serveur par certificat), afin d'assurer la confidentialit? des accr?ditations transmises. Remarque : une solution alternative, et rarement employ?e, consiste ? utiliser un m?canisme local au client, de type javascript, pour g?n?rer des accr?ditations ? ? usage unique ? ? partir des param?tres d'authentification, et envoyer ces accr?ditations au serveur (? travers un canal non chiffr?). ? titre d'exemple, la mise en oeuvre d'une authentification de type CHAP, par un javascript c?t? client et des scripts ASP c?t? serveur [6].

3.2 Suivi de session authentifi?e

Le suivi de session est ? la charge de l'application. Dans les cas HTTP ? Basic ? et ? Digest ? (avec ou sans surcouche SSL/TLS), il y a authentification ? chaque requ?te, avec une gestion d'?tat (ensemble de variables associ?es ? une ? session ? applicative, ? un ensemble de requ?te provenant d'un m?me utilisateur) c?t? serveur. Le m?me principe peut ?tre adopt? pour une application. Mais le processus est tout autre, en pratique : l'utilisateur commence par s'authentifier au niveau d'un formulaire, les param?tres sont transmis (via la m?thode GET ou la m?thode POST) pour validation, puis un identifiant de session est attribu? et sera utilis? pour lier les requ?tes suivantes au contexte initialis? c?t? serveur -- d'o? la notion de session applicative. Les m?thodes de passage d'identifiant de session sont multiples. ? titre d'exemple, une liste non exhaustive de techniques rencontr?es : - cookie - g?r? par le serveur HTTP : ? Fix? c?t? serveur, par l'ent?te Set-Cookie ou via javascript : Set-Cookie: SESSIONID=e3e5f57ca9adbbb19a21b1c2a22be987; path=/ ? ?mis par le client, par l'ent?te Cookie : Cookie: SESSIONID=e3e5f57ca9adbbb19a21b1c2a22be987 Le cookie est int?ressant d'un point de vue ? s?curit? ?, avec l'introduction de fonctionnalit?s via les param?tres ? secure ? (permettant la transmission du cookie via un tunnel SSL/TLS uniquement, afin d'?viter son interception ? passive ? par ?coute du trafic) ou encore ? httponly ? (introduit par le Service Pack 1 d'IE 6, prot?geant ce navigateur contre les attaques de type XSS en interdisant la manipulation du cookie par des scripts, type javascript notamment -- tout en demeurant vuln?rable ? un usage d?tourn? de la m?thode HTTP TRACE [7] qu'il convient donc de d?sactiver au niveau des serveurs HTTP). - m?thode GET : GET /page.php?SESSIONID=e3e5f57ca9adbbb19a21b1c2a22be987 - m?thode POST : POST /page.php Content-type: application/x-www-form-urlencoded Content-length: 42 SESSIOND=e3e5f57ca9adbbb19a21b1c2a22be987 - insertion dans la REQUEST_URI (mod_rewrite + PHP4 [8] ) : GET /SESSIOND=e3e5f57ca9adbbb19a21b1c2a22be987/index.php - r??criture d'URL GET /index.php Host: e3e5f57ca9adbbb19a21b1c2a22be987.webserver.tld La s?curit? relative ? la g?n?ration d'un cookie (ou d'un identifiant de session plus g?n?ralement) d'une part, et ? son suivi d'autre part sont deux sujets non triviaux qui d?bordent du cadre de cette br?ve. Cependant, des r?gles de bonnes pratiques existent : le projet OWASP [9] ?met un certain nombre de recommandations concernant ces aspects. Le point essentiel est qu'il convient d'identifier les requ?tes (ind?pendantes les unes des autres, dans la mesure o? HTTP est ? sans ?tat ?) d'un m?me utilisateur authentifi?, et les associer ? un contexte unique g?r? c?t? serveur et initialis? suite ? une phase d'authentification. Certaines suites logicielles de SSO, par exemple, mettent en oeuvre des m?canismes tout ? fait satisfaisants, en g?n?rant par exemple un nouvel identifiant de session ? chaque requ?te et permettant de se pr?munir contre les attaques de type XSS. La gestion correcte de cet identifiant assurera la s?curit? du service d'authentification, mais cet identifiant devra par la suite ?tre correctement corr?l? avec les habilitations des utilisateurs, afin de mettre un place une v?ritable gestion des autorisations, et non une simple ? personnalisation ? des menus affich?s ? l'utilisateur... Le manque de cloisonnement entre les contextes utilisateurs au niveau applicatif sont aussi dangereux que les vols de session par XSS, et leur r?solution ne passe pas forc?ment par l'ajout d'options diverses ? un cookie, ou encore ? un filtrage syst?matique du contenu des pages renvoy?es au client, mais ? une conception s?curis?e de l'application m?me, qu'une solution de filtrage ou de SSO ou autre, aussi ?volu?e soit-elle, ne remplacera pas.

4. Ressources

[1] RFC2616 -- ? Hypertext Transfer Protocol -- HTTP/1.1 ? http://www.ietf.org/rfc/rfc2616.txt [2] RFC2617 -- ? HTTP Authentication: Basic and Digest Access Authentication ? http://www.ietf.org/rfc/rfc2617.txt [3] ApacheWeek: Issue 317, 20th December 2002 http://www.apacheweek.com/issues/02-12-20 [4] RFC2246 -- ? The TLS Protocol -- Version 1.0 ? http://www.ietf.org/rfc/rfc2246.txt [5] ? Microsoft Knowledge Base Article - 265369 ? http://support.microsoft.com/default.aspx?scid=kb;EN-US;q265369 [6] ? MD5-based login scheme in Javascript ? http://builder.cnet.com/webbuilding/pages/Programming/Scripter/013100/ss02.html [7] ? Cross-Site Tracing -- XST ? http://www.cgisecurity.com/whitehat-mirror/WhitePaper_screen.pdf [8] Gestion des sessions dans PHP4 http://lxr.php.net/source/php4/ext/session/session.c [9] ? The Open Web Application Security Project ? http://www.owasp.org

Se proteger des buffers overflows

Mardi 1 février 2005

Cet article traitera des protections contre l’exploitation des d?bordements de buffer. Chaque article pr?sentera diff?rentes m?thodes de protections, et ce contre quoi elle prot?ge pr?cis?ment.

Introduction

Cet article est le premier d’une s?rie qui traitera des protections
contre l’exploitation des d?bordements de buffer. Chaque article
pr?sentera diff?rentes m?thodes de protections, et ce contre quoi elle
prot?ge pr?cis?ment. En effet, il ne faut pas installer ces
protections et croire qu’elles constituent un rempart
infranchissable.

Avant de rentrer dans le vif du sujet d?s le prochain article, nous
rappelons ici quelques notions indispensables ? la compr?hension de la
suite, comme le format ELF des binaires Linux, l’organisation de la
m?moire des processus et la PLT/GOT (Procedure Linkage Table/Global
Offset Table).

L’organisation de la m?moire

Rappels sur le format ELF

Le format ELF (Executable and Linking Format — format
d’ex?cution et d’?dition de liens) est le format actuel des binaires
sous Linux. Il a remplac? le format a.out pour diff?rentes
raisons :

  • plus souple et plus pratique gr?ce ? sa structure
    (cf. elf.h) ;
  • possibilit? de cr?er des biblioth?ques partag?es ;
  • format support? sur plusieurs autres syst?mes ;

La principale chose ? conna?tre sur ce format est son
organisation. En fait, un binaire au format ELF est d?coup? en
plusieurs sections. Chacune poss?de sa propre finalit?. Par
exemple, la section .text contient les instructions
machines du programme, c’est-?-dire son code ex?cutable. Ainsi, une
fois charg?e en m?moire, comme un processus ne peut modifier son
propre code, toutes les autres instances de ce programme utiliseront
cette m?me portion de m?moire. La section .text est
charg?e une seule et unique fois pour tous les processus issus de ce
binaire.

La commande file fournit les renseignements relatifs
au format d’un fichier :

$ file /usr/bin/vim
/usr/bin/vim: ELF 32-bit LSB executable, Intel 80386, version 1,
dynamically linked (uses shared libs), stripped

Le format ELF poss?de ?galement une table des symboles,
c’est-?-dire une liste de tous les symboles (labels, noms de
fonctions, adresses de variables, etc.) qui sont d?finis ou
r?f?renc?s dans le fichier, ainsi que des informations sur ces
symboles. Examinons les informations fournies par
hello.c :

  /* hello.c */
  #include 

  char world[6] = "world";
  char * empty;

  main(int argc, char ** argv )
  {
    printf( "Hello %s
", argv[1] );
  }

Avec gcc, nous transformons ces instructions en
fichier objet, puis la commande nm en affiche le
contenu :

  $ gcc -c hello.c -o hello.o
  $ ls
  hello.c  hello.o
  $ nm hello.o
  00000004 C empty
  00000000 t gcc2_compiled.
  00000000 T main
           U printf
  00000000 D world

La commande nm affiche tous les symboles contenus
dans un fichier objet. Pour chaque symbole, nm
donne :

  • la valeur du symbole ;
  • son type (en minuscule, le symbole est local, en majuscule, il
    est global) :

    • B : le symbole se trouve dans la zone m?moire
      .bss ;
    • D : le symbole se situe dans la zone m?moire des donn?es
      initialis?es .data ;
    • C : ce flag sert pour les symboles qui ne sont pas
      initialis?s apr?s la compilation. Dans notre exemple,
      empty est d?fini mais pas encore
      initialis?. S’il ne l’est nulle part, son type changera
      alors en B ;
    • T : le symbole est dans la zone m?moire .text
      (code) ;
    • U : le symbole est ind?fini (undefined).
    • Il existe de nombreux autres types d?crits dans la page
      info nm ;

  • le nom du symbole.

Dans le fichier objet, la fonction printf() n’est pas
encore d?finie. Dans le fichier ex?cutable, il faudra conna?tre
l’emplacement de cette fonction (i.e. la biblioth?que et son
adresse dans celle-ci). Comme cette fonction est externe, un m?canisme
de r?adressage est pr?vu. Tout d’abord, il contient un d?calage
(offset) dans la table des symboles qui r?f?rence le symbole
lui-m?me. Ensuite, il rec?le un d?calage dans la section

.text qui r?f?re l’adresse du code de la fonction. Enfin,
un tag indique le type de r?adressage utilis?.

Lors de l’?dition de liens, le linker recherche l’adresse
r?elle de la fonction printf(). Une fois d?couverte, elle
est recopi?e en m?moire afin que les appels ? la fonction soient
effectu?s sans repasser par cette ?tape de r?solution.

Ce m?canisme d?crit de mani?re tr?s g?n?rale le comportement de la PLT
(Procedure Linkage Table) et de la GOT (Global Offset Table). De
plus amples d?tails sont donn?s ci-apr?s.

Les r?gions m?moire d’un processus

Nous ne d?taillons pas ici le fonctionnement de la m?moire d’un
processus, mais simplement l’organisation de ses r?gions m?moire.

Au cours de l’ex?cution d’un programme, il est tout ? fait
possible de retrouver les caract?ristiques des r?gions (plage
d’adresses, droits d’acc?s …) gr?ce au fichier maps du
processus, dans le syst?me de fichiers /proc
(/proc/ /maps). M?me si ces informations ne
sont pas toujours exactes, elles d?crivent n?anmoins l’organisation du
processus dans la m?moire :

$ /bin/cat /proc/11384/maps
08048000-080ca000 r-xp 00000000 03:01 419059     /usr/bin/vim  [1]
080ca000-080d1000 rw-p 00081000 03:01 419059     /usr/bin/vim  [2]
080d1000-080f8000 rwxp 00000000 00:00 0			       [3]
40000000-40012000 r-xp 00000000 03:01 225598     /lib/ld-2.1.3.so
40012000-40014000 rw-p 00011000 03:01 225598     /lib/ld-2.1.3.so
40016000-40048000 r-xp 00000000 03:01 225579     /lib/libncurses.so.5.0
40048000-40050000 rw-p 00031000 03:01 225579     /lib/libncurses.so.5.0
40050000-40055000 rw-p 00000000 00:00 0
40055000-40059000 r-xp 00000000 03:01 563425     /usr/lib/libgpm.so.1.17.3
40059000-4005b000 rw-p 00003000 03:01 563425     /usr/lib/libgpm.so.1.17.3
4005b000-40130000 r-xp 00000000 03:01 225600     /lib/libc-2.1.3.so
40130000-40134000 rw-p 000d4000 03:01 225600     /lib/libc-2.1.3.so
40134000-40138000 rw-p 00000000 00:00 0
40138000-40142000 r-xp 00000000 03:01 225613     /lib/libnss_compat-2.1.3.so
40142000-40143000 rw-p 00009000 03:01 225613     /lib/libnss_compat-2.1.3.so
40143000-40155000 r-xp 00000000 03:01 225606     /lib/libnsl-2.1.3.so
40155000-40157000 rw-p 00011000 03:01 225606     /lib/libnsl-2.1.3.so
40157000-40159000 rw-p 00000000 00:00 0
bfffb000-c0000000 rwxp ffffc000 00:00 0				[4]

La ligne [1] repr?sente la r?gion m?moire .text o? le
code ex?cutable du programme est charg?. La commande objdump
-d
affiche les instructions Assembleur pr?sentes dans cette
section. La ligne [2] indique la r?gion des donn?es globales
initialis?es (.data), et la [3] la r?gion des donn?es
globales non initialis?es (.bss).

La commande objdump est une esp?ce de couteau suisse
pour lire ces informations :

 $ /usr/bin/objdump -h /usr/bin/vim

/usr/bin/vim: file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
[...]
12 .text         00073eec  08049c90  08049c90  00001c90  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
[...]
15 .data         000058d0  080ca940  080ca940  00081940  2**5
                  CONTENTS, ALLOC, LOAD, DATA
[...]
21 .bss          00002ecc  080d04a0  080d04a0  000874a0  2**5
                  ALLOC

Signalons que la commande readelf est capable de
performances identiques.

Lorsqu’un programme au format ELF est lanc?, le noyau organise la
m?moire virtuelle allou?e au processus : des plages m?moires sont
r?serv?es pour les besoins du programme (pile, tas, donn?es, code,
etc). S’il utilise des biblioth?ques dynamiques, le binaire contient
le nom de l’?diteur de liens ? utiliser
(/lib/ld-linux.so.2 en g?n?ral) dans la section

.interp :

$ /usr/bin/objdump -s -j .interp /usr/bin/vim

/usr/bin/vim:     file format elf32-i386

Contents of section .interp:
 80480f4 2f6c6962 2f6c642d 6c696e75 782e736f  /lib/ld-linux.so
 8048104 2e3200                               .2.

Le noyau passe d’abord le contr?le des op?rations ? l’?diteur de
liens afin qu’il charge les symboles (c’est-?-dire les r?f?rences aux
fonctions et variables des biblioth?ques dynamiques ou d’autres
fichiers objet, que nous avons vues pr?c?demment) qui ne sont pas encore
r?solus, puis au programme qui commence alors le cours normal de son
ex?cution.

Variables et m?moire

Comme il existe diff?rents types de variables, il existe ?galement
diff?rentes zones de m?moires dans lesquelles celles-ci sont stock?es.
Nous savons d?j? qu’il existe les sections .data et
.bss (cf. le paragraphe pr?c?dent). Ces zones sont
r?serv?es d?s la compilation car leur taille est d?finie et connue de
par la nature m?me des objets qu’elles contiennent.

Se pose maintenant le probl?me des variables locales et des
variables dynamiques. Elles sont regroup?es dans une zone m?moire
r?serv?e ? l’ex?cution du programme (user stack frame). Les
fonctions pouvant s’invoquer de mani?re r?currente, le nombre
d’instances d’une variable locale n’est pas connu ? l’avance. Elles
seront donc plac?es, au moment de leur d?finition dans la
pile du processus (stack). Cette pile se situe dans
les adresses hautes de l’espace d’adressage de l’utilisateur, et
fonctionne sur un mod?le LIFO (Last In, First Out), dernier
entr?, premier sorti.

Le bas de la zone user frame sert ? l’allocation des
variables dynamiques. Cette r?gion s’appelle le tas
(heap) : elle contient les zones m?moires adress?es par
les pointeurs, les variables dynamiques. Lors de sa d?claration un
pointeur occupe 32 bits soit dans BSS, soit dans la pile et ne pointe
nulle part en particulier. Lors de son allocation, il re?oit une
adresse qui correspond ? celle du premier octet r?serv? pour lui dans
le tas.

L’exemple suivant illustre la disposition des variables en
m?moire :

  /* mem.c */
  int    indice = 1;   //dans data
  char   * str;        //dans bss
  int    rien;         //dans bss

  void f( char c )
  {
    int i;               //dans la pile
    /* R?servation de 5 caract?res dans le tas */
    str = ( char * ) malloc ( 5 * sizeof (char) );
    strncpy( str, "abcde", 5 );
  }

  int main( void )
  {
    f( 0 );
  }
  

Des d?bordements de buffer peuvent se produire indistinctement dans
ces r?gions. Nous illustrons ceci simplement avec quatre petits
programmes qui simulent un d?bordement. Ils vont nous permettre de
constater l’impr?cision de certaines informations contenues dans le
syst?me de fichier /proc :

Shellcode dans le .data
      $ cat sh_data.c
      /* sh_data.c */
      char shellcode[] =
      "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
      "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
      "x80xe8xdcxffxffxff/bin/sh";

      int main()
      {
        int * ret;

        *( (int *) & ret + 2 ) = ( int ) shellcode;
        sleep( 5 );
        return( 0 );
      }
      $ ./sh_data
      sh-2.04$
      

gdb nous permet (comme toujours ;) de mieux voir les
choses :

      (gdb) info symbol shellcode
      shellcode in section .data
      (gdb) p &shellcode
      $2 = (char (*)[46]) 0x8049520
      

Maintenant, si nous regardons dans le syst?me de fichiers
/proc pour obtenir des informations sur la m?moire
utilis?e par le processus, nous obtenons les informations
suivantes :

      $ ./sh_data
      ^Z
      [3]+  Stopped                 ./sh_data
      $ cat /proc/`ps | grep sh_ | awk '{print $1}'`/maps
      00110000-00126000 r-xp 00000000 08:01 26579      /lib/ld-2.2.2.so
      00126000-00127000 rw-p 00015000 08:01 26579      /lib/ld-2.2.2.so
      00127000-00128000 rw-p 00000000 00:00 0
      00133000-0025c000 r-xp 00000000 08:01 26588      /lib/libc-2.2.2.so
      0025c000-00261000 rw-p 00128000 08:01 26588      /lib/libc-2.2.2.so
      00261000-00265000 rw-p 00000000 00:00 0
      08048000-08049000 r-xp 00000000 08:03 884812     /tmp/sh_data
      08049000-0804a000 rw-p 00000000 08:03 884812     /tmp/sh_data
      bfffe000-c0000000 rwxp fffff000 00:00 0
      

Comme vous pouvez le constater, notre shellcode se situe ?
l’adresse 0x8049520. Or, cette zone n’est pas
marqu?e comme ex?cutable dans
/proc/ /maps ! Et pourtant, il
tourne ;)

Shellcode dans le .bss
      /* sh_bss.c */
      char shellcode[64];

      int main()
      {
	int * ret;
	memset( shellcode, 0, 64 );
      	sprintf( shellcode,
	  "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
	  "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
	  "x80xe8xdcxffxffxff/bin/sh" );

	* ( (int *) & ret + 2 ) = (int)shellcode;
      	return( 0 );
      }
      

La variable globale shellcode est d?finie, mais
n’est initialis?e que dans la fonction main(). Elle
se situe dans dans la zone .bss :


      (gdb) info symbol shellcode
      shellcode in section .bss
      (gdb) p &shellcode
      $1 = (char (*)[64]) 0x80496c0
      

Bien que l’adresse du shellcode le situe dans une zone marqu?e
rw-, nous parvenons tout de m?me ? l’ex?cuter :

      $ ./sh_bss
      sh-2.04$
      

Shellcode dans le tas (heap)

      $ cat sh_heap.c
      /* sh_heap.c */
      int main()
      {
        int * ret;
        char * shellcode = ( char * ) malloc( 64 );
        sprintf( shellcode,
	  "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
	  "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
	  "x80xe8xdcxffxffxff/bin/sh" );
        *( (int *) & ret + 2 ) = ( int ) shellcode;
        return( 0 );
      }
      

La variable shellcode se trouve dans la pile
(stack), mais la m?moire qui lui est allou?e lors du
malloc() est r?serv?e dans le tas
(heap) :

      (gdb) p &shellcode

      $1 = (char **) 0xbffff6d0          //dans la pile
      (gdb) info symbol 0xbffff6d0
      No symbol matches 0xbffff6d0.
      (gdb) p shellcode
      $2 = 0x80496b0
           "?37^211v1?210Fa211Ff?13211?215N215Vf?2001?211?(at)?200?????/bin/sh"
      

Lorsque nous l’ex?cutons, tout se d?roule sans surprise, bien
que la m?moire allou?e pour shellcode dans le tas (en
0x80496b0) soit toujours indiqu?e comme non
ex?cutable :

      $ ./sh_heap
      sh-2.04$
      

Shellcode dans la pile (stack)

      $ cat sh_stack.c
      /* sh_stack.c */
      int main()
      {
        int * ret;
        char shellcode[] =
          "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
          "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
          "x80xe8xdcxffxffxff/bin/sh";
        *( (int *) & ret + 4 ) = ( int ) shellcode;
        return( 0 );
      }
      

Ici, le d?calage vers l’adresse de retour est diff?rent car des
registres sont plac?s sur la pile ? l’entr?e de la fonction (un
disass main sous gdb montre ceci).

      (gdb) p &shellcode
      $2 = (char (*)[46]) 0xbffff6d0         //dans la pile
      ...
      $ ./sh_stack
      sh-2.04$
      

Cette fois, tout se passe comme pr?vu puisque cette zone est
bien indiqu?e comme ex?cutable dans le syst?me de fichiers
/proc ;)

Maintenant que nous avons vu la disposition de la m?moire et des
variables, revenons ? l’?dition de liens.

La Procedure Linkage Table ou PLT

Son fonctionnement

Une section qui nous int?resse particuli?rement est la Procedure
Linkage Table
(ou PLT). Elle joue en quelque sorte le r?le
d’?diteur de liens (ou linker) pour les fonctions. Par d?faut, toutes
ses entr?es sont initialis?es non pas pour pointer vers la bonne
fonction, mais sur l’?diteur de liens lui-m?me (celui dont nous avons
parl? auparavant). Au premier appel d’une fonction donn?e, le linker
recherche la fonction dans la biblioth?que appropri?e et met ? jour
son adresse. Le prochain appel de la fonction pointe ainsi directement
o? il faut.

$ /bin/cat elf.c
#include 

main()
{
  printf( "Bonjour monde
" );
}
$ make elf
cc     elf.c   -o elf
$ gdb ./elf
[...]
(gdb) disass main
Dump of assembler code for function main:
0x80483e0 
: push %ebp 0x80483e1 : mov %esp,%ebp 0x80483e3 : sub $0x8,%esp 0x80483e6 : add $0xfffffff4,%esp 0x80483e9 : push $0x8048460 0x80483ee : call 0x804830c 0x80483f3 : add $0x10,%esp 0x80483f6 : jmp 0x8048400 0x80483f8 : jmp 0x8048402 0x80483fa : lea 0x0(%esi),%esi 0x8048400 : jmp 0x80483f6 0x8048402 : jmp 0x8048404 0x8048404 : leave 0x8048405 : ret [...] End of assembler dump. (gdb) disass printf Dump of assembler code for function printf: 0x804830c : jmp *0x80494a8 0x8048312 : push $0x18 0x8048317 : jmp 0x80482cc <_init+52> End of assembler dump. (gdb) x 0x80494a8 0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>: 0x08048312

La fonction main() contient un appel ? la fonction
printf(). En examinant le contenu de la m?moire ?
l’adresse indiqu?e (0x804830c, i.e. l’adresse de
printf()), nous constatons que la premi?re instruction
ex?cut?e est en fait un saut ? une adresse contenue dans la section

.got (Global Offset Table ou GOT). En simplifiant,
cette GOT joue le r?le d’index de la PLT : elle signale qu’il
faut revenir dans la PLT en 0x08048312, soit juste apr?s
le saut. Ensuite, un autre saut rend l’ex?cution du programme au
linker pour qu’il recherche l’adresse de la fonction dans la
biblioth?que ad?quate.

Pr?cisons qu’il est tout ? fait possible d’obtenir les m?mes r?sultats
avec la commande objdump :


$ /usr/bin/objdump -T ./elf | grep printf
0804830c      DF *UND*  0000002f  GLIBC_2.0   printf
$ /usr/bin/objdump -R ./elf | grep printf
080494a8 R_386_JUMP_SLOT   printf

La premi?re donne l’adresse de la PLT de la fonction
printf(), et la seconde son entr?e dans le GOT.

Il faut bien comprendre ici le r?le distinct de la PLT et de la
GOT. La premi?re effectue une action : construire le lien entre
une fonction requise dans le code du programme et le code machine
associ? dans une biblioth?que. En quelque sorte, la PLT est un
mini-?diteur de liens. D’ailleurs, tout comme la section
.text qui contient les instructions du programme, la PLT
est en lecture seule. De son c?t?, la GOT, qui est en
lecture/?criture, est un annuaire qui r?f?rence juste l’adresse d’une
fonction (en toute rigueur, elle indexe ?galement les variables
globales d?finies dans les biblioth?ques et utilis?es dans le
programme)

Cette approche s’appelle lazy symbol binding (r?solution
tardive des symboles). L’id?e est que si un programme utilise beaucoup
de biblioth?ques dynamiques, l’?dition de liens est tr?s (trop)
longue. Ainsi, celle-ci ne se fait que lorsqu’il y en a r?ellement
besoin.

Il est possible de forcer la r?solution des symboles par l’?diteur
de liens avec la variable d’environnement LD_BIND_NOW d?s
l’appel du programme, et non plus lorsqu’un symbole est requis
:

$ export LD_BIND_NOW=1
$ gdb ./elf
[...]
(gdb) b main
Breakpoint 1 at 0x80483e6
(gdb) r
Starting program: /home/zorgon/dev/articles/intro/./elf
Breakpoint 1, 0x80483e6 in main ()
(gdb) disass printf
Dump of assembler code for function printf:
0x804830c
:     jmp    *0x80494a8
0x8048312
:   push   $0x18
0x8048317
:  jmp    0x80482cc <_init+52>
End of assembler dump.
(gdb) x 0x80494a8
0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>:   0x40059d44
(gdb) info symbol 0x40059d44
printf in section .text
(gdb)

Cette fois, la r?solution est faite avant m?me l’ex?cution de la
fonction printf(). Nous remarquons que l’adresse contenue
dans la GOT pointe maintenant dans la section .text o? se
trouvent les instructions de la fonction.

Alchimie avec les fonctions

Pour illustrer ce m?canisme, nous montrons maintenant comment
transformer l’appel d’une fonction en une autre ? l’aide d’un petit
programme tr?s simple :

$ /bin/cat foobar.c
#include 
#include 

int main( int argc, char * argv[] )
{
        unsigned int got_addr = strtoul( argv[1], 0, 16 );
        unsigned int value = strtoul( argv[2], 0, 16 );

        * (int *) got_addr = value;
        printf( argv[3] );

        return;
}
$ gcc foobar.c -o foobar

Nous voulons que le programme foobar transforme
l’appel de la fonction printf() en un appel ?

system() en allant modifier la GOT. Pour y parvenir, nous
devons nous procurer deux informations :

  1. l’adresse de printf() dans la GOT :
          $ /usr/bin/objdump -R ./foobar | grep printf
          08049518 R_386_JUMP_SLOT   printf
          
  2. l’adresse de system() dans la libc :
          $ gdb ./foobar
          [...]
          (gdb) b main
          Breakpoint 1 at 0x8048426
          (gdb) r
          Starting program: /home/zorgon/dev/articles/intro/./foobar
          Breakpoint 1, 0x8048426 in main ()
          (gdb) p system
          $1 = {} 0x4004e2f0 
          

Ainsi, la PLT va chercher l’adresse de la fonction
printf() en 0x08049518. Il nous suffit alors
de remplacer le contenu de cette adresse par 0x4004e2f0
qui correspond ? l’adresse de la fonction system() en
m?moire, ce qui est r?alis? par l’instruction * (int *) got_addr
= value;
:

$ ./foobar 0x08049518 0x4004e2f0 /bin/sh
sh-2.03$

Conclusion

Nous avons pr?sent? ici diff?rentes notions relatives ?
l’ex?cution d’un programme. Chacune nous servira dans le prochain
article o? nous ?tudierons de multiples solutions offertes sous
Linux pour se pr?munir de l’ex?cution de shellcode r?sultant d’un
d?bordement de buffer : Openwall, Stackguard, PaX, LibSafe. Nous
d?taillerons les m?canismes mis en oeuvre par ces approches et les
d?fenses qu’elles fournissent, mais nous en verrons ?galement les
limites.


Fr?d?ric Raynal -

pappy(at)linuxmag-france.org

Samuel Dralet -

zorgon(at)mastersecurity.fr


PGP : Man in the Middle Attack

Lundi 17 janvier 2005

There is a fairly major hole in PGP that I have not seen published to date. PGP is based on IDEA and is an asymmetric public algorithm. The problem, as is true with most cryptographic implementations, is the implementation itself, not the algorithm. — By RSnake (October 04, 2004)

By RSnake (October 04, 2004)

Assume:
Alice = (message sender who has a compromised network)
Bob = (message recipient)
Cathy = (wo/man in the middle)

In a normal environment, under the best circumstances, Cathy is a MITM after the fact, and PGP is not vulnerable to MITM attacks:

  • Alice generates a public and a private key
  • Bob generates a public and a private key
  • Alice transmits her public key to MIT’s PGP server attached to the name alice@company-a.com
  • Bob transmits his public key to MIT’s PGP server attached to the name bob@company-b.com
  • Alice looks up Bob’s public key and encrypts her message with it
  • Cathy attains MITM status and awaits message transmission
  • Alice transmit encrypted data
  • Cathy cannot read the email, and cannot modify it without changing the digital signature
  • Bob recieves the message, verifies the message against Alice’s public key signature and is assured of it’s validity.

    However, because MIT’s PGP server (and other servers and implimentations as well) does not do any verification this opens the door for MITM attacks.

    Let’s assume in this scenario that Cathy has access to either Alice’s or Bob’s network (classic MITM) before either start transmitting keys.

  • Alice generates a public and a private key
  • Bob generates a public and a private key
  • Alice transmits her public key to MIT’s PGP server attached to the name alice@company-a.com
  • Bob transmits his public key to MIT’s PGP server attached to the name bob@company-b.com
  • Cathy attains MITM status and generates her own keys also called alice@company-a.com and bob@company-b.com
  • Cathy sends Bob a message from Alice’s account

    with a link to the PGP key server (with the fake alice@company-a.com key). MIT does not verify that those keys actually belong to Alice and Bob, and there would be no way to effectively do that without possible interception by Cathy by at least one, if not both of the users. MIT’s PGP server (like all others I’ve seen) also does not use SSL, making additional MITM attacks possible.

  • Bob looks up the link, verifies that it’s accurate, because it does in fact have Alice’s email address in it, and then sends an email back to Alice with his real public PGP key
  • Cathy intercepts this message and sends Alice a link to the PGP key server (with the fake bob@company-b.com key)
  • Alice verifies this key does, in fact, belong to bob@company-b.com, even though it is fake and trusts it is valid
  • Alice encrypts her message with Cathy’s fake bob@company-b.com key and sends it to bob@company-b.com
  • Cathy intercepts this message, decrypts it with her secret key, reads it, re-encrpyts it with Bob’s real public key and sends it on from Alice’s account
  • Bob recieves the email, decrypts it with his secret key and is not aware of message tampering.

    As in all forms of symmetric key exchange, even asymmetric key exchange is fallable when it comes to key servers. You must trust that they key server itself is not compromised to give different keys depending on who sees it, and it must return the correct key in MITM attacks. This is less likely in an intra-corporate communication environment, but is much more likely in cross organizational cases, where a public key server is used.

    The foundation of trust is another problem. If Bob looks at the certificate, it is possible that even it is signed by other people he knows (all of whom are being attacked in the same manner). Although this scenario is less likely, it is possible.

    The basic issue is that the user must transmit a public key somewhere, and any location can be compromised. Consider another scenario where Bob puts his public key on a webserver that only he controls. When Alice connects to it from her compromised network, Cathy simply displays a different key to Alice. There is no easy way around this form of attack, other than out of band mechanisms, where Cathy has no control.

    I have spoken with a few people about this who believe this is may only be a minor issue. However, being that the entire reason PGP was invented is to prevent eavesdropping, and this method allows for a 100% seamless MITM attack, and most importantly considering the widespread use of this application this is the largest issue today in any wide-spread asymmetric cryptographic implementation.