Creating the “Util – AspNet Control Encodings (Raw Format).h2″ script
Here is the sequence that created the script described in Consuming ASP.NET Control Encoding mappings and visualizing them – Part 1 which visualizes the html based ASP.NET control encodings into an serializable C# class.
The ultimate objective is to create a complete/comprehensive ASP.NET Web Controls XSS Mappings object, and this is just the first step on that direction
Fetching Url contents, save as local file and show in IE
var sourceFile = "http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-08-91-89-96/asp.net_5F00_control_5F00_encoding.htm";
var htmlContents = sourceFile.uri().getHtml();
var tempFile = htmlContents.saveAs( "".tempDir().pathCombine("asp.net_control_encodings.htm"));
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
return tempFile;
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
the url contents was saved to C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm
Viewing saved file
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
return tempFile;
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
looking at the Html Elements of the IE Object
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
ie.showElementsInTreeView();
return "ok";
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
Getting all rows (TR Element) – slow mode
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
return ie.elements("TR");
return "ok";
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
Getting the first row – slow mode
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
ie.details();
var rows= ie.elements("TR");
return rows[0];
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
Getting all rows – faster method
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
ie.details();
return ie.IE.TableRows;
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
show rows data in treeView
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
var rowsText = new List<string>();
foreach(var row in ie.IE.TableRows)
rowsText.add(row.str());
var treeView = topPanel.insert_Left()
.add_TreeView()
.add_Nodes(rowsText);
return "ok";
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
show rows data as a strongly type object in a table view:
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
var mappedData = (from row in ie.IE.TableRows
select new {
type = row.TableCells[0].str(),
propertyName = row.TableCells[1].str(),
attributeName_Script = row.TableCells[2].str(),
htmlEncode_scriptEncode = row.TableCells[3].str(),
urlEncode = row.TableCells[4].str()
}).toList();
topPanel.insert_Left()
.add_TableList()
.show(mappedData);
return mappedData.save();
//using WatiN.Core
//O2File:WatiN_IE_ExtensionMethods.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
Create a Dynamic type (won’t work due to serialization issues)
var dynamicTypeBuilder = "AspNetControlEncodings".dynamicType();
dynamicTypeBuilder.add_Properties<string>("type" , "propertyName", "attributeName_Script", "htmlEncode_scriptEncode","urlEncode");
var AspNetControlEncodings = dynamicTypeBuilder.create();
var encodingsList = AspNetControlEncodings.ctor().wrapOnList();
Populate data in a class that can be serialized
In the file AspNetControlEncodings.cs Create the class:
namespace O2.XRules.Database.Utils
{
public class AspNetControlEncodings_Raw : List<AspNetControlEncoding_Raw>
{
}
public class AspNetControlEncoding_Raw
{
[XmlAttribute] public string @Type { get; set;}
[XmlAttribute] public string PropertyName { get; set;}
[XmlAttribute] public string AttributeName_Script { get; set;}
[XmlAttribute] public string HtmlEncode_scriptEncode { get; set;}
[XmlAttribute] public string UrlEncode { get; set;}
}
}
The AspNetControlEncodings.cs is now used/consumed by this script
//var topPanel = O2Gui.open<Panel>("{name}",700,400);
var tempFile = @"C:\O2\_tempDir\11-8-2011\asp.net_control_encodings.htm";
var topPanel = panel.clear().add_Panel();
var ie = topPanel.add_IE_with_NavigationBar().silent(false);
ie.open(tempFile);
var mappedData = (from row in ie.IE.TableRows
select new AspNetControlEncoding_Raw {
@Type = row.TableCells[0].str(),
PropertyName = row.TableCells[1].str(),
AttributeName_Script = row.TableCells[2].str(),
HtmlEncode_scriptEncode = row.TableCells[3].str(),
UrlEncode = row.TableCells[4].str()
}).toList();
topPanel.insert_Left()
.add_TableList()
.show(mappedData);
return mappedData.save();
return "ok";
//using WatiN.Core
//O2File:WatiN_IE_ExtensionMethods.cs
//O2File:AspNetControlEncodings.cs
//using O2.XRules.Database.Utils.O2
//O2Ref:WatiN.Core.1x.dll
The data will be shown in a table list and a temp file will be created. In this case C:\O2\_tempDir\11-9-2011\tmp3A1F.tmp.xml (which will be save as AspNetControlEncodings_Raw.xml in the O2 Scripts folder)
show data in TableList directly (must faster)
var topPanel = panel.clear().add_Panel(); var tempFile = @"C:\O2\_tempDir\11-9-2011\tmp3A1F.tmp.xml"; var mappedData = tempFile.load<AspNetControlEncodings_Raw>(); topPanel.add_TableList() .show(mappedData); return mappedData.save(); //using WatiN.Core //O2File:WatiN_IE_ExtensionMethods.cs //O2File:AspNetControlEncodings.cs //using O2.XRules.Database.Utils.O2 //O2Ref:WatiN.Core.1x.dll
Show data in popup form
var topPanel = O2Gui.open<Panel>("Util - AspNet ControlEncodings (Raw Format) ",700,400);
var mappedData = "AspNetControlEncodings_Raw.xml".local().load<AspNetControlEncodings_Raw>();
topPanel.add_TableList()
.show(mappedData);
//O2File:AspNetControlEncodings.cs
FinalScript:
ColorCoding data in popupForm (this is the Final version of the Script)
//var topPanel = panel.clear().add_Panel();
var topPanel = O2Gui.open<Panel>("Util - AspNet ControlEncodings (Raw Format)",700,400);
var mappedData = "AspNetControlEncodings_Raw.xml".local().load<AspNetControlEncodings_Raw>();
var tableList = topPanel.add_TableList()
.show(mappedData);
tableList.add_Column("vuln");
tableList.visible(false);
foreach(var row in tableList.rows())
{
var values = row.values();
if (values[2] == "na")
row.textColor(Color.Black);
else if (values[3].toBool() && values[4].toBool())
row.textColor(Color.DarkGreen);
else if (values[3].toBool() || values[4].toBool())
row.textColor(Color.DarkOrange);
else
{
row.textColor(Color.Red);
row.SubItems[1].Text = "* " + values[1];
}
}
tableList.visible(true);
return "ok";
//O2File:AspNetControlEncodings.cs
O2 Script with Web Encoder and Decoder (with AntiXss Support)
A couple days ago I needed to do a number of Encodings/Decodings in sequence (Encoded Text -> UrlDecode -> UrlDecode-> HtmlDecode), and since there was no easy way to do that automatically with other tools, I wrote the “Util – Web Encoder (with AntiXss Support).h2″ script which looks like this:

Here is the method that runs the transformation (and show what is currently supported)
Func<string,string, string> applyTransformation =
(type, text)=>{
if (type.valid() && text.valid() )
{
switch(type)
{
case "none": break;
case "HtmlDecode": return text.htmlDecode();
case "HtmlEncode": return text.htmlEncode();
case "UrlDecode": return text.urlDecode();
case "UrlEncode": return text.urlEncode();
case "AntiXss.HtmlEncode": return Encoder.HtmlEncode(text);
case "AntiXss.UrlEncode": return Encoder.UrlEncode(text);
case "AntiXss.JavaScriptEncode": return Encoder.JavaScriptEncode(text);
case "AntiXss.CssEncode": return Encoder.CssEncode(text);
case "AntiXss.HtmlAttributeEncode": return Encoder.HtmlAttributeEncode(text);
case "AntiXss.HtmlFormUrlEncode": return Encoder.HtmlFormUrlEncode(text);
case "AntiXss.XmlAttributeEncode": return Encoder.XmlAttributeEncode(text);
case "AntiXss.XmlEncode": return Encoder.XmlEncode(text);
case "AntiXss.VisualBasicScriptEncode": return Encoder.VisualBasicScriptEncode(text);
case "AntiXss.LdapDistinguishedNameEncode": return Encoder.LdapDistinguishedNameEncode(text);
case "AntiXss.LdapFilterEncode": return Encoder.LdapFilterEncode(text);
case "Sanitizer.GetSafeHtml": return Sanitizer.GetSafeHtml(text);
case "Sanitizer.GetSafeHtmlFragment": return Sanitizer.GetSafeHtmlFragment(text);
default:
return text + " not supported: {0}".format(type);
}
}
return text;
};
This uses the latest version of the AntiXSS library, including the new HtmlSanitizationLibrary.dll which has the GetSafeHtml* methods and looks really powerful.
Here is the entire code of this script:
var topPanel = O2Gui.open<Panel>("Util - Web Encoder (with AntiXss Support)",1000,400);
//var topPanel = panel.clear().add_Panel();
var configPanel = topPanel.insert_Above(40,"Config");
var sourceText = topPanel.add_GroupBox("Source Text").add_SourceCodeViewer();
var transformedText = topPanel.insert_Right("Transformed Text").add_SourceCodeViewer();
var transformationsAvailable = new List<string> { "none",
"HtmlDecode", "HtmlEncode","UrlDecode", "UrlEncode",
"AntiXss.HtmlEncode", "AntiXss.UrlEncode", "AntiXss.JavaScriptEncode", "AntiXss.CssEncode",
"AntiXss.HtmlAttributeEncode", "AntiXss.HtmlFormUrlEncode", "AntiXss.XmlAttributeEncode", "AntiXss.XmlEncode",
"AntiXss.VisualBasicScriptEncode","AntiXss.LdapDistinguishedNameEncode", "AntiXss.LdapFilterEncode",
"Sanitizer.GetSafeHtml", "Sanitizer.GetSafeHtmlFragment"};
var transformMode_1 = "";
var transformMode_2 = "";
var transformMode_3 = "";
Func<string,string, string> applyTransformation =
(type, text)=>{
if (type.valid() && text.valid() )
{
switch(type)
{
case "none": break;
case "HtmlDecode": return text.htmlDecode();
case "HtmlEncode": return text.htmlEncode();
case "UrlDecode": return text.urlDecode();
case "UrlEncode": return text.urlEncode();
case "AntiXss.HtmlEncode": return Encoder.HtmlEncode(text);
case "AntiXss.UrlEncode": return Encoder.UrlEncode(text);
case "AntiXss.JavaScriptEncode": return Encoder.JavaScriptEncode(text);
case "AntiXss.CssEncode": return Encoder.CssEncode(text);
case "AntiXss.HtmlAttributeEncode": return Encoder.HtmlAttributeEncode(text);
case "AntiXss.HtmlFormUrlEncode": return Encoder.HtmlFormUrlEncode(text);
case "AntiXss.XmlAttributeEncode": return Encoder.XmlAttributeEncode(text);
case "AntiXss.XmlEncode": return Encoder.XmlEncode(text);
case "AntiXss.VisualBasicScriptEncode": return Encoder.VisualBasicScriptEncode(text);
case "AntiXss.LdapDistinguishedNameEncode": return Encoder.LdapDistinguishedNameEncode(text);
case "AntiXss.LdapFilterEncode": return Encoder.LdapFilterEncode(text);
case "Sanitizer.GetSafeHtml": return Sanitizer.GetSafeHtml(text);
case "Sanitizer.GetSafeHtmlFragment": return Sanitizer.GetSafeHtmlFragment(text);
default:
return text + " not supported: {0}".format(type);
}
}
return text;
};
Action applyTransformations =
()=>{
var originalText = sourceText.get_Text();
var result = applyTransformation(transformMode_1,originalText);
result = applyTransformation(transformMode_2, result);
result = applyTransformation(transformMode_3, result);
transformedText.set_Text(result);
};
sourceText.onTextChange(
(text)=>{
applyTransformations();
});
configPanel.add_Label("Color Code the text as").top(3)
.append_Control<ComboBox>().dropDownList().top(0)
.add_Items(".xml",".html",",cs")
.onSelection((value)=> {
transformedText.editor().setDocumentHighlightingStrategy(value.str());
sourceText.editor().setDocumentHighlightingStrategy(value.str());
})
.selectFirst()
.append_Label("Transform using:").top(3).autoSize()
.append_Control<ComboBox>().dropDownList().top(0).width(170).comboBoxHeight(250)
.add_Items(transformationsAvailable)
.onSelection<string>((value)=> { transformMode_1 = value; applyTransformations(); } )
.selectFirst()
.append_Label("and:").top(3).autoSize()
.append_Control<ComboBox>().dropDownList().top(0).width(170).comboBoxHeight(250)
.add_Items(transformationsAvailable)
.onSelection<string>((value)=> { transformMode_2 = value; applyTransformations(); } )
.append_Label("and:").top(3).autoSize()
.append_Control<ComboBox>().dropDownList().top(0).width(170).comboBoxHeight(250)
.add_Items(transformationsAvailable)
.onSelection<string>((value)=> { transformMode_3 = value; applyTransformations(); } )
;
sourceText.set_Text("this is a <b> test </b>");
return "done";
//using Microsoft.Security.Application
//O2Ref:AntiXSSLibrary.dll
//O2Ref:HtmlSanitizationLibrary.dll
Fixing one of JPetStore’s AutoBinding Vulnerabilities (changing the purchase price)
JPetStore (test application included with the Spring Framework) has a couple Autobinding vulnerabilities. The objective of this post is to present a solution for fixing them that meets the following criteria:
- minumal code changes on the controllers
- no code changes on the views
- dramatically reduction of the size of the binded object (the ModelAttribute, CommandClass),
- matches the expected web-layer inputs (i.e. the attack surface)
- supports UnitTesting
- doesn’t rely on Spring Framework’s recommended solution (which is based on the SetAllowedFields and SetDisallowedFields)
Here are a number of posts that give the brackground to this problem and document the O2 Script I’m using to test the fix:
- “Two Security Vulnerabilities in the Spring Framework’s MVC” pdf (from 2008)
- O2 Script with BlackBox exploits for Spring MVC AutoBinding vulnerabilities in JPetStore
- Packaged Spring MVC Security Test Apps: JPetStore and PetClinc
- Current O2 support for analyzing Spring MVC
- JPetStore releated O2 blog posts: http://o2platform.wordpress.com/category/java/spring-mvc/jpetstore/
The vulnerability we are going to fix is the one that allows (amongst other things) the modification of the TotalPrice value of our shopping cart (which is not a good thing
)
This vulnerability is graphically described in this video:
… and it is fundamentally created by the fact that the setTotalPrice(…) method is exposed in the Order class which is exposed in the OrderForm class (which is used as the command class for the OrderFormController).
So here is the fix.
1) The first step is to create a class that can be used on as the CommandClass. Trying to give it a name that made sense for what is was going to do, I called it OrderData and place it in a package called …jpetstore.domain.fromWeb (vs the original OrderForm class in the package … jpetstore.domain)
package org.springframework.samples.jpetstore.domain.fromWeb;
import org.springframework.samples.jpetstore.domain.Order;
import org.springframework.samples.jpetstore.web.spring.OrderForm;
public class OrderData {
private OrderForm orderForm;
public OrderData(OrderForm orderForm)
{
this.orderForm = orderForm;
}
public static OrderForm getOrderForm(Object command)
{
OrderData orderData = (OrderData)command;
return orderData.orderForm;
}
//bindable values
public OrderData getOrder()
{
return this;
}
....
}
This expects to receive an OrderForm object in its contructor which it will store in a private field.
The static getOrderForm() method is there to help the casting of the object given to us by the Spring Framework
2) On the FormBackingObject method return an OrderData object instead of an OrderForm object (which is vulnerable to the AutoBinding injection) and put on the Http Request attributes the original orderForm object (this will allow the views to have access to the original orderForm , which they expec,t, and prevent us from needing to make code changes to the view (the fact that views should have access to massive domain objects is a topic for another time))
protected Object formBackingObject(HttpServletRequest request) throws ModelAndViewDefiningException {
UserSession userSession = (UserSession) request.getSession().getAttribute("userSession");
Cart cart = (Cart) request.getSession().getAttribute("sessionCart");
if (cart != null) {
// Re-read account from DB at team's request.
Account account = this.petStore.getAccount(userSession.getAccount().getUsername());
OrderForm orderForm = new OrderForm();
orderForm.getOrder().initOrder(account, cart);
request.setAttribute("orderForm", orderForm); // AutoBinding fix
return new OrderData(orderForm); // AutoBinding fix
}
else {
ModelAndView modelAndView = new ModelAndView("Error");
modelAndView.addObject("message", "An order could not be created because a cart could not be found.");
throw new ModelAndViewDefiningException(modelAndView);
}
}
3) modify all locations that originally received the OrderFrom object from SpringFramework in order to handle the fact that it is now an OrderData object (this uses the static method getOrderForm in order to make the code change short and sweet)
> onBindAndValidate
protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors, int page) {
if (page == 0 && request.getParameter("shippingAddressRequired") == null) {
//OrderForm orderForm = (OrderForm) command; // AutoBinding fix
OrderForm orderForm = OrderData.getOrderForm(command); // AutoBinding fix
orderForm.setShippingAddressRequired(false);
}
}
> on getTargetPage:
protected int getTargetPage(HttpServletRequest request, Object command, Errors errors, int currentPage) {
//OrderForm orderForm = (OrderForm) command; // AutoBinding fix
OrderForm orderForm = OrderData.getOrderForm(command); // AutoBinding fix
if (currentPage == 0 && orderForm.isShippingAddressRequired()) {
return 1;
}
else {
return 2;
}
}
>processFinish
protected ModelAndView processFinish(
HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) {
//OrderForm orderForm = (OrderForm) command; // AutoBinding fix
OrderForm orderForm = OrderData.getOrderForm(command); // AutoBinding fix
this.petStore.insertOrder(orderForm.getOrder());
request.getSession().removeAttribute("sessionCart");
Map model = new HashMap();
model.put("order", orderForm.getOrder());
model.put("message", "Thank you, your order has been submitted.");
return new ModelAndView("ViewOrder", model);
}
4) finally add to the FormData object the getters and setters of the variables that we want to expose.
A good place to find out which variables to expose is to look at the values that are submited from the web pages, in this case:

Here is the full code of this class:
package org.springframework.samples.jpetstore.domain.fromWeb;
import org.springframework.samples.jpetstore.domain.Order;
import org.springframework.samples.jpetstore.web.spring.OrderForm;
public class OrderData {
private OrderForm orderForm;
public OrderData(OrderForm orderForm)
{
this.orderForm = orderForm;
}
public static OrderForm getOrderForm(Object command)
{
OrderData orderData = (OrderData)command;
return orderData.orderForm;
}
//bindable values
public void setShippingAddressRequired (boolean shippingAddressRequired)
{
this.orderForm.setShippingAddressRequired(shippingAddressRequired);
}
public OrderData getOrder()
{
return this;
}
public void setCardType (String cardType) { this.orderForm.getOrder().setCardType (cardType);}
public void setCreditCard (String creditCard) { this.orderForm.getOrder().setCreditCard (creditCard); }
public void setExpiryDate (String expiryDate) { this.orderForm.getOrder().setExpiryDate (expiryDate); }
public void setBillToFirstName (String billToFirstName) { this.orderForm.getOrder().setBillToFirstName (billToFirstName); }
public void setBillToLastName (String billToLastName) { this.orderForm.getOrder().setBillToLastName (billToLastName); }
public void setBillAddress1 (String billAddress1) { this.orderForm.getOrder().setBillAddress1 (billAddress1); }
public void setBillAddress2 (String billAddress1) { this.orderForm.getOrder().setBillAddress2 (billAddress1); }
public void setBillCity (String billCity) { this.orderForm.getOrder().setBillCity (billCity); }
public void setBillState (String billState) { this.orderForm.getOrder().setBillState (billState); }
public void setBillZip (String billZip) { this.orderForm.getOrder().setBillZip (billZip); }
public void setBillCountry (String billCountry) { this.orderForm.getOrder().setBillCountry (billCountry); }
public String getCardType () { return this.orderForm.getOrder().getCardType ();}
public String getCreditCard () { return this.orderForm.getOrder().getCreditCard ();}
public String getExpiryDate () { return this.orderForm.getOrder().getExpiryDate ();}
public String getBillToFirstName() { return this.orderForm.getOrder().getBillToFirstName();}
public String getBillToLastName () { return this.orderForm.getOrder().getBillToLastName ();}
public String getBillAddress1 () { return this.orderForm.getOrder().getBillAddress1 ();}
public String getBillAddress2 () { return this.orderForm.getOrder().getBillAddress2 ();}
public String getBillCity () { return this.orderForm.getOrder().getBillCity ();}
public String getBillState () { return this.orderForm.getOrder().getBillState ();}
public String getBillZip () { return this.orderForm.getOrder().getBillZip ();}
public String getBillCountry () { return this.orderForm.getOrder().getBillCountry ();}
}
The getOrder() method which returns the OrderData object, is a cool trick to trick the StringFramwork (which will come looking for an getOrder() method) and prevent us from creating multiple class files (which is where blind spots tend to occur). I like the fact that I can put all bindable variables in one location/file. There is a small side effect that now we can provide multiple order.* values, ie. order.order.order.order.order.billCity (I wonder if there is DoS angle here)
And that’s it (this example is a bit simpler since JPetStore doesn’t use a very complex binding wiring (for example no config files to change), but I will provide more examples later that handle the other variations.
Now, with this fix in place we can verify the fix by running the web automation script that previously allowed us to control the purchase price, and now it doesn’t
exploit variation #1

exploit variation #2

Fixing/Encoding .NET code in real time (in this case Response.Write)
Here is a example of what we should be doing on Dev’s environments in order to make security invisible/automatic to them.
This script will automatically add an Encoding function to Response.Write so that it is always encoded. In this case the patch is done in real time, but it could also be done behind the scenes or on code check in (note that if you are a developer there is no way to avoid it, since the code patch happens automatically).
The script is called “Fixing Response.Write.h2″ and here is a video of it in action:
This is how it works:
1) when the script executes you will have two code windows: on the left is the original code, on the right is the patched code (initially they both look the same)

2) make a change on the left window (look in line 21 on the left and line 18 on the right) and note that both windows code is syncronized

3) Unless you type Response.Write, nothing major happens to the code. Note how the Response.End() we just wrote was not changed (line 21 on left, line 18 on right), but on the previous line, the Response.Write was protected with a call to AntiXss.HtmlEncode (line 20 on left and line 17 on on right):

4) Now type on line 20 the Response.Write command (maybe with an XSS payload) and see how it is automatically wrapped in AntiXss.HtmlEncode (it is pretty cool to do this in real time
)

How does this work?
This was done using O2′s .NET Static Analysis engine which provices easy access to the code’s structure and exposes a number of helper methods to create refactored code:
//panel.clear();
//var topPanel = panel;
var topPanel = O2Gui.open<Panel>("Fixing Response.Write",1000,500);
var controls = topPanel.add_1x1("Original Code", "Patched Code");
var originalCode = controls[0].add_SourceCodeEditor();
var patchedCode = controls[1].add_SourceCodeEditor();</pre>
originalCode.eDocumentDataChanged+= (text)=>
{
if (text.valid())
{
var csharpAst = text.csharpAst();
//show.info(csharpAst.CompilationUnit.iNodes<InvocationExpression>());
foreach(var invocationExpression in csharpAst.CompilationUnit.iNodes<InvocationExpression>())
{
var memberReference = invocationExpression.TargetObject as MemberReferenceExpression;
if (memberReference.notNull() && memberReference.MemberName == "Write")
{
var className = "AntiXss";
var methodName = "HtmlEncode";
var newMemberReference = new MemberReferenceExpression(new IdentifierExpression(className),methodName );
var newInvocationExpression = new InvocationExpression(newMemberReference);
newInvocationExpression.Arguments.AddRange(invocationExpression.Arguments);
invocationExpression.Arguments.Clear();
invocationExpression.Arguments.Add(newInvocationExpression );
}
csharpAst.CompilationUnit.add_Using("Microsoft.Security.Application");
}
var patchedCSharpCode = csharpAst.CompilationUnit.csharpCode();
patchedCSharpCode = @"//O2Ref:AntiXSSLibrary.dll".line() + patchedCSharpCode; // so that it compiles OK
var patchedCSharpFile = patchedCSharpCode.saveWithExtension(".cs");
patchedCode.open(patchedCSharpFile);
}
};
var originalFile = @"Request.Write.cs".local();
originalCode.open(originalFile);
//using ICSharpCode.NRefactory.Parser
//using ICSharpCode.NRefactory.Ast
//using ICSharpCode.NRefactory
//using O2.API.AST.CSharp;
//using O2.API.AST.ExtensionMethods;
//using O2.API.AST.ExtensionMethods.CSharp;
//O2Ref:O2_API_AST.dll

