[Fixed] Server breaks HTTP/308 redirect responses and does internal silent rev-proxy

#1
Hi!

It took me 3 hours to finally pinpoint why my application started generating weird "detect loop redirection" error messages.
Since all programmers love to see code let's begin with just plain test.php file:
Code:
<?php
if(isset($_GET['index_please'])) {
header("HTTP/1.1 308 Permanent Redirect");
header('Location: /index.php');
die();
}

if(isset($_GET['404_please'])) {
header("HTTP/1.1 308 Permanent Redirect");
header('Location: /non-existing-file');
die();
}

if(isset($_GET['500_please'])) {
header("HTTP/1.1 308 Permanent Redirect");
header('Location: ' . $_SERVER['PHP_SELF'] . '/derp');
die();
}

echo $_SERVER['REQUEST_URI'];
Then also create helper index.php file:
Code:
<?php
echo 'INDEX: ' . $_SERVER['REQUEST_URI'];
Virtualhost configuration is short and standard:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<virtualHostConfig>
  <docRoot>$VH_ROOT</docRoot>
</virtualHostConfig>

Now let's use good ol' telnet to eliminate browser influence:
Code:
$ telnet [redacted] 80
Trying [redacted]...
Connected to [redacted].
Escape character is '^]'.
GET /test.php?test HTTP/1.1
Host: [redacted]

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 14
Date: Wed, 09 Dec 2015 01:19:40 GMT
Accept-Ranges: bytes
Server: LiteSpeed

/test.php?test


That was just a test - it works as expected. Now the fun begins with next request:
Code:
$ telnet [redacted] 80
Trying [redacted]...
Connected to [redacted].
Escape character is '^]'.
GET /test.php?index_please HTTP/1.1
Host: [redacted]

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 22
Date: Wed, 09 Dec 2015 01:28:54 GMT
Accept-Ranges: bytes
Server: LiteSpeed

INDEX: /test.php?index_please
As you can see Litespeed made under-the-hood silent reverse proxy to index.php file when test.php generated 308 redirect to index.php. Weird and unacceptable due to some marketing-seo bullshit but it will work in the end, so from the admin perspective it's just "LGTM" ;)



Now let's try to abuse that and generate 404 using 308:
Code:
$ telnet [redacted] 80
Trying [redacted]...
Connected to [redacted].
Escape character is '^]'.
GET /test.php?404_please HTTP/1.1
Host: [redacted]

HTTP/1.1 404 Not Found
Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0
Pragma: no-cache
Content-Type: text/html
Content-Length: 618
Date: Wed, 09 Dec 2015 01:32:50 GMT
Accept-Ranges: bytes
Server: LiteSpeed
That makes sense if you contrast it with previous behaviour. In the end user will get 404 anyway, so everyone's happy.



The final example presents what took me so much time to debugging application to find an error. Usecase in my app is dare simple. Example url /old-article-title-13 should be redirected to /new-article-title-13 to prevent duplicated content on both new url and old one. I used 308 code for redirect and I've got previously mentioned "detect loop redirection" error.
Presented test.php script can replicate such case:
Code:
$ telnet [redacted] 80
Trying [redacted]...
Connected to [redacted].
Escape character is '^]'.
GET /test.php?500_please HTTP/1.1
Host: [redacted]

HTTP/1.1 500 Internal Server Error
Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0
Pragma: no-cache
Content-Type: text/html
Content-Length: 622
Date: Wed, 09 Dec 2015 01:21:48 GMT
Accept-Ranges: bytes
Server: LiteSpeed
Connection: close

<!DOCTYPE html>
<html style="height:100%">
<head><title> 500 Internal Server Error
</title></head>
<body style="color: #444; margin:0;font: normal 14px/20px Arial, Helvetica, sans-serif; height:100%; background-color: #fff;">
<div style="height:auto; min-height:100%; ">     <div style="text-align: center; width:800px; margin-left: -400px; position:absolute; top: 30%; left:50%;">
        <h1 style="margin:0; font-size:150px; line-height:150px; font-weight:bold;">500</h1>
<h2 style="margin-top:20px;font-size: 30px;">Internal Server Error
</h2>
<p>An internal server error has occured.</p>
</div></div></body></html>
Connection closed by foreign host.


This issue is present on v5.0.9 Standard running on Debian 64-bit. LSWS v4.2.23 running on OS X 10.11.1 64-bit handles all examples presented without any server-side processing (so 308 reaches browser).
Please keep in mind bug is also present when you use urls like /test.php/some_value or mod_rewrite instead of pure GET parameters. I also verified it with HTTP/1.0 and HTTP/2.0 requests - results aren't any different.


p.s. I didn't attached full server configuration, but I believe it's nothing special there. If you have troubles replicating that bug I can attach it, but I have to anonimize it for obvious reasons ;)
 
Last edited:
Top