(Updated) Mitigation of Apache Range Header DoS Attack

Update

After deeper research into the underlying vulnerability and analyzing customer traffic, SpiderLabs has developed a new BETA ModSecurity ruleset to mitigate the Apache Range Header DoS vulnerability. The following rules may be used to truncate the Range header fields to five:

SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "^bytes=\s*((\d+)?\-(\d+)?\,){5,}" "chain,phase:1,t:none,log,msg:'Truncating Large Range Header Field.',capture,pass"        SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "^bytes=\s*((\d+)?\-(\d+)?\,){5}" "chain,capture"                SecRule TX:0 "^(.*),$" "capture,setenv:range_header=%{tx.1}"RequestHeader unset Range env=range_headerRequestHeader set Range "%{range_header}e" env=range_header

These rules work by first checking for a Range header with more than five values. If this is found, the Range header ranges are then limited to 5 and then this data is exported to an Apache ENV variable. We then specify two new Apache RequestHeader directives. The 1st one removes the existing Range header and the 2nd one replaces it with our truncated version.

Side Note

During testing, RequestHeader edit was tested however the macro expansion from the ModSecurity ENV data was not working for some reason. This might be a bug in the current version tested.

The benefit of this new approach vs. other workarounds suggested are that instead of blocking requests with many Range fields, we can still allow the the request through. By truncating the Range fields, not only do we prevent the DoS condition but we also don't break other legitimate clients that are sending a large number of non-overlapping Range fields (specifically Adobe Acrobat was identified during our internal analysis of customer data).

We encourage organizations to test out these rules and to report back their experiences.

Original Post

There was a posting to the Full Disclosure mail-list yesterday by @kingcope in which he provided a script called killapache.pl that will cause a severe denial of service condition on Apache web servers. I had a chance to test out the script and can confirm that it will lock up an Apache server rather quickly. This blog post will highlight how the attack works and also the new rules that were just added to the OWASP ModSecurity CRS to prevent it.

HTTP Range Header

The attack sends malicious HTTP Range Request header data. The Range header is normally used when a client is requesting larger files from a web site. These files are too large to fit within the body of a single response so they are segmented and sent to the client in chunks. When sending responses to range requests, web server should trigger a 206 Partial Content HTTP status code.

killapache.pl Attack

The killapache.pl script sends the following request:

HEAD / HTTP/1.1Host: 127.0.0.1Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14,5-15,5-16,5-17,5-18,5-19,5-20,5-21,5-22,5-23,5-24,5-25,5-26,5-27,5-28,5-29,5-30,5-31,5-32,5-33,5-34,5-35,5-36,5-37,5-38,5-39,5-40,5-41,5-42,5-43,5-44,5-45,5-46,5-47,5-48,5-49,5-50,5-51,5-52,5-53,5-54,5-55,5-56,5-57,5-58,5-59,5-60,5-61,5-62,5-63,5-64,5-65,5-66,5-67,5-68,5-69,5-70,5-71,5-72,5-73,5-74,5-75,5-76,5-77,5-78,5-79,5-80,5-81,5-82,5-83,5-84,5-85,5-86,5-87,5-88,5-89,5-90,5-91,5-92,5-93,5-94,5-95,5-96,5-97,5-98,5-99,5-100,5-101,5-102,5-103,5-104,5-105,5-106,5-107,5-108,5-109,5-110,5-111,5-112,5-113,5-114,5-115,5-116,5-117,5-118,5-119,5-120,5-121,5-122,5-123,5-124,5-125,5-126,5-127,5-128,5-129,5-130,5-131,5-132,5-133,5-134,5-135,5-136,5-137,5-138,5-139,5-140,5-141,5-142,5-143,5-144,5-145,5-146,5-147,5-148,5-149,5-150,5-151,5-152,5-153,5-154,5-155,5-156,5-157,5-158,5-159,5-160,5-161,5-162,5-163,5-164,5-165,5-166,5-167,5-168,5-169,5-170,5-171,5-172,5-173,5-174,5-175,5-176,5-177,5-178,5-179,5-180,5-181,5-182,5-183,5-184,5-185,5-186,5-187,5-188,5-189,5-190,5-191,5-192,5-193,5-194,5-195,5-196,5-197,5-198,5-199,5-200,5-201,5-202,5-203,5-204,5-205,5-206,5-207,5-208,5-209,5-210,5-211,5-212,5-213,5-214,5-215,5-216,5-217,5-218,5-219,5-220,5-221,5-222,5-223,5-224,5-225,5-226,5-227,5-228,5-229,5-230,5-231,5-232,5-233,5-234,5-235,5-236,5-237,5-238,5-239,5-240,5-241,5-242,5-243,5-244,5-245,5-246,5-247,5-248,5-249,5-250,5-251,5-252,5-253,5-254,5-255,5-256,5-257,5-258,5-259,5-260,5-261,5-262,5-263,5-264,5-265,5-266,5-267,5-268,5-269,5-270,5-271,5-272,5-273,5-274,5-275,5-276,5-277,5-278,5-279,5-280,5-281,5-282,5-283,5-284,5-285,5-286,5-287,5-288,5-289,5-290,5-291,5-292,5-293,5-294,5-295,5-296,5-297,5-298,5-299,5-300,5-301,--CUT--1016,5-1017,5-1018,5-1019,5-1020,5-1021,5-1022,5-1023,5-1024,5-1025,5-1026,5-1027,5-1028,5-1029,5-1030,5-1031,5-1032,5-1033,5-1034,5-1035,5-1036,5-1037,5-1038,5-1039,5-1040,5-1041,5-1042,5-1043,5-1044,5-1045,5-1046,5-1047,5-1048,5-1049,5-1050,5-1051,5-1052,5-1053,5-1054,5-1055,5-1056,5-1057,5-1058,5-1059,5-1060,5-1061,5-1062,5-1063,5-1064,5-1065,5-1066,5-1067,5-1068,5-1069,5-1070,5-1071,5-1072,5-1073,5-1074,5-1075,5-1076,5-1077,5-1078,5-1079,5-1080,5-1081,5-1082,5-1083,5-1084,5-1085,5-1086,5-1087,5-1088,5-1089,5-1090,5-1091,5-1092,5-1093,5-1094,5-1095,5-1096,5-1097,5-1098,5-1099,5-1100,5-1101,5-1102,5-1103,5-1104,5-1105,5-1106,5-1107,5-1108,5-1109,5-1110,5-1111,5-1112,5-1113,5-1114,5-1115,5-1116,5-1117,5-1118,5-1119,5-1120,5-1121,5-1122,5-1123,5-1124,5-1125,5-1126,5-1127,5-1128,5-1129,5-1130,5-1131,5-1132,5-1133,5-1134,5-1135,5-1136,5-1137,5-1138,5-1139,5-1140,5-1141,5-1142,5-1143,5-1144,5-1145,5-1146,5-1147,5-1148,5-1149,5-1150,5-1151,5-1152,5-1153,5-1154,5-1155,5-1156,5-1157,5-1158,5-1159,5-1160,5-1161,5-1162,5-1163,5-1164,5-1165,5-1166,5-1167,5-1168,5-1169,5-1170,5-1171,5-1172,5-1173,5-1174,5-1175,5-1176,5-1177,5-1178,5-1179,5-1180,5-1181,5-1182,5-1183,5-1184,5-1185,5-1186,5-1187,5-1188,5-1189,5-1190,5-1191,5-1192,5-1193,5-1194,5-1195,5-1196,5-1197,5-1198,5-1199,5-1200,5-1201,5-1202,5-1203,5-1204,5-1205,5-1206,5-1207,5-1208,5-1209,5-1210,5-1211,5-1212,5-1213,5-1214,5-1215,5-1216,5-1217,5-1218,5-1219,5-1220,5-1221,5-1222,5-1223,5-1224,5-1225,5-1226,5-1227,5-1228,5-1229,5-1230,5-1231,5-1232,5-1233,5-1234,5-1235,5-1236,5-1237,5-1238,5-1239,5-1240,5-1241,5-1242,5-1243,5-1244,5-1245,5-1246,5-1247,5-1248,5-1249,5-1250,5-1251,5-1252,5-1253,5-1254,5-1255,5-1256,5-1257,5-1258,5-1259,5-1260,5-1261,5-1262,5-1263,5-1264,5-1265,5-1266,5-1267,5-1268,5-1269,5-1270,5-1271,5-1272,5-1273,5-1274,5-1275,5-1276,5-1277,5-1278,5-1279,5-1280,5-1281,5-1282,5-1283,5-1284,5-1285,5-1286,5-1287,5-1288,5-1289,5-1290,5-1291,5-1292,5-1293,5-1294,5-1295,5-1296,5-1297,5-1298,5-1299Accept-Encoding: gzipConnection: close

By sending this single request with such a large number of fields within the Range header, the attacker is amplifying their request as each byte range field forces Apache to make separate copies of the requested resource server-side which is consuming resources deep within the Apache internals. See this Apache Dev list thread on bucket brigade issues in handling this attack.

Mitigating the Range Header DoS Attack with ModSecurity

One of the most under-appreciated apsects of ModSecurity is the fact that it can often be used to mitigate vulnerabilities found within Apache itself and this Range header DoS attack is just the latest example. While the core issue should certianly be addressed within the Apache code itself, in the meantime, Apache administrators could also use ModSecurity and the latest version of the OWASP ModSecurity CRS to mitigate this attack. The SpiderLabs Research Team has just updated the 20 Protocol Violations rules file to include new rules to mitigate these various Range header anomalies.

Invalid Byte Ranges

Per the HTTP RFC states the following about the the byte range fields:

If the last-byte-pos value is present, it MUST be greater than or   equal to the first-byte-pos in that byte-range-spec, or the byte-   range-spec is syntactically invalid.

If you look at the initial Range fields of the attack you will see that a number of them are then considered invalid:

HEAD / HTTP/1.1Host: 127.0.0.1Range: bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14,5-15,5-

This is due to the following looping code in the killapache.pl script -

$p = "";for ($k=0;$k<1300;$k++) {        $p .= ",5-$k";}

This logic could obviously be fixed within the script but this type of RFC violation is easily caught with the following new CRS rule:

SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range "(\d+)\-(\d+)\," "chain,capture,phase:2,rev:'2.2.2',t:none,block,msg:'Range: Invalid Last Byte Value.',logdata:'%{matched_var}'severity:'5',id:'958230',tag:'RULE_MATURITY/5',tag:'RULE_ACCURACY/7',tag:'https://www.owasp.org/index.php/ModSecurity_CRS_RuleID-%{tx.id}',tag:'PROTOCOL_VIOLATION/INVALID_HREQ',tag:'http://www.bad-behavior.ioerror.us/documentation/how-it-works/',setvar:'tx.msg=%{rule.msg}',setvar:tx.id=%{rule.id},setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/INVALID_HREQ-%{matched_var_name}=%{matched_var}"        SecRule TX:2 "!@ge %{tx.1}"

This rule will capture both the 1st and 2nd byte range values and then compares them and will alert if the 2nd value is not greater than or equal to the 1st.

Excessive Amount of Byte Range Values

The other characteristic of this attack that can be flagged is the excessive amount of range fields within a single request. Normal web clients (browsers) don't specify such a high number of ranges within a single request. The following CRS rule will alert if there are five ranges within a single request:

SecRule REQUEST_HEADERS:Range|REQUEST_HEADERS:Request-Range
"^bytes=(\d+)?\-(\d+)?\,\s?(\d+)?\-(\d+)?\,\s?(\d+)?\-(\d+)?\,\s?(\d+)?\-(\d+)?\,\s?(\d+)?\-(\d+)?\," \ "phase:2,capture,rev:'2.2.1',t:none,block,msg:'Range: Too many fields',logdata:'%{matched_var}'severity:'5',id:'958231',tag:'RULE_MATURITY/5',tag:'RULE_ACCURACY/7',tag:'https://www.owasp.org/index.php/ModSecurity_CRS_RuleID-%{tx.id}',tag:'PROTOCOL_VIOLATION/INVALID_HREQ',tag:'http://www.bad-behavior.ioerror.us/documentation/how-it-works/',setvar:'tx.msg=%{rule.msg}',setvar:tx.id=%{rule.id},setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/INVALID_HREQ-%{matched_var_name}=%{matched_var}"

Trustwave reserves the right to review all comments in the discussion below. Please note that for security and other reasons, we may not approve comments containing links.