flawopen.com/ssrf/Csharp

● CWE-918 · 高
セキュリティ研究 · FlawOpen

脆弱性の解説:Server-Side Request Forgery in C#

How HttpClient allows SSRF in ASP.NET Core, and how to enforce IP boundary assertions in SocketsHttpHandler.

💡 わかりやすい解説 (ELI5)

オフィスの宅配便受取係に小包を取りに行くよう依頼する際、外部の店舗ではなく役員室の金庫の部屋番号を指定する場面を想像してください。受取係は社内通行バッジを持っているため、疑われることなく金庫室に入り機密書類を持ち出してしまいます。

主要な概念と専門用語

Web Application Security
主要概念 (Web Application Security):Core architecture component affected by CWE-918.
CWE-918
主要概念 (CWE-918):Standard Common Weakness Enumeration classification for ssrf-csharp.
Defense-in-Depth
主要概念 (Defense-in-Depth):Multi-layered engineering verification and runtime boundary isolation.

ステップ・バイ・ステップの攻撃フロー

Step 1

外部URLパラメータの受信

アプリケーションがユーザーから指定されたURLを受け取り、画像の取得やWebhook配信を実行します。

Step 2

内部ネットワークアドレスの指定

攻撃者がクラウドメタデータサービス(例: http://169.254.169.254/latest/meta-data/)や内部ループバックURLを指定します。

Step 3

保護領域からの制限なきソケット送信

サーバーのHTTPクライアントがプライベートVPC内からリクエストを発行し、内部IPチェックを行いません。

Step 4

クラウド認証情報およびトークンの窃取

メタデータサービスがリクエストを信頼し、一時的なIAM認証情報や内部APIデータを攻撃者に返却します。

ソースコード比較:脆弱 vs 堅牢化

✕ 脆弱な実装
// VULNERABLE: Naive HttpClient usage with user-supplied URL
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase {
    private readonly HttpClient _client = new HttpClient();

    [HttpGet("test")]
    public async Task<IActionResult> TestWebhook([FromQuery] string url) {
        // Attacker input: http://169.254.169.254/metadata/v1/
        var response = await _client.GetAsync(url);
        var content = await response.Content.ReadAsStringAsync();
        return Ok(content);
    }
}
✓ 堅牢化されたセキュアパッチ
// HARDENED: SocketsHttpHandler with ConnectCallback IP validation
using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase {
    private static readonly HttpClient SafeClient;

    static WebhookController() {
        var handler = new SocketsHttpHandler {
            AllowAutoRedirect = false, // Block redirect bypasses
            ConnectTimeout = TimeSpan.FromSeconds(3),
            ConnectCallback = async (context, cancellationToken) => {
                var entry = await Dns.GetHostEntryAsync(context.DnsEndPoint.Host, cancellationToken);
                foreach (var ip in entry.AddressList) {
                    if (IPAddress.IsLoopback(ip) || isPrivateOrLinkLocal(ip)) {
                        throw new SocketException((int)SocketError.AccessDenied);
                    }
                }
                var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                await socket.ConnectAsync(entry.AddressList[0], context.DnsEndPoint.Port, cancellationToken);
                return new NetworkStream(socket, ownsSocket: true);
            }
        };
        SafeClient = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(5) };
    }

    private static bool isPrivateOrLinkLocal(IPAddress ip) {
        if (ip.IsIPv4MappedToIPv6) ip = ip.MapToIPv4();
        byte[] bytes = ip.GetAddressBytes();
        if (bytes[0] == 10) return true; // 10.0.0.0/8
        if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) return true; // 172.16.0.0/12
        if (bytes[0] == 192 && bytes[1] == 168) return true; // 192.168.0.0/16
        if (bytes[0] == 169 && bytes[1] == 254) return true; // 169.254.0.0/16 Link-Local
        return false;
    }

    [HttpGet("test")]
    public async Task<IActionResult> TestWebhook([FromQuery] string url) {
        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || (uri.Scheme != "http" && uri.Scheme != "https")) {
            return BadRequest("Invalid URL scheme");
        }

        try {
            var response = await SafeClient.GetAsync(uri);
            var content = await response.Content.ReadAsStringAsync();
            return Ok(content);
        } catch (Exception) {
            return Forbid("Access to private/internal network rejected");
        }
    }
}

エンジニアリング&システム堅牢化チェックリスト

References