Lösung 1: C (Mac OS X x86_64), 109 Byte
Die Quelle für golf_sol1.c
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Führen Sie dann zur Ausführung des Programms Folgendes aus:
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
Ergebnisse:
Leider sucht Valgrind nicht nach Speicher, der durch Systemaufrufe zugewiesen wurde, sodass ich kein schönes erkanntes Leck anzeigen kann.
In vmmap sehen wir jedoch den großen Teil des zugewiesenen Speichers (MALLOC-Metadaten).
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
Erläuterung
Ich denke, ich muss beschreiben, was hier tatsächlich vor sich geht, bevor ich mich der verbesserten Lösung zuwende.
Diese Hauptfunktion missbraucht die fehlende Typdeklaration von C (daher wird standardmäßig int verwendet, ohne dass wir das Schreiben von Zeichen verschwenden müssen) sowie die Funktionsweise von Symbolen. Der Linker kümmert sich nur darum, ob er ein main
zum Aufrufen aufgerufenes Symbol finden kann . Also machen wir hier main zu einem Array von ints, die wir mit unserem Shellcode initialisieren, der ausgeführt wird. Aus diesem Grund wird main nicht zum __TEXT-Segment, sondern zum __DATA-Segment hinzugefügt, weshalb das Programm mit einem ausführbaren __DATA-Segment kompiliert werden muss.
Der in main gefundene Shellcode ist der folgende:
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Dazu wird die Funktion syscall aufgerufen, um eine Speicherseite zuzuweisen (die Funktion syscall mach_vm_allocate verwendet sie intern). RAX sollte 0x100000a entsprechen (teilt dem Syscall mit, welche Funktion wir wollen), während RDI das Ziel für die Zuordnung enthält (in unserem Fall soll dies mach_task_self () sein), RSI sollte die Adresse enthalten, um den Zeiger in den neu erstellten Speicher zu schreiben (also zeigen wir nur auf einen Abschnitt im Stapel), RDX enthält die Größe der Zuordnung (wir übergeben nur RAX oder 0x100000a, um nur Bytes zu sparen), R10 enthält die Flags (wir geben an, dass dies möglich ist) überall zugeteilt werden).
Jetzt ist nicht klar ersichtlich, woher RAX und RDI ihre Werte beziehen. Wir wissen, dass RAX 0x100000a und RDI der Wert sein muss, den mach_task_self () zurückgibt. Zum Glück ist mach_task_self () eigentlich ein Makro für eine Variable (mach_task_self_), die sich jedes Mal auf derselben Speicheradresse befindet (sollte sich jedoch beim Neustart ändern). In meinem speziellen Fall befindet sich mach_task_self_ zufällig unter 0x00007fff7d578244. Um Anweisungen einzuschränken, geben wir stattdessen diese Daten von argv weiter. Aus diesem Grund führen wir das Programm mit diesem Ausdruck aus$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
für das erste Argument. Die Zeichenfolge sind die beiden kombinierten Werte, wobei der RAX-Wert (0x100000a) nur 32 Bit beträgt und auf den ein Einsen-Komplement angewendet wurde (es gibt also keine Null-Bytes; wir haben nur NICHT den Wert, um das Original zu erhalten). Der nächste Wert lautet Die RDI (0x00007fff7d578244), die mit 2 zusätzlichen Junk-Bytes am Ende nach links verschoben wurde (um die Null-Bytes wieder auszuschließen, verschieben wir sie einfach zurück nach rechts, um sie wieder auf das Original zurückzusetzen).
Nach dem Systemaufruf schreiben wir in unseren neu zugewiesenen Speicher. Der Grund dafür ist, dass der mit mach_vm_allocate (oder diesem Syscall) zugewiesene Speicher tatsächlich VM-Seiten sind und nicht automatisch in den Speicher ausgelagert werden. Vielmehr werden sie reserviert, bis Daten in sie geschrieben werden, und dann werden diese Seiten in den Speicher abgebildet. War nicht sicher, ob es den Anforderungen entsprechen würde, wenn es nur reserviert wäre.
Für die nächste Lösung werden wir die Tatsache ausnutzen, dass unser Shell-Code keine Null-Bytes enthält, und können ihn daher aus dem Code unseres Programms verschieben, um die Größe zu verringern.
Lösung 2: C (Mac OS X x86_64), 44 Byte
Die Quelle für golf_sol2.c
main[]={141986632,10937,1032669184,2,42227};
Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
Führen Sie dann zur Ausführung des Programms Folgendes aus:
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
Das Ergebnis sollte dasselbe sein wie zuvor, da wir eine Zuweisung derselben Größe vornehmen.
Erläuterung
Es folgt ungefähr dem Konzept von Lösung 1, mit der Ausnahme, dass wir den Teil unseres undichten Codes außerhalb des Programms verschoben haben.
Der in main gefundene Shellcode ist jetzt der folgende:
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
Dies kopiert im Grunde den Shellcode, den wir in argv übergeben haben, um nach diesem Code zu sein (also wird der eingefügte Shellcode ausgeführt, nachdem er kopiert wurde). Was zu unseren Gunsten funktioniert, ist, dass das __DATA-Segment mindestens eine Seitengröße hat. Selbst wenn unser Code nicht so groß ist, können wir dennoch mehr "sicher" schreiben. Der Nachteil ist hier die ideale Lösung, würde nicht einmal die Kopie benötigen, sondern nur den Shellcode in argv direkt aufrufen und ausführen. Leider hat dieser Speicher keine Ausführungsrechte. Wir könnten die Rechte dieses Speichers ändern, aber es würde mehr Code erfordern als nur das Kopieren. Eine alternative Strategie wäre, die Rechte eines externen Programms zu ändern (dazu später mehr).
Der Shellcode, den wir an argv übergeben, ist der folgende:
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
Dies entspricht in etwa unserem vorherigen Code. Der einzige Unterschied besteht darin, dass wir die Werte für EAX und RDI direkt einbeziehen.
Mögliche Lösung 1: C (Mac OS X x86_64), 11 Byte
Die Idee, das Programm extern zu modifizieren, gibt uns die Möglichkeit, den Leaker in ein externes Programm zu verschieben. Wenn unser aktuelles Programm (Einreichung) nur ein Dummy-Programm ist und das Leaker-Programm Speicher in unserem Zielprogramm reserviert. Jetzt war ich mir nicht sicher, ob dies unter die Regeln für diese Herausforderung fallen würde, teilte sie aber trotzdem.
Wenn wir also mach_vm_allocate in einem externen Programm mit dem Ziel verwenden würden, das auf unser Herausforderungsprogramm festgelegt ist, könnte dies bedeuten, dass unser Herausforderungsprogramm nur etwas im Sinne von Folgendem sein müsste:
main=65259;
Wo dieser Shellcode nur ein kurzer Sprung zu sich selbst ist (unendlicher Sprung / Endlosschleife), bleibt das Programm geöffnet und wir können von einem externen Programm darauf verweisen.
Mögliche Lösung 2: C (Mac OS X x86_64), 8 Byte
Lustigerweise habe ich bei der Betrachtung der Valgrind-Ausgabe gesehen, dass Valgrind zufolge Dyld den Speicher verliert. So effektiv verliert jedes Programm etwas Speicher. Wenn dies der Fall ist, könnten wir tatsächlich nur ein Programm erstellen, das nichts tut (einfach beendet), und das tatsächlich Speicher verliert.
Quelle:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks