Archive for janvier, 2008

Uninformed volume 9

Le nouvel opus du fameux journal Uninformed vient de sortir. Au programme on a du reverse sur les protections employées par Battle.net, un article trop court sur l’exploitation des ActiveX, un autre sur l’encodage dynamique des payloads en fonction du contexte ou ils seront exécutés afin de bypass un michant HIPS et enfin un essai sur les leçons qu’on peut tirer sur la protection d’un code depuis son exploitation. Perso, je suis un peu déçu, il n’y a pas d’exploitation kernel delamortquitue des chocapicz, pas de release de codes … ouinz. Les auteurs manqueraient-ils d’idées ?

2 comments janvier 28th, 2008

CreateProcess

Hum, un post un peu forcé, histoire de poser quelque chose sur ce blog. J’avoue que sur ce coup je suis un peu décu de mon taff. Bref, un échec ne peut qu’être enrichissant … Je me suis lancé dans l’idée de coder ma propre fonction CreateProcess afin de réaliser une attaque précise, malheureusement j’ai rencontré 2 gros problèmes.. :(


D’abord pour ce mettre dans l’ambiance, on va s’écouter du Burzum, ca aide à décrasser le cerveau.
Burzum – JesusTod

La création d’un processus n’est pas si compliqué que ca. Tout d’abord il nous faut un joli binaire, on commence gentiment en l’ouvrant avec CreateFile puis on lui associe un objet section avec l’API CreateFileMapping avec l’attribut SEC_IMAGE. Cet attribut permet de dire à l’API quelle représente une image exécutable afin que l’API nous fabrique une structure SECTION_IMAGE_INFORMATION dessus, nous verrons son utilité par la suite. Dans le code suivant j’utilise directement les API natives, j’avais envie de me prendre la tête :)

InitializeObjectAttributes(&ObjectAttributes, &usPath, OBJ_CASE_INSENSITIVE, NULL, NULL);

//ouvre le fichier binaire
Status=ZwOpenFile(&hFile,
				FILE_EXECUTE|SYNCHRONIZE,
				&ObjectAttributes,
				&IoBlock,
				FILE_SHARE_READ,
				FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(Status))
{
	printf("Error with ZwOpenFile : 0x%x\n", Status);
	goto end;
}

InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
//cree l'objet section avec l'attribut SEC_IMAGE specifique au executable images.
Status=ZwCreateSection(&hSection,
					SECTION_ALL_ACCESS,
					&ObjectAttributes,
					0,
					PAGE_EXECUTE,
					SEC_IMAGE,
					hFile);
if(!NT_SUCCESS(Status))
{
	printf("Error with ZwCreateSection : 0x%x\n", Status);
	goto end;
}

Maintenant qu’on a notre handle sur la section on peut crée l’objet EPROCESS himsefl avec l’API ZwCreateProcess.

NTSTATUS
NtCreateProcess(
    __out PHANDLE ProcessHandle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __in HANDLE ParentProcess,
    __in BOOLEAN InheritObjectTable,
    __in_opt HANDLE SectionHandle,
    __in_opt HANDLE DebugPort,
    __in_opt HANDLE ExceptionPort
    )
Routine Description:

    This routine creates and initializes a process object.

Arguments:

    ProcessHandle - Returns the handle for the new process.

    DesiredAccess - Supplies the desired access modes to the new process.

    ObjectAttributes - Supplies the object attributes of the new process.

    ParentProcess - Supplies a handle to the process' parent process.  If this
                    parameter is not specified, then the process has no parent
                    and is created using the system address space.

    Flags         - Process creation flags

    SectionHandle - Supplies a handle to a section object to be used to create
                    the process' address space.  If this parameter is not
                    specified, then the address space is simply a clone of the
                    parent process' address space.

    DebugPort - Supplies a handle to a port object that will be used as the
                process' debug port.

    ExceptionPort - Supplies a handle to a port object that will be used as the
                    process' exception port.

Remarquez le commentaire au niveau du SectionHandle, si jamais celui-ci est a NULL, on fabrique un fork du process courant, ce qui dans certains cas peut s’avérer utile.

Quand on appel ZwCreateProcess, le kernel se charge de mettre en place la structure EPROCESS associé avec lui en le créant avec ObCreateObject. Son espace mémoire est crée avec MmCreateProcessAddressSpace et init avec MmInitializeProcessAddressSpace.

La suite consiste à initialiser le primary du futur process. Pour cela on doit lui allouer une mémoire qui lui servira de stack. La taille de stack est retrouvé à l’aide du PE header du binaire, ce qui est cool c’est que la fonction ZwQuerySection peut nous renvoyer une structure SECTION_IMAGE_INFORMATION qui contient les valeurs suivantes :

typedef struct _SECTION_IMAGE_INFORMATION {
    PVOID TransferAddress;
    ULONG ZeroBits;
    SIZE_T MaximumStackSize;
    SIZE_T CommittedStackSize;
    ULONG SubSystemType;
    union {
        struct {
            USHORT SubSystemMinorVersion;
            USHORT SubSystemMajorVersion;
        };
        ULONG SubSystemVersion;
    };
    ULONG GpValue;
    USHORT ImageCharacteristics;
    USHORT DllCharacteristics;
    USHORT Machine;
    BOOLEAN ImageContainsCode;
    BOOLEAN Spare1;
    ULONG LoaderFlags;
    ULONG Reserved[ 2 ];
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;

2 champs nous intéresse, CommittedStackSize et MaximumStackSize. Le premier correspond à la taille de stack à allouer obligatoirement, le second à la taille max. Sachant que le thread n’aura pas forcément besoin de toute sa stack, on va éviter le gaspillage en réservant la mémoire sur une taille de MaximumStackSize tout en utilisant physiquement que CommitedStackSize de mémoire.

Plus tard, pour crée le thread avec ZwCreateThread on va avoir besoin de lui fournir une structure INITIAL_TEB :

typedef struct _INITIAL_TEB {
    struct {
        PVOID OldStackBase;
        PVOID OldStackLimit;
    } OldInitialTeb;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID StackAllocationBase;
} INITIAL_TEB, *PINITIAL_TEB;

Comme nous connaissons les besoins de la main stack, nous allons remplir cette structure. Première étape, allouer en MEM_RESERVE MaximumStackSize de mémoire avec ZwAllocateVirtualMemory dans l’espace mémoire fraichement crée. On récupère l’adresse dans le champ StackAllocationBase. Ensuite, on fabrique la pile selon le schéma suivant :

StackAllocationBase
|                 |
|    StackLimit   |
|                 |
|                 |
|    StackBase    |
+-----------------+

L'espace representé par StackAllocationBase est en MEM_RESERVE.
Celui entre StackLimit et StackBase est en MEM_COMMIT|MEM_RESERVE.

Tout l’espace de StackAllocationBase est en MEM_RESERVE, celui de l’adresse la plus haute de StackAllocationBase (Stackbase) jusqu’a Stackbase-ImageInfo.CommittedStackSize est en MEM_COMMIT|MEM_RESERVE.

Cela veut dire que physiquement on aura réservé dans la ram que CommittedStackSize de mémoire, alors que les VAD (Virtual Address Descriptors) du process décriront l’espace représenter par StackAllocationBase comme utilisé.

//recup la structure ImageInfo
Status=ZwQuerySection(hSection,
					SectionImageInformation,
					&ImageInfo,
					sizeof(ImageInfo),
					NULL);
if(!NT_SUCCESS(Status))
{
    printf("Error with ZwQuerySection : 0x%x\n", Status);
	goto end;
}

printf("ImageInfo.MaximumStackSize : 0x%x\n", ImageInfo.MaximumStackSize);
printf("ImageInfo.CommittedStackSize : 0x%x\n", ImageInfo.CommittedStackSize);

RtlZeroMemory(&InitialTeb, sizeof(InitialTeb));

//Alloue ImageInfo.MaximumStackSize et stoque l'adresse dans InitialTeb.StackAllocationBase
//les pages de sont que MEM_RESERVE
Status=ZwAllocateVirtualMemory(hProcess,
							&InitialTeb.StackAllocationBase,
							0,
							&ImageInfo.MaximumStackSize,
							MEM_RESERVE,
							PAGE_READWRITE);
if(!NT_SUCCESS(Status))
{
	printf("Error with ZwAllocateVirtualMemory (1) : 0x%x\n", Status);
	goto end;
}							

InitialTeb.StackBase=(PVOID)((ULONG)InitialTeb.StackAllocationBase+ImageInfo.MaximumStackSize);
InitialTeb.StackLimit=(PVOID)((ULONG)InitialTeb.StackBase-ImageInfo.CommittedStackSize);

n=(ULONG)(ImageInfo.CommittedStackSize);
p=(PVOID)InitialTeb.StackLimit;

//Commit les pages entre StackBase et StackLimit qui seront utilisées pour la stack
Status=ZwAllocateVirtualMemory(hProcess,
							(PVOID*)&p,
							0,
							&n,
							MEM_COMMIT,
							PAGE_READWRITE);
if(!NT_SUCCESS(Status))
{
	printf("Error with ZwAllocateVirtualMemory (2): 0x%x\n", Status);
	goto end;
}

Après tout ca, on peut tranquillement créer notre thread avec ZwCreateThread de proto :

NTSTATUS
NtCreateThread(
    __out PHANDLE ThreadHandle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __in HANDLE ProcessHandle,
    __out PCLIENT_ID ClientId,
    __in PCONTEXT ThreadContext,
    __in PINITIAL_TEB InitialTeb,
    __in BOOLEAN CreateSuspended
    )

/*++

Routine Description:

    This routine creates and initializes a thread object.

Arguments:

    ThreadHandle - Returns the handle for the new thread.

    DesiredAccess - Supplies the desired access modes to the new thread.

    ObjectAttributes - Supplies the object attributes of the new thread.

    ProcessHandle - Supplies a handle to the process that the thread is being
                    created within.

    ClientId - Returns the CLIENT_ID of the new thread.

    ThreadContext - Supplies a pointer to a context frame that represents the
                initial user-mode context for a user-mode thread. The absence
                of this parameter indicates that a system thread is being
                created.

    InitialTeb - Supplies the contents of certain fields for the new threads
                 TEB. This parameter is only examined if both a trap and
                 exception frame were specified.

    CreateSuspended - Supplies a value that controls whether or not a user-mode
                      thread is created in a suspended state.

--*/

Hop le code très simple qui réalise cela :

RtlZeroMemory(&Context, sizeof(CONTEXT));

//Definit le context de depart du thread

Context.SegGs=0;
Context.SegFs=0x3B; //segment fs r3
Context.SegEs=0x23; //segment de data r3
Context.SegDs=0x23;
Context.SegSs=0x23;
Context.SegCs=0x1B; //segment de code r3
Context.EFlags=0x200; // force interrupts on, clear all else.
Context.Esp=(ULONG)InitialTeb.StackBase-4;
//TransferAddress est renvoye dans la structure ImageInfo, c'est l'entrypoint de notre programme
Context.Eip=(ULONG)ImageInfo.TransferAddress;

printf("ImageInfo.TransferAddress (EntryPoint) : 0x%x\n", ImageInfo.TransferAddress);

Status=ZwCreateThread(&hThread, //ThreadHandle
					THREAD_ALL_ACCESS, //DesiredAccess
					NULL, //ObjectAttributes
					hProcess,  //ProcessHandle
					&ClientId, //ClientId,
					&Context, //ThreadContext
					&InitialTeb, //InitialTeb
					TRUE); //CreateSuspended
if(!NT_SUCCESS(Status))
{
	printf("Error with ZwCreateThread : 0x%x\n", Status);
	goto end;
}

Remarquez que le thread est crée en suspended. On doit faire encore une petite opération avec de la lancer.

Entre temps nous somme censés initialiser le champ ProcessParameters du PEB du futur process à l’aide de la structure RTL_USER_PROCESS_PARAMETERS et de l’API RtlCreateProcessParameters :

typedef struct _RTL_USER_PROCESS_PARAMETERS {
    ULONG MaximumLength;
    ULONG Length;

    ULONG Flags;
    ULONG DebugFlags;

    HANDLE ConsoleHandle;
    ULONG  ConsoleFlags;
    HANDLE StandardInput;
    HANDLE StandardOutput;
    HANDLE StandardError;

    CURDIR CurrentDirectory;        // ProcessParameters
    UNICODE_STRING DllPath;         // ProcessParameters
    UNICODE_STRING ImagePathName;   // ProcessParameters
    UNICODE_STRING CommandLine;     // ProcessParameters
    PVOID Environment;              // NtAllocateVirtualMemory

    ULONG StartingX;
    ULONG StartingY;
    ULONG CountX;
    ULONG CountY;
    ULONG CountCharsX;
    ULONG CountCharsY;
    ULONG FillAttribute;

    ULONG WindowFlags;
    ULONG ShowWindowFlags;
    UNICODE_STRING WindowTitle;     // ProcessParameters
    UNICODE_STRING DesktopInfo;     // ProcessParameters
    UNICODE_STRING ShellInfo;       // ProcessParameters
    UNICODE_STRING RuntimeData;     // ProcessParameters
    RTL_DRIVE_LETTER_CURDIR CurrentDirectores[ RTL_MAX_DRIVE_LETTERS ];
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

extern "C"
NTSYSAPI
NTSTATUS
NTAPI
RtlCreateProcessParameters(
    PRTL_USER_PROCESS_PARAMETERS *ProcessParameters,
    PUNICODE_STRING ImagePathName,
    PUNICODE_STRING DllPath,
    PUNICODE_STRING CurrentDirectory,
    PUNICODE_STRING CommandLine,
    PVOID Environment,
    PUNICODE_STRING WindowTitle,
    PUNICODE_STRING DesktopInfo,
    PUNICODE_STRING ShellInfo,
    PUNICODE_STRING RuntimeData
    );

On retrouve un peu la même structure que celle de PROCESS_INFORMATION qu’on fournit en entrée à CreateProcess. Pour init le PEB on utilise le code suivant :

VOID CreateProcessParameters(HANDLE hProcess, PPEB Peb, PUNICODE_STRING ImageFile)
{
	ULONG n;
	PVOID p;
	NTSTATUS Status;
	PRTL_USER_PROCESS_PARAMETERS pp;

	Status=RtlCreateProcessParameters(&pp, ImageFile, 0, 0, 0, 0, 0, 0, 0, 0);
	if(!NT_SUCCESS(Status))
	{
		printf("Error with RtlCreateProcessParameters : 0x%x\n", Status);
		return;
	} 

	pp->Environment=CopyEnvironment(hProcess);

	n=pp->MaximumLength;
	p=0;

	//alloue l'espace pour la structure RTL_USER_PROCESS_PARAMETERS
	Status=ZwAllocateVirtualMemory(hProcess, &p, 0, &n, MEM_COMMIT, PAGE_READWRITE);
	if(!NT_SUCCESS(Status))
	{
		printf("Error with ZwAllocateVirtualMemory : 0x%x\n", Status);
		return;
	}

	//copie la structure dans l'espace memoire du process
	Status=ZwWriteVirtualMemory(hProcess, p, pp, pp->MaximumLength, 0);
	if(!NT_SUCCESS(Status))
	{
		printf("Error with ZwWriteVirtualMemory : 0x%x\n", Status);
		return;
	}

	//met a jour le champ du PEB
	Status=ZwWriteVirtualMemory(hProcess, (PCHAR)Peb + 0x10, &p, sizeof(p), 0);
	if(!NT_SUCCESS(Status))
	{
		printf("Error with ZwWriteVirtualMemory : 0x%x\n", Status);
		return;
	}

	Status=RtlDestroyProcessParameters(pp);
}

Enfin il reste la partie la plus mystérieuse, informé le subsystem à travers le LPC \Windows\ApiPort du process csrss.exe. Je vous avouerais que c’est la partie la moins documentée, qui, de ce fait, pose le plus de problème. D’après ce que j’ai pu récupérer, on aurait un code dans ce style là :

typedef struct _CSR_CAPTURE_HEADER {
    ULONG Length;
    PCSR_CAPTURE_HEADER RelatedCaptureBuffer;
    ULONG CountMessagePointers;
    PCHAR FreeSpace;
    ULONG_PTR MessagePointerOffsets[1]; // Offsets within CSR_API_MSG of pointers
} CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER;

typedef ULONG CSR_API_NUMBER;

typedef struct _PBASE_CREATEPROCESS_MSG {
	HANDLE hProcess;
	HANDLE hThread;
	DWORD dwProcessId;
	DWORD dwThreadId;
	DWORD CreationFlags;
	CLIENT_ID DebuggerClientId;
	DWORD VdmBinaryType;
}BASE_CREATEPROCESS_MSG ,*PBASE_CREATEPROCESS_MSG;

typedef struct _BASE_API_MSG {
    PORT_MESSAGE h;
    PCSR_CAPTURE_HEADER CaptureBuffer;
    CSR_API_NUMBER ApiNumber;
    ULONG ReturnValue;
    ULONG Reserved;
    union {
        BASE_CREATEPROCESS_MSG CreateProcess;
    } u;
} BASE_API_MSG, *PBASE_API_MSG;

VOID InformCsrss(HANDLE hProcess, HANDLE hThread, ULONG pid, ULONG tid)
{
	NTSTATUS Status;
	BASE_API_MSG m;
    PBASE_CREATEPROCESS_MSG a=(PBASE_CREATEPROCESS_MSG)&m.u.CreateProcess;

	RtlZeroMemory(&m, sizeof(m));

	a->hProcess=hProcess;
	a->hThread=hThread;
	a->dwProcessId=pid;
	a->dwThreadId=tid;

	Status=CsrClientCallServer((PVOID)&m, 0, 0x10000, sizeof(*a));
	if(!NT_SUCCESS(Status))
	{
		printf("Error with CsrClientCallServer : 0x%x\n", Status);
		return;
	}
}

Hop quand ca c’est fait, il n’y a plus qu’a lancé le thread avec ResumeThread et c’est partit. Normalement …

Le code que je fournis ne marche pas à chaque fois, j’ai un peu de mal à déterminer d’ou provient le bug et je serais content si quelqu’un pouvais me donner une piste voir même la solution.

En fait, au départ, si j’ai commencé ce code, c’était dans l’optique de crée un process à partir de rien sur le disque. Je voulais uniquement crée la section depuis la mémoire d’un process « lanceur » qui aurait servit de base pour ZwCreateProcess, apparemment dès qu’on utilise l’attribut SEC_IMAGE avec ZwCreateSection, il faut obligatoirement un handle sur un fichier exécutable. De ce fait, je n’ai pas réussit à implémenter ma petite idée, tant pis :-(

Vous trouverez le code+binaire ici :
http://ivanlef0u.fr/repo/CreateProcess.rar

Sinon, pour jouer, quelques liens sur le petit monstre du moment, je veux bien sur parler du rooktit infectant le MBR Pour ceux qui s’ennuient vous pouvez même récupérer le binaire chez OffensiveComputing.
http://www2.gmer.net/mbr/
http://www.symantec.com/enterprise/security_response/weblog/2008/01/from_bootroot_to_trojanmebroot.html
http://www.microsoft.com/security/portal/Entry.aspx?name=VirTool:WinNT/Sinowal.A

Refs :

Windows Intenal, Chapter 6, Flow of CreateProcess
http://book.itzero.com/read/microsoft/0507/Microsoft.Press.Microsoft.Windows.Internals.Fourth.Edition.Dec.2004.internal.Fixed.eBook-DDU_html/0735619174/ch06lev1sec2.html

Kernel-mode backdoors for Windows NT
http://www.phrack.org/issues.html?issue=62&id=6&mode=txt

What Goes On Inside Windows 2000: Solving the Mysteries of the Loader
http://msdn.microsoft.com/msdnmag/issues/02/03/Loader/

Windows 2000 Native Api Reference
Example 6.2: Creating a Win32 Process

A catalog of NTDLL kernel mode to user mode callbacks, part 6: LdrInitializeThunk
http://www.nynaeve.net/?p=205

15 comments janvier 19th, 2008

Blackclowns is out !

Non je ne suis pas mort, je laissais un peu de coté mon blog ces derniers temps en attendant un joyeux événement. En effet, le zine Blackclowns est enfin sortit, remarquez qu’en même temps que mon blog fête ses 1 ans, w00t. Une année de Windows, de reverse (avec les symbols :p) de codes, de trolls, de whine, une superbe expérience qui m’a fait connaître et m’a ouvert pas mal de beaux horizons et pour tout cela je vous en remercie (sisi même les québécois). J’espère que l’année suivante sera aussi enrichissante pour vous et pour moi, en tout cas je vais tout faire pour que cela continue.

Vous trouverez dans Blackclowns :
01 – Table Des Matières
02 – Introduction
03 – Tapz Prophiling
04 – Audi-K Nouvelles Aventures En Kernel Land 1/2
05 – Audi-K Nouvelles Aventures En Kernel Land 2/2
06 – Il etait une fois le CCC
07 – File System Rootkit Acheron
08 – Corporate Survival Guide
09 – Fabrication de savons a Paper Street 1/2
10 – Fabrication de savons a Paper Street 2/2
11 – Les Clowns Au Tribunal
12 – To Be Continued

Et ça ce dl là !
http://www.blackclowns.org/magazine/bcmag/bcmag-01.tar.gz

Mon article ne casse pas des briques, il s’agit de l’implémentation d’un File System Filter Driver, un soft qui, un peu comme les AV, va filtrer les requêtes passées au driver NTFS. Très pratique pour un rk ring0 pour cacher des fichiers et manipuler le file system à son aise (mangez des IRP, c’est bon !). Avec cette implémentation, on gagne aussi en furtivité car l’on n’effectue aucune opération malveillante au niveau du système, bref je vous laisse lire.

Sinon quelques petites news : Ca fait plaisir de voir qu’il existe encore des sploits aussi simple qui font si mal, celui-ci mérite vraiment de faire partir des pwnie awards tellement il est beau imho. Je veux bien sur parler du sploit sendmail+clamav.

EX_X0FF et ses potes de UG-NORTH auteurs de l’un des meilleurs anti-rk, Rootkit Unhooker, partent chez MS pour dev un proj appelé Secured Eye (SEye) qui n’a rien a voir avec les rootkits, logique :] « But all what we done this two year was a part of our big project called Secured Eye (SEye), in a two words this is project mix of software/hardware related to distributed calculations and virtualization technologies based on Vanderpool / Pacifica extensions ». En même le code de Rk Unhooker a été vendu à MS qui d’après EP_X0FF ne devrait pas être utilisé pour vendre des tools de protect payant, w8 & see…

HAN ! Security Vulnerability Research & Defense : Information from Microsoft about vulnerabilities, mitigations and workarounds, active attacks, and other related guidance and information. Ca à l’air mieux que MS Hackers en tout cas :)]

Le buzz du moment l’émission de pentest de la Tiger Team.

MIAM ! Using leak tests to evaluate firewall effectiveness.

Enfin, une très bonne chose est arrivé avec Nowel, The Collaborative RCE Tool Library, une collection de tools de pour reversers/codeurs en ligne dispo pour tous. C’est vraiment bien foutu et ca risque de devenir un site de référence dans le futur.

Sur ce, bonne année les singes de l’espace.

11 comments janvier 5th, 2008


Calendar

janvier 2008
L Ma Me J V S D
« déc   fév »
 123456
78910111213
14151617181920
21222324252627
28293031  

Posts by Month

Posts by Category