Frage

Mein Freund produzierte einen kleinen Proof-of-Concept-Assembler, die auf x86 gearbeitet. Ich beschloss, es auch für x86_64 zum Hafen, aber ich schlug sofort ein Problem.

Ich schrieb ein kleines Stück Programm in C, dann kompiliert und objdumped den Code. Danach habe ich es mein Python-Skript eingefügt, damit der x86_64-Code korrekt ist:

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
  0x55,                     # push   %rbp
  0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
  0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
  0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
  0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
  0xc9,                     # leaveq 
  0xc3,                     # retq
]))

fptr = cast(c_char_p(buffer), CFUNCTYPE(c_long, c_long))
print fptr(1234)

Nun, warum dieses Skript immer wieder tun Segmentierungsfehler, wenn ich es laufen?

Ich habe noch eine Frage zu mprotect und kein Ausführungskennzeichen. Es wird gesagt, gegen die meisten grundlegenden Sicherheitslücken wie Pufferüberläufe zu schützen. Aber was ist der wahre Grund, warum es in Gebrauch ist? Sie könnten nur auf dem Schreiben halten, bis Sie die .text schlugen, dann Ihre Anweisungen in einen schönen, PROT_EXEC -Bereichs injizieren. Es sei denn natürlich, verwenden Sie einen Schreibschutz in .text

Aber warum haben die PROT_EXEC sowieso überall? Wäre es nicht nur helfen enorm, dass Ihr .text Abschnitt schreibgeschützt ist?

War es hilfreich?

Lösung

Wie vincent erwähnt, ist dies aufgrund der zugeordneten Seite markiert werden als nicht ausführbar. Neuere Prozessoren unterstützen diese Funktionalität und seine als zusätzliche Sicherheit von Betriebssystemen verwendet, die sie unterstützen, . Die Idee ist, gegen bestimmte Pufferüberlauf-Angriffen zu schützen. Z.B. Ein gemeinsamer Angriff ist ein Stapelüberlauf-Variable, die Rücksprungadresse Punkt Umschreiben eingelegten zu codieren. Mit einem nicht ausführbaren Stack dies jetzt nur zu einer segfault, anstatt Steuerung des Prozesses. Ähnliche Angriffe auch für Heap-Speicher vorhanden ist.

, um es zu erhalten, müssen Sie den Schutz ändern. Dies kann nur auf Seite ausgerichtet Speichern durchgeführt werden, so dass Sie wahrscheinlich benötigen, um Ihren Code so etwas wie die folgenden ändern:

libc = CDLL('libc.so')

# Some constants
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

def executable_code(buffer):
    """Return a pointer to a page-aligned executable buffer filled in with the data of the string provided.
    The pointer should be freed with libc.free() when finished"""

    buf = c_char_p(buffer)
    size = len(buffer)
    # Need to align to a page boundary, so use valloc
    addr = libc.valloc(size)
    addr = c_void_p(addr)

    if 0 == addr:  
        raise Exception("Failed to allocate memory")

    memmove(addr, buf, size)
    if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
        raise Exception("Failed to set protection on buffer")
    return addr

code_ptr = executable_code(buffer)
fptr = cast(code_ptr, CFUNCTYPE(c_long, c_long))
print fptr(1234)
libc.free(code_ptr)

Hinweis: Es kann eine gute Idee sein, die ausführbare Flagge unscharf zu schalten, bevor Sie die Seite zu befreien. Die meisten C-Bibliotheken nicht zurück tatsächlich die Erinnerung an das Betriebssystem, wenn Sie fertig, aber es in ihrem eigenen Pool halten. Dies könnte bedeuten, dass sie die Seite wieder verwenden werden an anderer Stelle, ohne das EXEC Bit zu löschen, unter Umgehung der Sicherheitsvorteil.

Beachten Sie auch, dass diese recht nicht tragbar ist. Ich habe es auf Linux getestet, aber nicht auf einem anderen Betriebssystem. Es wird nicht auf Windows arbeiten, kaufen auf anderen Unix-Varianten können tun (BSD, OSX?).

Andere Tipps

einige der Forschung mit meinem Freund Geschehen und fand heraus, das ein plattformspezifische Problem. Wir vermuten, dass auf einigen Plattformen malloc mmaps Speicher ohne PROT_EXEC und auf andere es tut.

Daher ist es notwendig, anschließend die Schutzstufe mit mprotect zu ändern.

Lame Sache, dauerte eine Weile, um herauszufinden, was zu tun ist.

from ctypes import (
    cast, CFUNCTYPE, c_long, sizeof, addressof, create_string_buffer, pythonapi
)

PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC = 0, 1, 2, 4
mprotect = pythonapi.mprotect

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
    0x55,                     # push   %rbp
    0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
    0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
    0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
    0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
    0xc9,                     # leaveq 
    0xc3,                     # retq
]))

pagesize = pythonapi.getpagesize()
cbuffer = create_string_buffer(buffer)#c_char_p(buffer)
addr = addressof(cbuffer)
size = sizeof(cbuffer)
mask = pagesize - 1
if mprotect(~mask&addr, mask&addr + size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0:
    print "mprotect failed?"
else:
    fptr = cast(cbuffer, CFUNCTYPE(c_long, c_long))
    print repr(fptr(1234))

Ich denke, man kann nicht frei jeden zugewiesenen Speicher auszuführen, ohne sie zunächst als ausführbare Einstellung. Ich habe nie versucht, mich, aber Sie könnten die Unix-Funktion mprotect überprüfen möchten:

http://linux.about.com/library/cmd/blcmdl2_mprotect.htm

VirtualProtect scheint die gleiche Sache auf Windows zu tun:

http://msdn.microsoft.com/ en-us / library / aa366898 (VS.85) aspx

Hat Python auch eine solche Verwendung zulassen? Ich sollte es dann lernen ...

Ich denke, dass der Dolmetscher erwartet nicht irgendein Register verändert werden. Versuchen Sie, die Register speichern, die Sie in der Funktion verwenden, wenn Sie Ihre Assembler Ausgabe wie folgt zu verwenden.

Btw, Aufrufkonvention von x86_64 ist anders als normale x86. Sie können Schwierigkeiten haben, wenn Sie Zeiger Ausrichtung verlieren stapeln und externe Objekte mit anderen Werkzeugen erzeugt mischen.

Es gibt einfacheren Ansatz, den ich nur gedacht habe, aber vor kurzem, dass nicht mprotect beteiligt ist. Ganz offensichtlich direkt mmap den ausführbaren Raum für Programm. In diesen Tagen Python hat ein Modul für tut genau dies, obwohl ich nicht Art und Weise fand die Adresse des Codes zu erhalten. Kurz gesagt werden Sie Speicher Aufruf Mmap anstelle von String-Puffer und Setzen des Ausführungs-Flag indirekt zuordnen. Das ist einfacher und sicherer, können Sie sicher sein, nur können Sie den Code jetzt ausgeführt werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top