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_header
RequestHeader 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.1 Host: 127.0.0.1 Range: 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-1299 Accept-Encoding: gzip Connection: 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.1
Host: 127.0.0.1
Range: 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}"

hi
tried the mod security ruleset on apache 2.2 using mod_security 2.5 and am seeing these errors in the error_log. as for the killer script, it still is able to load apache very heavily. so basically, i am seeing no effect with this ruleset..
[Thu Sep 01 15:21:58 2011] [error] [client 10.11.14.22] ModSecurity: Warning. Pattern match "^(.*),$" at TX:0. [file "/etc/httpd/conf.d/00_mod_security.conf"]
[line "9"] [msg "Truncating Large Range Header Field."] [hostname "postfix"] [uri "/"] [unique_id "rErE7woLDhYAAE-sJJUAAAAE"]
[Thu Sep 01 15:21:58 2011] [error] [client 10.11.14.22] ModSecurity: Warning. Pattern match "^(.*),$" at TX:0. [file "/etc/httpd/conf.d/00_mod_security.conf"]
[line "9"] [msg "Truncating Large Range Header Field."] [hostname "postfix"] [uri "/"] [unique_id "rErOswoLDhYAAFG3KsUAAAAI"]
[Thu Sep 01 15:21:58 2011] [error] [client 10.11.14.22] ModSecurity: Warning. Pattern match "^(.*),$" at TX:0. [file "/etc/httpd/conf.d/00_mod_security.conf"]
[line "9"] [msg "Truncating Large Range Header Field."] [hostname "postfix"] [uri "/"] [unique_id "rEq2nQoLDhYAAE-qI8EAAAAC"]
[Thu Sep 01 15:21:58 2011] [error] [client 10.11.14.22] ModSecurity: Warning. Pattern match "^(.*),$" at TX:0. [file "/etc/httpd/conf.d/00_mod_security.conf"]
[line "9"] [msg "Truncating Large Range Header Field."] [hostname "postfix"] [uri "/"] [unique_id "rErnzgoLDhYAAFKzN-sAAAAK"]
Posted by: Saas Qa | 01 September 2011 at 17:31
Hi,
The first rule only validates the first range that has start and end byte positions. It can be circumvented by adding some valid range before the invalid ranges, e.g.
Range: bytes=0-,5-6,5-0,5-1,5-2,5-3,5-4,...
Still, it's possible to write separate rules that validate the first N ranges, and then separately limit the number of allowed ranges to N.
Posted by: Sampo Niskanen | 30 August 2011 at 02:01
For the ModSecurity 1.x users out there this might do the trick - assuming they don't really rely on using the range request headers for anything in particular...
# killapache.pl Aug 2011
SecFilterSelective HTTP_Range !(^$|^bytes=0-$)
SecFilterSelective HTTP_Request-Range !(^$|^bytes=0-$)
Supposed to say: We accept range headers only when
- "^$" empty value - or header not present
- value is "bytes=0-"
Posted by: Erik | 25 August 2011 at 17:16
Whoops, did wrong.
The rule I wrote will block ALL requests due to a little misspelling.
This is the correct:
RewriteEngine On
RewriteCond %{HTTP:range} ^.+$ [NC]
RewriteRule .* - [F]
RewriteCond %{HTTP:request-range} ^.+$ [NC]
RewriteRule .* - [F]
note that it is changed to .+ instead of .*, .* means everything, even a unspecified string, but .+ means at least one charachter.
So do the above, it will sucessfully block request containing a Range and Request-Range header.
Posted by: Sebastian Nielsen | 25 August 2011 at 11:12
Another good idea is to completely block range request by filtering the header.
RewriteEngine On
RewriteCond %{HTTP:range} ^.*$ [NC]
RewriteRule .* - [F]
RewriteCond %{HTTP:request-range} ^.*$ [NC]
RewriteRule .* - [F]
will block all requests containing a Range header.
Posted by: Sebastian Nielsen | 25 August 2011 at 11:04
Rm4dillo - this blocks other range requests too : we happen to use a flash based uploader (uploadify) which sends a request like this when it's loaded using a version query string (v=4.1) required by IE to render correctly.
Range: bytes=0-
If-Range: Fri, 15 Jul 2011 10:44:34 GMT
That is 403 denied by the configuration suggested in the .htaccess workaround, so the uploader file is never loaded
I've suggested this additional check which allows the "range:0-" request
# Explicitly allow "range:0-" request from Flash uploaded client request
RewriteCond %{HTTP:range} ^bytes=0-$
RewriteRule .* - [L]
# @see http://marc.info/?l=apache-httpd-dev&m=131418828705324&w=2
RewriteCond %{HTTP:range} ^bytes=[^,]+(,[^,]+){0,4}$
RewriteRule .* - [F]
Regards - Neil Smith
Posted by: Neil Smith | 25 August 2011 at 05:05
Hi Ryan,
I think that browsers never send HEAD requests with a "Range" header as it's no use (I checked on high traffic servers). Adding a rule that blocks HEAD requests with "Range" header should stop script kiddies.
I also noticed that Apache also accepts "Request-Range" header (that acts just the same as "Range") for compatibility reasons. Yous should probably added it to these rules.
Anyway, thank you for your fast reaction concerning the issue!
Rm4dillo
Posted by: Armadillo Dasypodidae | 25 August 2011 at 03:09