Shell Codes
Category: Backdoor,Sicherheitslücken
In diesem Whitepaper möchte ich euch die Grundlagen von Shellcode näher
bringen und das Ganze soweit erklären, dass ihr danach schon euren eigenen
Shellcode schreiben könnt. Wenn noch irgendwelche Fragen offen stehen sollten
oder ich etwas ausgelassen haben sollte, mailt mir doch einfach oder schreibt
in mein Gästebuch.
[email protected]
www.katharsis.x2.to
———————————————————————-
[0x00] Was ist Shellcode?
Shellcode ist praktisch nichts anderes als unabhängiger Maschinencode. Er unterscheidetet sich allerdings durch gewisse
Grundsätze, beispielweise darin, dass in Shellcode keine NULL-Bytes oder NULL-Byte-Operanden enthalten sein dürfen, da
diese zur String Termination und somit zur Beendigung des Befehls führen würden. Das ist wichtig, denn wir verwenden
Shellcode beispielweise und auch hauptsächlich dafür, um speicherorientierte Sicherheitslücken wie z.B. Buffer Overflows
auszunutzen. Bei einem solchen Angriff überschreiben wir den Zeiger mit einem auf unseren Shellcode zeigenden,
mit welchem wir das Programm manipuliert haben. Ein großes Archiv für fertige Shellcodes findet man auf Metasploit¹,
wo man auch in der Praxis gut sehen kann, welche Methoden man ursprünglich verwendet (was sehr vielseitig ist, von Bindshellss
über Exec() Implementationen bis hin zu simplen Systembeeps, um die Lücke nur zu testen oder ein Proof of Concept aufzuweisen)
¹ http://www.metasploit.com/shellcode
[0x01] Was benötige ich um Shellcode zu schreiben?
Man sollte zumindest Basiskenntnisse in C und Assembler haben. Ich werde in diesem Whitepaper den Netwide Assembler¹ (nasm)
unter einem x86 Linux-System verwenden, vieles lässt sich allerdings auch einfach implementieren.
Allerdings sollte man auf die Interrupts und die Syscalls achten, diese könnten unter verschiedenen Systemen variieren.
Zudem benötigen wir objdump², ein praktisches Werkzeug, welches sich u.A. als Disassembler verwenden lässt.
Um fortgeschrittenen Shellcode zu entwickeln kann auch der Shellcode Generator³ ein nützliches Werkzeug sein.
Dieser generiert automatisch mit Hilfe von objdump aus einer ausführbaren Datei direkt verwendbaren Shellcode.
¹ http://nasm.sf.net
² http://objdump.sf.net
³ http://katharsis.bplaced.net/shellgen.txt
[0x02] Shellcode Grundlagen
Um die Grundlagen der Programmierung von Shellcode zu erklären, starten wir mit einem einfachen Beispiel. In dem folgenden Code
verwende ich den Syscall 29, welcher für die Systemfunktion pause() steht. Diese Funktion benötigt keine Argumente und ist einfach
zu verwenden, deswegen ist sie sehr geeignet für ein simples Beispiel.
pause.asm:
[SECTION .text]
global _start
_start:
mov eax, 29
int 80h
In dem obigen Code setzen wir den Wert 29 (Syscall Pause) im E-Register und machen einen Kernelcall (int 80h). Das sollte aber jedem
Hobbyprogrammierer bekannt sein. Nun assemblieren und kompilieren wir unseren Code.
lifelover@box:~$ nasm -f elf pause.asm
lifelover@box:~$ ld -o pause pause.o
So weit, so gut. Um zu testen ob unsere Applikation funktioniert starten wir sie einfach, und beenden sie wieder mit STRG+C.
lifelover@box:~$ ./pause
^C
lifelover@box:~$
Um nun den Maschinencode zu extrahieren verwenden wir objdump mit dem Parameter -d (disassemle).
lifelover@box:~$ objdump -d pause
pause: file format elf32-i386
Disassembly of section .text:
08048060 :
8048060: b8 1d 00 00 00 mov $0x1d,%eax
8048065: cd 80 int $0×80
Unser Shellcode sieht also wie folgt aus:
\xb8\x1d\x00\x00\x00\xcd\x80
Ob dieser nun funktioniert prüfen wir mit einer simplen C-Applikation:
pause.c:
char code[] = “\xb8\x1d\x00\x00\x00\xcd\x80″;
int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
Diesen Code kompilieren wir nun und testen, ob es funktioniert.
lifelover@box:~$ gcc pause.c -o pause-test
lifelover@box:~$ ./pause-test
^C
lifelover@box:~$
Unser Shellcode scheint zu funktionieren, allerdings enthält unser Code noch NULL-Bytes. Diese müssen wir entfernen, da sie ansonsten
unseren Shellcode terminieren würden. In unserem Shellcode haben wir zum Glück nur eine Anweisung die NULL-Bytes enthält (mov).
Nun müssen wir “rausfinden”, warum die NULL-Bytes zustandekommen. Das ist an dieser Stelle jedoch ganz einfach. Das E-Register (EAX)
ist ein 32 Bit Register, allerdings schreiben wir nur einen 8 Bit Wert in dieses Register. Die restlichen 24 Bits werden nun also mit
NULL-Bytes gefüllt – was wir nicht wollen. Also müssen wir eax einfach nullen und unseren Wert nach al schreiben.
pause.asm:
[SECTION .text]
global _start
_start:
xor eax, eax
mov al, 29
int 80h
Nun disassemblieren wir unser neues Programm wieder.
lifelover@box:~$ nasm -f elf pause.asm
lifelover@box:~$ ld -o pause pause.o
lifelover@box:~$ objdump -d pause
pause: file format elf32-i386
Disassembly of section .text:
08048060 :
8048060: 31 c0 xor %eax,%eax
8048062: b0 1d mov $0x1d,%al
8048064: cd 80 int $0×80
Super – es hat funktioniert. Unser Maschinencode enthält keine NULL-Bytes mehr und kann nun als fertiger Shellcode verwendet werden:
pause.c:
char code[] = “\x31\xc0\xb0\x1d\xcd\x80″; // neuer shellcode
int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
lifelover@box:~$ gcc pause.c -o pause-test
lifelover@box:~$ ./pause-test
^C
lifelover@box:~$
Nun haben wir erfolgreich unserer ersten, funktionierenden Shellcode entwickelt. Da unsere Variante keine NULL-Bytes enthält, kann
sie direkt eingesetzt werden und ist funktionsfähig.
[0x03] EOF (kthxbai)
Bla, das wärs dann schon wieder gewesen. Ich hoffe ich konnte euch in diesem Whitepaper ein wenig über Shellcodes beibringen, sodass
ihr nun in der Lage sein solltet, zumindest simple Shellcodes zu entwickeln und einzusetzen.
// katharsis
www.katharsis.x2.to
[email protected]
Autor: katharsis
Quelle: katharsis.x2.to