Suricata 6.0.4: HTTP URI normalization?

Hi there,

Upon researching the Citrix CVE-2019-19781 vulnerability and the according rulesets of Suricata (2029206, 2029255 and 2034279) I found that based on IOC’s these rules would not trigger evasion techniques based on ASCII-encoding such as: ’http://x.x.x.x/vpn/js/../
.%2E/%76pns/cfg/smb.conf .

Original rules:

alert http any any -> $HTTP_SERVERS any (msg:"ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781)"; flow:established,to_server; http.uri; content:"/vpns/"; fast_pattern; http.uri.raw; content:"/../"; reference:url,support.citrix.com/article/CTX267679; reference:cve,2019-19781; classtype:attempted-admin; sid:2029206; rev:4; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2019_12_30, deployment Perimeter, deployment SSLDecrypt, former_category EXPLOIT, signature_severity Major, updated_at 2020_10_27;)

alert http any any -> $HTTP_SERVERS any (msg:"ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) M2"; flow:established,to_server; http.uri; content:"/vpns/"; fast_pattern; http.header; content:"|0d 0a|NSC_USER|3a 20|"; nocase; content:"|0d 0a|NSC_NONCE|3a 20|"; nocase; content:"/../"; reference:url,support.citrix.com/article/CTX267679; reference:cve,2019-19781; classtype:attempted-admin; sid:2029255; rev:3; metadata:affected_product Web_Server_Applications, attack_target Server, created_at 2020_01_13, deployment Perimeter, signature_severity Critical, updated_at 2020_11_10;)

Based on that I made the following rule with a regex filter to filter on possible ASCII-encoding.

alert http any any -> $HTTP_SERVERS any (msg:"ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781)";
flow:established,to_server; http.uri; content:"/vpns/"; nocase; http.uri.raw; pcre:"/((%2F|\/)(\.|%2E)(\.|%2E)(%2F|\/))/I";
reference:url,support.citrix.com/article/CTX267679; reference:cve,2019-19781; classtype:attempted-admin; sid:1999999; rev:1; metadata:affected_product Web_Server_Applications, attack_target Server, created_at 2022_01_23, deployment Perimeter, signature_severity Critical;)

Could anybody explain why the original rules would not work based on the Suricata http_uri function?

Regards,
citrix_dataset_clean_http_2_evasion.pcapng (21.1 KB)
cve-2019-19781_port80_GET_vulnerability_path_check.pcap (1.4 KB)

Hello there!

First, I want to say thanks for providing pcaps, they contain the complete exploit chain, with the reverse shell. It’s not very often False Negatives are reported with this associated data which makes investigating them so much easier. If I had a gold star to give you, I would. It is well deserved.

I’ve taken a good look at these rules in comparison to your pcaps. I’ll first explain the basic the rule logic, and then examine them one pcap at a time and attempt to explain why/how the rules alert, or not. I will do this over multiple posts in effort to format thing cleanly. This is probably WAY more than you were looking for, but hopefully it proves useful.

TLDR;

  • Amazing FN report, thank you!
  • Buffer normalization can make this stuff tricky, IDS evasion is a real thing.
  • The url_decode transformation applied to the http.uri.raw buffer works well, but is only available on Suricata 6.0+
  • Additional signature will be released to provide additional coverage of this exploit.
  • Feedback, including False Negatives or even requests for new signatures, on Emerging Threats sigs can be reported to Emerging Threats via the feedback portal

sid:2029206

This signature makes use of the unnormalized http.uri.raw buffer, which means the /../ has to appear in the original sent traffic.

sid 2029255

This signature makes use of the normalized http.header buffer. However, url-encoding is not normalized, as per the
docs, the normalzition occurring can be found here on the mailing list post from Oct of 2011

So for this rule to alert, /../ has to occur in the originally sent traffic, without any other method of obfuscation.

cve-2019-19781_port80_GET_vulnerability_path_check.pcap

This pcap has two http request/response pairs, I’ll focus only on the http requests as this is the data which is of interest to the two signature in question.

this pcap and suricata variables

In this pcap, all traffic to sourced from 127.0.0.2 and destined to 127.0.0.1, which by default is not part of the $HOME_NET or $HTTP_SERVERS variables. There are a couple ways to address this, but in my testing, I opted to just add 127.0.0.1 to the $HOME_NET variable. Proper variables is critical to the correct evaluation of the rules. In my opinion this is singly the most overlooked configuration item notice leading to False Negatives and False Positives.

Emerging Threats generally considers $HOME_NET to be loosely defined as “The network you are trying to defend/protect”. In this case, given the ‘server’ is being exploited, we can include 127.0.0.1 in $HOME_NET which, in the below configuration, will force 127.0.0.2 to be considered $EXTERNAL_NET

vars:
  # more specific is better for alert accuracy and performance
  address-groups:
    HOME_NET: "[127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"
    EXTERNAL_NET: "!$HOME_NET"
    HTTP_SERVERS: "$HOME_NET"

first request

00000000  47 45 54 20 2f 76 70 6e  2f 2e 2e 2f 76 70 6e 73   GET /vpn /../vpns
00000010  2f 63 66 67 2f 73 6d 62  2e 63 6f 6e 66 20 48 54   /cfg/smb .conf HT
00000020  54 50 2f 31 2e 31 0d 0a  48 6f 73 74 3a 20 31 30   TP/1.1.. Host: 10
00000030  2e 31 30 2e 31 2e 32 35  31 0d 0a 55 73 65 72 2d   .10.1.25 1..User-
00000040  41 67 65 6e 74 3a 20 63  75 72 6c 2f 37 2e 36 34   Agent: c url/7.64
00000050  2e 31 0d 0a 41 63 63 65  70 74 3a 20 2a 2f 2a 0d   .1..Acce pt: */*.
00000060  0a 63 69 70 2d 68 65 61  64 65 72 3a 20 31 30 2e   .cip-hea der: 10.
00000070  31 30 2e 31 2e 36 32 20  20 20 20 20 0d 0a 0d 0a   10.1.62      ....

sid:2029206 correctly alerts on this traffic (once variables are defined), but an explanation of how suricata “normalizes” the http.uri buffer follows

If we attempt to use the normalized http.uri, the buffer ends up “normalzing” the directory traversal, and the http.uri buffer actually becomes

00000000  2F 76 70 6E 73 2F 63 66 67 2F 73 6D 62 2E 63 6F |/vpns/cfg/smb.co|
00000010  6E 66                                           |nf         

while http.uri.raw, which is unnormalized shows up as

00000000  2F 76 70 6E 2F 2E 2E 2F 76 70 6E 73 2F 63 66 67 |/vpn/../vpns/cfg|
00000010  2F 73 6D 62 2E 63 6F 6E 66                      |/smb.conf       |

Alerts

We only expect 2029206 to alert on this traffic, and it does.

01/13/2020-10:32:30.621624  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 127.0.0.2:28147 -> 127.0.0.1:80

second request

00000080  47 45 54 20 2f 76 70 6e  2f 2e 2e 2f 56 50 6e 73   GET /vpn /../VPns
00000090  2f 63 66 67 2f 73 6d 62  2e 63 6f 6e 66 20 48 54   /cfg/smb .conf HT
000000A0  54 50 2f 31 2e 31 0d 0a  48 6f 73 74 3a 20 31 30   TP/1.1.. Host: 10
000000B0  2e 31 30 2e 31 2e 32 35  31 0d 0a 55 73 65 72 2d   .10.1.25 1..User-
000000C0  41 67 65 6e 74 3a 20 63  75 72 6c 2f 37 2e 36 34   Agent: c url/7.64
000000D0  2e 31 0d 0a 41 63 63 65  70 74 3a 20 2a 2f 2a 0d   .1..Acce pt: */*.
000000E0  0a 63 69 70 2d 68 65 61  64 65 72 3a 20 31 30 2e   .cip-hea der: 10.
000000F0  31 30 2e 31 2e 36 32 20  20 20 20 20 0d 0a 0d 0a   10.1.62      ....

Except for the casing of the /VPns, this traffic is the exact same as the first request. As such, one could expect sid:2029206 to alert, however, this is a very basic evasion. It can be corrected by applying a “nocase” modifier to the rule below (tracked as rev:5)

alert http any any -> $HTTP_SERVERS any (msg:"ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781)"; flow:established,to_server; http.uri; content:"/vpns/"; nocase; fast_pattern; http.uri.raw; content:"/../"; reference:url,support.citrix.com/article/CTX267679; reference:cve,2019-19781; classtype:attempted-admin; sid:2029206; rev:5; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2019_12_30, deployment Perimeter, deployment SSLDecrypt, former_category EXPLOIT, signature_severity Major, updated_at 2020_10_27;)

Alerts

After applying rev:5; the rule we see two alerts for both requests.

01/13/2020-10:32:30.621624  [**] [1:2029206:5] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 127.0.0.2:28147 -> 127.0.0.1:80
01/13/2020-10:32:35.512774  [**] [1:2029206:5] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 127.0.0.2:28147 -> 127.0.0.1:80

citrix_dataset_clean_http_2_evasion.pcapng

This pcap consists of four unique TCP streams. Again, I’ll try to break them all down here, but will not go into as much detail.

  1. 192.168.116.1:33530 → 162.168.116.65:80
  2. 192.168.116.1:1034 → 162.168.116.65:80
  3. 192.168.116.1:1035 → 162.168.116.65:80
  4. 192.168.116.25:36238 → 192.168.116.1:5555

192.168.116.1:33530 → 162.168.116.65:80

These requests attempt to validate a vulnerable server.

This session contains multiple HTTP requests/response pairs, and while they do contain minor differences, for the purposes of these two signatures in questions, they are all the same

GET /vpn/js/%2E./.%2E/%76pns/cfg/smb.conf HTTP/1.1
User-Agent: PostmanRuntime/7.29.0
Accept: */*
Postman-Token: 8bf270c7-7142-41f9-b852-97feba79ee3c
Host: 192.168.116.65
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
RAW_URI_DUMP, 37

00000000  2F 76 70 6E 2F 6A 73 2F 25 32 45 2E 2F 2E 25 32 |/vpn/js/%2E./.%2|
00000010  45 2F 25 37 36 70 6E 73 2F 63 66 67 2F 73 6D 62 |E/%76pns/cfg/smb|
00000020  2E 63 6F 6E 66                                  |.conf           |

URI_DUMP, 18

00000000  2F 76 70 6E 73 2F 63 66 67 2F 73 6D 62 2E 63 6F |/vpns/cfg/smb.co|
00000010  6E 66                                           |nf              |

Alerts

None observed for this session - Traffic should fire sid:2029206 however, this is successfully evaded via the %76 and %2E obfuscation within the http.uri.raw buffer.

Tune

I think the best way to reliably cause this to alert would be to apply the url_decode part of normalization to the http.uri.raw buffer, we can do this in Suricata 6.0+, as seen in rev:6 below, by using the url_decode transformation.

alert http any any -> $HTTP_SERVERS any (msg:"ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781)"; flow:established,to_server; http.uri; content:"/vpns/"; fast_pattern; nocase; http.uri.raw; url_decode; content:"/../"; reference:url,support.citrix.com/article/CTX267679; reference:cve,2019-19781; classtype:attempted-admin; sid:2029206; rev:6; metadata:affected_product Windows_XP_Vista_7_8_10_Server_32_64_Bit, attack_target Client_Endpoint, created_at 2019_12_30, deployment Perimeter, deployment SSLDecrypt, former_category EXPLOIT, signature_severity Major, updated_at 2020_10_27;)

However, Emerging Threats currently supports Suricata 6.0+ via the Suricata 5 ruleset, and therefore cannot currently make use of this transformation within our ruleset. In order to create a ruleset that would make use of keywords/transformations/etc supported by Suricata 6.0+, a new ruleset would have to be produced. ET calls this process “forking the ruleset”.

By deploying sid:2029206; rev:6; as described above, we see the alerts fire for each HTTP request.

01/21/2022-15:36:34.072526  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:33530 -> 192.168.116.65:80
01/21/2022-15:36:35.221486  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:33530 -> 192.168.116.65:80
01/21/2022-15:36:36.444532  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:33530 -> 192.168.116.65:80
01/21/2022-15:36:37.296835  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:33530 -> 192.168.116.65:80
01/21/2022-15:36:38.071834  [**] [1:2029206:4] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:33530 -> 192.168.116.65:80

192.168.116.1:1034 → 162.168.116.65:80

This session uploads a python reverse shell to the proposed template file.

ASCII Post

POST /vpn/%2E./vpns/portal/scripts/newbm.pl HTTP/1.1
Host: 192.168.116.65
Accept-Encoding: identity
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
NSC_USER: ../../../netscaler/portal/templates/pedvsxnurk
NSC_NONCE: 6
Content-Length: 3661
Content-Type: application/x-www-form-urlencoded

http.uri.raw unnormalized buffer

RAW_URI_DUMP, 38

00000000  2F 76 70 6E 2F 25 32 45 2E 2F 76 70 6E 73 2F 70 |/vpn/%2E./vpns/p|
00000010  6F 72 74 61 6C 2F 73 63 72 69 70 74 73 2F 6E 65 |ortal/scripts/ne|
00000020  77 62 6D 2E 70 6C                               |wbm.pl          |

http.uri normalized buffer

URI_DUMP, 29

00000000  2F 76 70 6E 73 2F 70 6F 72 74 61 6C 2F 73 63 72 |/vpns/portal/scr|
00000010  69 70 74 73 2F 6E 65 77 62 6D 2E 70 6C          |ipts/newbm.pl   |

http.headers buffer

REQ_HEADERS_DUMP, 290

00000000  48 6F 73 74 3A 20 31 39 32 2E 31 36 38 2E 31 31 |Host: 192.168.11|
00000010  36 2E 36 35 0D 0A 41 63 63 65 70 74 2D 45 6E 63 |6.65..Accept-Enc|
00000020  6F 64 69 6E 67 3A 20 69 64 65 6E 74 69 74 79 0D |oding: identity.|
00000030  0A 55 73 65 72 2D 41 67 65 6E 74 3A 20 4D 6F 7A |.User-Agent: Moz|
00000040  69 6C 6C 61 2F 35 2E 30 20 28 4D 61 63 69 6E 74 |illa/5.0 (Macint|
00000050  6F 73 68 3B 20 49 6E 74 65 6C 20 4D 61 63 20 4F |osh; Intel Mac O|
00000060  53 20 58 20 31 30 2E 31 34 3B 20 72 76 3A 37 31 |S X 10.14; rv:71|
00000070  2E 30 29 20 47 65 63 6B 6F 2F 32 30 31 30 30 31 |.0) Gecko/201001|
00000080  30 31 20 46 69 72 65 66 6F 78 2F 37 31 2E 30 0D |01 Firefox/71.0.|
00000090  0A 4E 53 43 5F 55 53 45 52 3A 20 2E 2E 2F 2E 2E |.NSC_USER: ../..|
000000A0  2F 2E 2E 2F 6E 65 74 73 63 61 6C 65 72 2F 70 6F |/../netscaler/po|
000000B0  72 74 61 6C 2F 74 65 6D 70 6C 61 74 65 73 2F 70 |rtal/templates/p|
000000C0  65 64 76 73 78 6E 75 72 6B 0D 0A 4E 53 43 5F 4E |edvsxnurk..NSC_N|
000000D0  4F 4E 43 45 3A 20 36 0D 0A 43 6F 6E 74 65 6E 74 |ONCE: 6..Content|
000000E0  2D 4C 65 6E 67 74 68 3A 20 33 36 36 31 0D 0A 43 |-Length: 3661..C|
000000F0  6F 6E 74 65 6E 74 2D 54 79 70 65 3A 20 61 70 70 |ontent-Type: app|
00000100  6C 69 63 61 74 69 6F 6E 2F 78 2D 77 77 77 2D 66 |lication/x-www-f|
00000110  6F 72 6D 2D 75 72 6C 65 6E 63 6F 64 65 64 0D 0A |orm-urlencoded..|
00000120  0D 0A                                           |..              |

Alerts

  • sid:2029255 successfully alerts on this session as expected given the /../ occurring in the http.header buffer.
  • sid:2029206; rev:4; does not alert and is successfully evaded due to the use of %2E in the uri.
  • sid:2029206; rev:5; does not alert and is successfully evaded due to the use of %2E in the uri.
  • sid:2029206; rev:6; successfully alerts on this traffic.
01/21/2022-15:36:47.276584  [**] [1:2029255:3] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) M2 [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:1034 -> 192.168.116.65:80
01/21/2022-15:36:47.276584  [**] [1:2029206:6] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:1034 -> 192.168.116.65:80

Interestingly enough, there is actually another signature which has been produced to fire on this session, but also FNs. This is sid:2034279;, it FNs for different reasons which do not involve uri normalization and as such, I’ll not bother explaining here, but will push an update to the rule. It’ll come out at rev:2;

192.168.116.1:1035 → 162.168.116.65:80

This session contains a single HTTP request which executes the uploaded python reverse shell

GET /vpn/%2E./vpns/portal/pedvsxnurk.xml HTTP/1.1
Host: 192.168.116.65
Accept-Encoding: identity
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
NSC_USER: tzynytmqli
NSC_NONCE: 6

Alerts

  • sid:2029206; rev:4; does not alert and is successfully evaded due to the use of %2E in the uri.
  • sid:2029206; rev:5; does not alert and is successfully evaded due to the use of %2E in the uri.
  • sid:2029206; rev:6; successfully alerts on this traffic.
01/21/2022-15:36:34.012778  [**] [1:2029206:6] ET EXPLOIT Possible Citrix Application Delivery Controller Arbitrary Code Execution Attempt (CVE-2019-19781) [**] [Classification: Attempted Administrator Privilege Gain] [Priority: 1] {TCP} 192.168.116.1:1035 -> 192.168.116.65:80

192.168.116.25:36238 → 192.168.116.1:5555

This session contains the reverse shell traffic, showing a complete and successful exploit chain.

It’s not related to the question at hand, so I’ll skip it. Though an Alert does fire on the use of the id command here. There are some additional signature opportunities, I’ll get those out as soon as I can.

01/21/2022-15:36:34.012778  [**] [1:2019284:3] ET ATTACK_RESPONSE Output of id command from HTTP server [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.116.25:36238 -> 192.168.116.1:5555

Hi bmurphy,

Many thanks for going into great details of describing how the normalization takes place in the different versions of Suricata. This was exactly what I needed and was very helpful for my research paper.
It is really interesting to see how lifecycle management and legacy versions pose a problem within default rulesets. Would you happen to have a link that describes this proces of forking v5 to v6 rules?
If I understand it correctly it will be possible to detect this attack for newer setups running Suricata v6. For older systems running v5, would it be advisable to use the PCRE like suggested earlier, or would that have too much impact to really take this into consideration?
Again, many thanks for the in-depth explanation!

Regards,

I don’t have a link, but the general process is that the v5 ruleset will be cloned into a new ruleset. At that point, several things start to occur.

  1. Mass updates are done on existing rules to better support new rule language keywords, transforms, buffers, etc.
  2. New rules will be written “natively” in this new ruelset to make use of available keywords, transforms, buffers, etc
  3. QA will be run on the new ruleset, using multiple version of suricata which are designed to support the new ruleset.

There are a bunch of smaller impacts/processes that need to happen as well. Due to all the effort and time that go into supporting a new version and ruleset, the decision of when to “fork the ruleset” is not taken lightly, and generally speaking a substantial set of keywords/buffers/etc. The Suri5 fork introduced a huge amount of new “sticky buffers” and better ways to handle common detection logics like “endswith;” and “startswith;”

Try running those pcaps through with the latest ruleset! They should have much better coverage, and I opted for using PCRE like you suggested. These rules, and many others like it, will likely be revisited when the next ruleset fork occurs to make better use of the transformations available!