CVE-2011-1281 Privilege escalation in CSRSS proof of concept

After one year without blogging (all my apologies) I’m back. A few days ago I’ve seen the pwnie awards nominations list, there were lot’s of interesting and sophisticated bug exploitation. But one attract my attention « Privilege escalation in CSRSS » discovered by Matthew ‘j00ru’ Jurczyk. If you want to undestand this vulnerability and the way to exploit it, read this excellent post http://j00ru.vexillium.org/?p=893. And if you’re not familiar with CSRSS I advice you to read this article or this one (in french).

So, for writing the PoC we have to follow this steps :

  1. Spray the shared WIN32K section, by creating a sufficient amount of USER objects. The section is then going to be mapped to every process running in the context of the local desktop, thus we can perform this step at this early point,
  2. Create N instances of a process, each of which will create a single zombie console and then go idle, (*)
  3. Kill all N instanes of the processes,
  4. Create 3N local threads, (**)
  5. Kill 2N threads (in the order described in the “Second Stage” section),
  6. Kill the remaining N threads,
  7. Emulate the win+u key presses, resulting in a new instance of UTILMAN.EXE being created,
  8. Call SendMessage(HWND_BROADCAST,WM_SYSCOMMAND,0xFFF7,0), triggering the execution of CreateRemoteThread on each of the N freed handles.

* – by creating a zombie console, we also mean replacing the original PropertiesProc address (used in kernel32!AllocConsole) with a custom pointer.
** – the technique is very time-sensitive. If any handle is picked / stored on the free-list between steps 3 and 4, than steps 5 and 6 might not succeed in setting up the expected free-list handle layout.

I wont speak about first step immediately for different reason.
Let’s start with step two « create a single zombie console », for me it’s the most hard part. We have to code AllocConsole and AllocConsoleInternal (I only scope Windows XP version for the moment). With AllocConsoleInternal we can control the PropRoutine & CtrlRoutine of the console. For conding this function I start googling with « AllocConsoleInternal + PropRoutine + CtrlRoutine » and reach this function definition :

BOOL APIENTRY AllocConsoleInternal(
 IN LPWSTR lpTitle,
 IN DWORD dwTitleLength,
 IN LPWSTR lpDesktop,
 IN DWORD dwDesktopLength,
 IN LPWSTR lpCurDir,
 IN DWORD dwCurDirLength,
 IN LPWSTR lpAppName,
 IN DWORD dwAppNameLength,
 IN LPTHREAD_START_ROUTINE CtrlRoutine,
 IN LPTHREAD_START_ROUTINE PropRoutine,
 IN OUT PCONSOLE_INFO pConsoleInfo)

With some call to ntdll!CsrAllocateCaptureBuffer, ntdll!CsrCaptureMessageBuffer for desktop, title and curent dir memory allocation. And then ntdll!CsrClientCallServer with allocConsole request we will reach winsrv!SrvAllocConsole and then spawn a console. For testing we lunch a broken console and kill his process and his parent process, after that we do a « right clic + proprieties/default » on the broken console and then we have a winsrv!InternalCreateCallbackThread executed with free handle! (the killed parent process handle precisely)

Step 3, 4, 5, 6, 8 is quite easy. Step 7 (WIN+U emulation) too but SetKeyboardState and PostMessage doesn’t work, we have to use keybd_event (depreciated) or SendInput to invoke utilman.exe.

Therefore, with all this steps we are able to get CSRSS to call CreateRemoteThread with a system process handle and a controlled start address. Now we need step one « Spray the shared WIN32K section of system process with USER object » and it’s done! For this we have to invoke ultiman (WIN+U) which spawn three new process :

-> ultiman.exe [NT AUTHORITY\SYSTEM]
+-> ultiman.exe /start [USER]
+–> narrator.exe /UM [USER]

Then we create user object like MessageBox with over long title (32Ko). But ultiman (system) doesn’t share the win32k section with other process at all times. After trying differents unsuccessful methods, I decided contact j00ru who give me the solution. We can inject user object in ultiman (system) if another user (regardless of his privileges) is logged on the machine at the same time. At this moment, I haven’t found explanation of this behaviour. I think it’s something in relation with Desktop/Winstation/Session, if you have some idea tell me.

Source of the poc :
http://mysterie.fr/prog/blog/allocConsole/

Thanks to j00ru for his help and all shared knowledge on his blog, hitb and so on!

Format string automatique

Cet été j’ai décidé d’apprendre le python, et pour que se soit fun je me suis mis en tête de faire un script qui automatise les format strings.

En plus c’est la mode:
http://esec.fr.sogeti.com/blog/index.php?2010/07/09/88-exploitation-de-format-string-avec-metasm
http://sh4ka.fr/codes/fmt_string_builder.html
ou pas:
http://www.ouah.org/REMOTEFMT-HOWTO
http://nibbles.tuxfamily.org/?p=887
http://www.redspin.com/blog/2009/11/25/automatic-format-string-exploitation/

Le script se décline en 3 fichiers:
main.py
warper.py (détecte ou se situe les arguments faillibles du programme et où placer le payload)
frmtStr.py (exploite la format string)

La format string est exploitable dans la cas où:

  • On est sur une archi x86 (32bit), sous linux
  • La pile est exécutable
  • Pas d’aslr (Quoi qu’avec un gros padding/nopsleed ca passe)
  • La section dynamic est +W
  • Le programme vulnérable n’est pas trop verbeux (ca peut fausser la détection)

En bref sur un wargame c’est parfait (ca permet de gagner du temps), sur un programme réel ca l’est moins.

Je vous conseille de regarder la vidéo en fullscreen (pas super lisible).

Write Up – NDH 2010 – VM Windows

Pour les personnes n’ayant pas participé au capture the flag ou pour ceux qui n’ont pas exploité les services présent sur la machine Windows. Je mets à disposition les sources des épreuves que j’ai réalisé, et une solution pour chacunes d’elles.

Premier point d’entrée, utilisateur toto mot de passe azerty.

Deuxième point d’entrée, un remote buffer overflow avec réécriture du SEH. Sauf que sur la VM le binaire était full safeSEH, alors que je l’ai testé/exploité sur ma box, j’ai du merder à la compilation bref désolé pour ceux qui ont passé du temps dessus.

Troisième point d’entrée, sur le bureau de chaque challenger se trouvait un pdf. Le lecteur par défaut était adobe reader 8.0. Le pdf en question était crafté de façon à exploiter la faille cve-2009-0927.
J’ai ensuite utilisé un shellcode windows trouvé sur le net et retravaillé pour télécharger et exécuter un malware se trouvant à l’url http://192.168.3.200/tmp.exe
Étant donné les quelques problèmes de mise en place du ctf, l’url n’as pas été disponible tout de suite. Quatre équipes se sont infectées mais il est possible que bien plus d’équipes aient ouvert le pdf.

Le shellcode modifié :
source ici

Le pseudo botnet :
source ici

Le botnet réécrit l’IAT d’explorer.exe et de tous les processus lancés par explorer. Afin de se cacher (hook de findFirstFile / findNextFile) et se connecte à irc.
Malgré le fait que le pdf soit sur le bureau et que quatre équipes soit infectées (dont l’équipe gagnante) personne n’a creusé, et personne n’a rejoint irc pour essayer de prendre le contrôle du bot, donc j’ai fais mumuse tout seul:
screen1
screen2
screen3

Viens ensuite quelques épreuves plus triviales comme un serveur java qui sérialise l’objet qu’il reçoit. Cet objet lit un fichier contenant de l’ascii art et renvoie son contenu sur le réseaux, le but étant de modifier l’objet afin d’afficher un fichier contenant le hash de l’utilisateur ascii sur chaque machine.
source ici
Il y avait aussi deux épreuves web mais je n’en suis pas l’auteur donc je n’en parlerai pas. :)

Et enfin un « Local Privilege Escalation« , le binaire checksum.exe communique avec un driver. Il envoie le contenu du fichier qu’on lui donne au driver, celui-ci fait un checksum des données et les renvoie au programme. La communication entre eux se fait par la méthode METHOD_NEITHER.
Les challengers ont accès en lecture à checksum.sys. Il pourront remarquer que le driver contient des failles dont un déréférencement de pointeur (Adresse du buffer d’output non vérifié). Les symbols de windows 2003 sp2 était disponible en ftp. Je ne mettrai pas à disposition la source de checksum.exe car il est très crade:
sources ici
Une petite difficultée était présente, comme le font pas mal d’antivirus le code ioctl de gestion du checksum était aléatoire. En analysant le driver ou l’exécutable on retrouvais facilement le code. Le but était de freiner les challengers en cas d’exploitation massive, mais personne n’a exploité cette épreuve. :(

That’s all folks !

Write Up – NDH 2010 – Crackme

Le 19 et 20 Juin a eu lieu la 8eme édition de la nuit du hack. Pour cette occasion j’ai réalisé différentes épreuves dont des crackmes accessibles aux personnes participant au challenge public (par le wifi) et aux participants du Capture the flag.
Dans la section Crackme j’ai réalisé les niveaux 1, 3 et 4.

Crackme Niveau 1:

Le crackme n’a aucun mécanisme d’anti-debug. Ce qui lui est donné en entrée est comparé directement à une chaine en mémoire. Rien de bien compliqué en soit:

GetWindowText(hEdit, text, 255);
theSecret[0]++;
theSecret[2]++;
theSecret[4]++;
if(strlen(text) > 0 && !strcmp(text, theSecret)) {
    ShowWindow(hVrai, SW_SHOW);

    strcpy(text, getMD5(text));
    SetWindowText(hMd5, text);
    ShowWindow(hMd5, SW_SHOW);
    ShowWindow(hFaux, SW_HIDE);
} else {
    ShowWindow(hVrai, SW_HIDE);
    ShowWindow(hMd5, SW_HIDE);
    ShowWindow(hFaux, SW_SHOW);
}
theSecret[0]--;
theSecret[2]--;
theSecret[4]--;

La source du crackme:
cm1/

Crackme Niveau 3:

Le mot de passe du programme n’est pas stocké dans sa mémoire ou dans le fichier exécutable. Au chargement il crée 8 plages mémoires contenant des op code de saut assembleur (5 octets). Chaque plage contient 256 sauts, chaque saut équivaut à un caractère ascii. Dans chacune des plages un seul saut est valide et pointe vers une routine de décryptage les autres sauts pointent indirectement vers une routine qui affiche mot de passe faux.
J’utilise aussi l’instruction rdtsc pour calculer le temps effectué entre le première saut et le dernier afin de vérifier que le programme s’exécute en moins d’une seconde sinon c’est que le programme est débuggé et donc j’affiche mot de passe faux dans tous les cas.
La source du crackme:
cm2/

Crackme Niveau 4:

Ce niveau est destiné à des personnes ayant l’habitude de ce genre d’épreuve la difficulté principale de ce challenge est donc de débugger le programme (l’analyser). J’ai mi beaucoup de routine anti-débug. Tout d’abord à l’entrée du programme j’ai incrusté des instructions assembleur pour modifier la signature de l’exécutable (ces instructions ne sont jamais exécutées) le but est de faire croire que l’exécutable est packé c’est à dire qu’il est compressé par un autre logiciel.
Ensuite j’appel une fonction spéciale un peu partout dans le code de mon programme. (junk code)

void brack() {
    __asm {
        _emit 0xEB;  // 0xEB01 = jmp short relatif saute au dessus
        _emit 0x01;  de l'octet 0x1D. Donc ne fait rien au final
        _emit 0x1D;  // 0x1D fait croire au debugger que l'instruction qui suit est SBB
    }
}

Le programme crée ensuite un thread. La fonction du thread est la suivante:

while(1) {
    brack();
    Sleep(1000);
    __try {
        __asm {
            int 3;
        }
    }
    __except(eps = GetExceptionInformation(), EXCEPTION_EXECUTE_HANDLER) {
        brack();
        isDbg = false;
    }
    if(isDbg) {
        quit();
        break;
    }
    isDbg = true;
}

Dans le cas où un débugger serait présent au démarrage du programme ou s’il s’attache au programme durant son exécution, l’instruction int 3 (point d’arrêt) est exécutée le débugger la récupère. Le programme détecte ainsi qu’un débugger est présent et le mot de passe n’est jamais révélé.

La fonction IsDebuggerPresent est aussi appelée dans le programme. Elle est appelée indirectement (récupération de l’adresse de la fonction dynamique).

GetProcAddress(GetModuleHandle("Kernel32.dll"), "IsDebuggerPresent");

Et enfin j’utilise l’instruction rdtsc dans le programme pour vérifier que le temps d’exécution de celui ci n’est pas trop long.

Si le challenger contourne toute ces protections alors le programme alloue une plage mémoire, écrit à l’emplacement de cette plage des octets spécifiques (fonction de déchiffrement encodé), puis la décode avec un XOR. Lorsque l’utilisateur entre son mot de passe la fonction déchiffrée effectue plusieurs opérations aritmétriques sur le mot de passe:

  • Longueur de la chaine
  • Est-ce que la deuxième lettre est égale à la 5eme
  • OU / ET logique, XOR etc…

Par un calcul mathématique ou un peu de logique on retrouve assez simplement le passe. Il n’y a aucune collision une seule solution est valide.
La source du crackme:
cm3/

Au niveau des résultats sur 180 participants (public/ctf):

  • Level1 10 validations
  • Level3 5 validations
  • Level4 3 validations

Des solutions sont déjà disponibles sur le net:
http://w4kfu.free.fr/blog/index.php?post/2010/06/20/Write-up-NDH-2010-crackme-lvl-1
http://w4kfu.free.fr/blog/index.php?post/2010/06/20/Write-up-NDH-2010-crackme-lvl-2
http://w4kfu.free.fr/blog/index.php?post/2010/06/20/Write-up-NDH-2010-crackme-lvl-3

Et si vous êtes féru de crackme ce week end Eset NOD32 organise un challenge.

Return Oriented Programming

Ce week-end se tenait à Maubeuge les rssil (Rencontres des Solutions de Sécurité et d’Informatique Libre).
Je vous encourage à y aller l’an prochain. Il y avait une très bonne ambiance, des conf intéressantes et j’ai pu y rencontrer pas mal de monde. Je tiens à remercier tout particulièrement les organisateurs de l’évènement pour leur accueil et leur organisation.

Pour ceux qui veulent récupérer mes slides ou tout simplement ceux qui n’ont pas pu assister à la conférence:
rop.pdf
rop.odp
rop.ppt

Et pour ceux qui sont intéressés par le sujet je vous conseil aussi la lecture de ce papier réalisé par Dino dai zovi il y a un mois.

Pour les autres conférences, les slides devraient être eux aussi en ligne d’ici peu. Ainsi que les épreuves du challenge ethical hacking (en tout cas les résolus).

UA-7839874-2