When is an apparently incorrect TLS entry actually executing a file ?
I already introduced TLS:
before execution of the Entrypoint, each callback is taken as is - since it's a VA - and executed, until a null entry or an exception occurs.
linked to Imports
But if you make the callbacks point to one of the imports:
AddressOfCallBacks dd __imp__WinExec
In the file, the import to WinExec will originally point to a Hint+Name structure:
WinExec:so it doesn't look like it's a valid VA. At least, not yet.
jmp [__imp__WinExec]
__imp__WinExec:
DD aWinExec - IMAGEBASE ; RVA
DD 0
aWinExec:
dw 0 ; Hint
db 'WinExec',0 ; Name
Once the file is loaded, it will points to the API.
0040026C:So it's 'now' a valid VA, and TLS execution will happen correctly.
7C86250D kernel32.WinExec
Arguments
You're calling an API, but you just don't control the parameters.
If it's your own DLL, you could still do anything. In any case, the first parameter on the stack is actually the IMAGEBASE, which points to the start of the file. Thus, MZ... and nothing prevents this signature to be taken as a normal parameter:
EIP 7C86250D kernel32.WinExecStack:
7C90118A CALL to WinExec from ntdll.7C901187(7C90118A corresponds to NtDLL's LdrInitializeThunk + 24, which is indeed TLS execution return)
00400000 CmdLine = "MZ"
00000001 ShowState = SW_SHOWNORMAL
So, what will it do ? Execute a file named MZ. Or, if you modify the header a bit more, a file named MZsomething.
Thus, with an obscure TLS entry, you get a free pre-entry point API call - here, a file execution.
This is a funny trick by Peter Ferrie in his comment.
Binary and source
TLS et Imports
Quand est-ce qu'un TLS apparemment incorrect exécute correctement un fichier ?
J'ai déjà parlé du TLS:
avant l'exécution de l'Entrypoint, chaque callback est pris tel quel - puisque c'est une adresse virtuelle - et exécuté, jusqu'à une entrée nulle ou qu'une exception se produise.
TLS et imports
Mais si on fait pointer un des callbacks vers un des imports:
AddressOfCallBacks dd __imp__WinExec
Dans le fichier, l'import vers WinExec pointe initialement vers une structure Hint+Name:
WinExec:donc ça n'a pas l'air d'une VA valide. Du moins, pas pour l'instant.
jmp [__imp__WinExec]
__imp__WinExec:
DD aWinExec - IMAGEBASE ; RVA
DD 0
aWinExec:
dw 0 ; Hint
db 'WinExec',0 ; Name
Une fois que le fichier sera chargé, cela pointera vers l'API en mémoire.
0040026C:Donc c'est 'à présent' une VA valide, et l'exécution du TLS se produira correctement.
7C86250D kernel32.WinExec
Paramètres
Vous appelez donc une API, sans contrôler les paramètres.
Si c'est votre propre DLL, vous pouvez faire ce que vous voulez. Dans tous les cas, le premier paramètre sur la pile est l'IMAGEBASE, qui pointe vers le début du fichier. MZ, donc...et rien n'interdit cette signature d'être interprétée comme un paramètre normal:
EIP 7C86250D kernel32.WinExecStack:
7C90118A CALL to WinExec from ntdll.7C901187(7C90118A correspond à NtDLL LdrInitializeThunk + 24, qui est effectivement le point de retour des TLS)
00400000 CmdLine = "MZ"
00000001 ShowState = SW_SHOWNORMAL
Donc, que va-t-il se passer ? Un fichier nommé MZ sera exécuté. Ou, si on modifie l'en-tête, un fichier nommé MZquelquechose.
Donc, avec un TLS bizarre, on obtient gratuitement un appel d'API avant l'EntryPoint - dans le cas présent, l'exécution d'un fichier.
Merci à Peter Ferrie pour cette astuce dans son commentaire.
Binaire et source
No comments:
Post a Comment