📜 ⬆️ ⬇️

Reviewing the IDS Bypass Competition at Positive Hack Days 9

For the first time, an IDS Bypass competition was held at the Positive Hack Days 2019 international forum. Participants had to investigate a network segment of five nodes, then either exploit the service’s vulnerability, or fulfill a given condition (for example, send a specific HTTP response) and thus retrieve the flag. It was easy to find an exploit, but IDS complicated the task: the system stood between participants and nodes and checked every network packet. Attackers are seen on the dashboard, if the signature blocked their connection. Below I will tell in detail about the tasks themselves and analyze their decision.



100.64.0.11 - Struts


The first node in the number of tasks solved was Struts. After scanning the Nmap ports, we find the Apache Struts service on port 8080.
')
# nmap -Pn -sV -p1-10000 100.64.0.11 631/tcp open ipp CUPS 2.1 8005/tcp open mxi? 8009/tcp open ajp13 Apache Jserv (Protocol v1.3) 8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1 



Vulnerability to Apache Struts died down in 2017: using OGNL-injection, the attacker could execute any code on Struts without authorization. There is an exploit, for example, on GitHub , but is caught by IDS:

[Drop] [**] [1:1001:1] Apache Struts2 OGNL inj in header (CVE-2017-5638) [**]

The signature code itself is not available to participants, but from the message in the logs you can understand the mechanism of its work. In this case, the signature found an OGNL injection in HTTP:

 GET /showcase.action HTTP/1.1 Accept-Encoding: identity Host: 100.64.0.11:8080 Content-Type: %{(#_='multipart/form-data')... 

If you look at the IDS behavior, it becomes obvious that it catches the% {combination at the beginning of the Content-Type header. There are several solutions:

  1. The member @empty_jack tried to break the combination of the characters% {with his own fuzzing dictionary and thus came to a solution with the Content-Type string:% $ {.
  2. To plot the HTTP request itself. The participant @ c00lhax0r found that a null character at the beginning of the header would also bypass IDS: Content-Type: \ 0 $ {.
  3. Most of the exploits for CVE-2017-5638 are injected with the percent symbol. But some researchers of this and previous Apache Struts vulnerabilities write that the injection can begin with both% and $. Thus, the combination of $ {bypasses the IDS-signature and executes the code on the system. This decision was originally intended.

This task was the easiest, it was decided by eight participants.

100.64.0.10 - Solr


Port 8983 contained an Apache Solr server written in Java.

 $ nmap -Pn -sV -p1-10000 100.64.0.10 22/tcp open ssh (protocol 2.0) 8983/tcp open http Jetty 



An exploit for Apache Solr 5.3.0 is easy to find - CVE-2019-0192 . An attacker could spoof the address of the RMI server in the collection. Operation requires the ysoserial framework, which generates chains of Java objects (gadgets) and delivers them in various ways. For example, from a JRMP server.

Of course, using the above exploit in the forehead, the participants will see IDS signatures trigger:

[Drop] [**] [1:10002700:3001] ATTACK [PTsecurity] Java Object Deserialization RCE POP Chain (ysoserial Jdk7u21) [**]

Jdk7u21 is only one of thirty loads, and their choice depends on the libraries used in the vulnerable service. The gadget chain Jdk7u21 uses only standard classes from the Java Development Kit version 7u21, and the chain CommonsCollections1 contains classes from the widely used Apache Common Collections 3.1.

The attacker replaces the RMI server address in the Solr collection with his own, and then starts the JRMP server. Solr requests an object by address and receives a malicious Java object. After its deserialization, the code is executed on the server.

The signature is triggered by a sequence of classes in a serialized Java object. It is transmitted from the attacker's car and in traffic begins like this:



The solution to this problem was simple. The signature explicitly refers to Jdk7u21. To work around it was necessary to try other chains of gadgets. For example, one of the CommonsCollections. Signatures for other chains in IDS were not. The participant will receive a shell on the system and read the flag. Five participants coped with the task.

100.64.0.12 - SAMR


One of the most difficult and interesting tasks of the competition. This is a Windows-machine with open port 445. The flag is divided into the names of two users of the system, and in order to complete the task, it was necessary to obtain a list of all users on the Windows node.

Of course, MS17-010 and other exploits did not work on this machine. Enumerate users could, for example, scripts from Nmap or impacket framework:

 $ python samrdump.py 100.64.0.12 Impacket v0.9.15 - Copyright 2002-2016 Core Security Technologies [*] Retrieving endpoint list from 100.64.0.12 [*] Trying protocol 445/SMB… Found domain(s): . SAMR . Builtin [*] Looking up users in domain SAMR [-] The NETBIOS connection with the remote host timed out. [*] No entries received. 

Both scenarios make DCERPC requests to the machine on port 445. But not everything is so simple: some packages are blocked by IDS, and this time two signatures work already:

[**] [1:2001:2] SAMR DCERPC Bind [**]
[Drop] [**] [1:2002:2] SAMR EnumDomainUsers Request [**]

The first one detects the connection to the SAMR service and only marks the TCP connection with a flag. And the second is triggered by the EnumDomainUsers request to the SAMR service. This service has other ways to get users: QueryDisplayInfo, QueryDisplayInfo2, QueryDisplayInfo3. All of them were also blocked by signatures.

The DCERPC protocol and Windows services provide tremendous opportunities for remote node management. This protocol is used by most well-known tools, such as PsExec or BloodHound. The SAMR service, that is, SAM Remote Protocol, allows you to work with user accounts on the site, including the list of users.

For the EnumDomainUsers Impacket request, do the following:



A DCERPC connection to the SAMR service is established on top of SMB, and all further requests are in the context of this service. Signatures work on the first and last package from the screenshot.

To the task I gave two tips:

  • IDS generate 2 alerts. Look closely at the first.
  • Do you know?

We are talking about the DCERPC protocol and connection methods. In the list of available PDUs, the commands Bind and Alter Context are responsible for connecting and changing context, the second allowing you to change the current context without breaking the connection.

To solve this, you had to rewrite the samrdump script logic:

  1. Make a bind to another service, for example with UUID 3919286a-b10c-11d0-9ba8-00c04fd92ef5.
  2. Use Alter Context to switch to SAMR.
  3. Send an EnumDomainUsers request.

Changes fit on three lines:

 < dce.bind(samr.MSRPC_UUID_SAMR) --- > dce.bind(uuid.uuidtup_to_bin(("3919286a-b10c-11d0-9ba8-00c04fd92ef5", "0.0"))) > dce.alter_ctx(samr.MSRPC_UUID_SAMR) > dce._ctx = 1 

An alternative solution was proposed by the winner of the @ psih1337 contest. The EnumDomainUsers request returned a list of users not by name, but by SID (Security ID). SID is not a random number. For example, the LocalSystem account has a SID of S-1-5-18, and for manually created users, it starts at 1000.

Thus, manually sorting out the sids from 1000 to 2000, you can, with a high degree of probability, locate the required accounts in the system. They were found under sidami 1008 and 1009.

The solution to this assignment required an understanding of the DCERPC protocol and the experience of exploring Windows infrastructures. @ psih1337 was the only one who decided it.

100.64.0.13 - DNSCAT


On the 80th port there is a web page with a form for the IP address.



If you specify your IP, then UDP arrives on port 53:

 17:40:45.501553 IP 100.64.0.13.38730 > 100.64.0.187: 61936+ CNAME? dnscat.d2bc039ce800000000d6eae8eae3bf81fd84d1695f5888aba8dcec06d071.a73b3f0561ca4906d268214f4b70da1bdb50f75739ae0577139096732bf8.0d0a987ce23408bac15426a22e. (173) 17:40:45.501639 IP 100.64.0.187 > 100.64.0.13: ICMP 100.64.0.187 udp port domain unreachable, length 209 17:40:46.520457 IP 100.64.0.13.38730 > 100.64.0.187: 21842+ TXT? dnscat.7f4e039ce800000000d6eae8eae3bf81fd84d1695f5888aba8dcec06d071.a73b3f0561ca4906d268214f4b70da1bdb50f75739ae0577139096732bf8.0d0a987ce23408bac15426a22e. (173) 17:40:46.520546 IP 100.64.0.187 > 100.64.0.13: ICMP 100.64.0.187 udp port domain unreachable, length 209 


Clearly, this is DNSCAT - a tool for DNS tunnels. After specifying the IP address in the form to it, the DNSCAT client tries to establish a connection. If this works out, the server (that is, the participant) will receive a shell on the competitive machine and pull out the flag.

Of course, if we simply raise the DNSCAT server and try to accept the connection, we will fail:

[Drop] [**] [1:4001:1] 'dnscat' string found in DNS response [**]

IDS-signature is triggered by the string “dnscat” in the traffic from our server - this is clearly stated in the message. Obfuscating or encrypting traffic also will not work.

Looking at the client code, we find that the checks in it are not strict enough. That is, the response string "dnscat" may not be at all! It only remains to remove it from the code or replace it with the NetSED utility on the fly. It’s much easier to replace right away, but I’ll still give you a patch for the server code:

 diff -r dnscat2/server/libs/dnser.rb dnscat2_bypass/server/libs/dnser.rb < segments << unpack("a#{len}") > segments << [unpack("a#{len}")[0].upcase] < name.split(/\./).each do |segment| > name.upcase.split(/\./).each do |segment| diff -r dnscat2/server/tunnel_drivers/driver_dns.rb dnscat2_bypass/server/tunnel_drivers/driver_dns.rb < response = (response == "" ? "dnscat" : ("dnscat." + response)) > response = (response == "" ? "dnsCat" : ("dnsCat." + response)) 

The competition had five solutions for this assignment.

100.64.0.14 - POST


Nobody got the flag from this competitive car.



We see a familiar form with an IP address. Someone invites us to participate in testing a new malware. Among its innovations is an IDS bypass in an unknown way. For a flag, you just need to send it the HTTP header “Server: ng1nx” in response. It will be hot.

As expected: we receive a GET request to our IP and send a response, which is blocked by IDS.

[Drop] [**] [1:5002:1] 'ng1nx' Server header found. Malware shall not pass [**]

There is a hint:

Sometimes tasks that look hard are the simplest. If you’re not feeling right

Something vulnerable right in front of our noses is IDS. From the triggers page you can find that we have an open Suricata IDS.



The first link on the query "Suricata IDS Bypass" leads to CVE-2018-6794 . This vulnerability allows you to bypass packet inspection, if you disrupt the normal course of establishing a TCP connection (TCP handshake) and send data before the process is complete. It looks like this:

 Client -> [SYN] [Seq=0 Ack=0] -> Evil Server # 1/2 Client <- [SYN, ACK] [Seq=0 Ack=1] <- Evil Server # 2/2 Client <- [PSH, ACK] [Seq=1 Ack=1] <- Evil Server # Data here Client <- [FIN, ACK] [Seq=83 Ack=1] <- Evil Server Client -> [ACK] [Seq=1 Ack=84] -> Evil Server # 3/2 Client -> [PSH, ACK] [Seq=1 Ack= 4] -> Evil Server 

We download the exploit, change the string to "ng1nx", turn off the kernel RST-packages and run.

As already mentioned, no one received flags from this car, although a couple of participants were close to the decision.

Conclusion


49 participants were registered in the competition, 12 passed at least one flag. It is interesting that competition tasks can have several solutions at once, especially tasks with the SMB and DCERPC protocol. Maybe you have your own ideas for completing some tasks?

Top places:


Signature response statistics:



Thanks to all participants! Next year there will be even more tasks of different levels of difficulty.

Author : Kirill Shipulin, Positive Technologies

Source: https://habr.com/ru/post/457932/


All Articles