flawopen.com/path-traversal/C-cpp

● CWE-918 · उच्च
सुरक्षा अनुसंधान · FlawOpen

सुरक्षा भेद्यता: Path Traversal in C/C++

How snprintf file path construction allows directory escapes in C/C++, and how to enforce containment using realpath() and strncmp.

💡 आसान भाषा में (ELI5)

एक होटल के कमरे के कार्ड रीडर की कल्पना करें जिसे केवल दूसरी मंजिल के कमरे खोलने चाहिए। यदि कोई मेहमान कीपैड पर '../../master-safe' टाइप करता है, तो असुरक्षित ताला हॉलवे से बाहर निकलकर प्रबंधक की तिजोरी खोल देता है।

इस पेज के मुख्य शब्द

Web Application Security
सुरक्षा अवधारणा (Web Application Security): Core architecture component affected by CWE-918.
CWE-918
सुरक्षा अवधारणा (CWE-918): Standard Common Weakness Enumeration classification for path-traversal-c-cpp.
Defense-in-Depth
सुरक्षा अवधारणा (Defense-in-Depth): Multi-layered engineering verification and runtime boundary isolation.

हमले का चरण-दर-चरण प्रवाह

Step 1

क्लाइंट फ़ाइल पाथ इनपुट

एंडपॉइंट HTTP पैरामीटर के माध्यम से उपयोगकर्ता द्वारा प्रदान किया गया फ़ाइल नाम स्वीकार करता है।

Step 2

डायरेक्टरी ट्रैवर्सल सीक्वेंस इंजेक्शन

हमलावर फ़ाइल नाम में '../' या '..%2f' जैसे रिलेटिव डायरेक्टरी ट्रैवर्सल सीक्वेंस डालता है।

Step 3

फ़ाइल सिस्टम सीमा बाईपास

बैकएंड कैनोनिकल पाथ को सत्यापित किए बिना बेस डायरेक्टरी के साथ पाथ को जोड़ देता है।

Step 4

गोपनीय फ़ाइलों का प्रकटीकरण

सर्वर सिस्टम की संवेदनशील फ़ाइलें (/etc/passwd, पासवर्ड या कुंजियाँ) पढ़कर हमलावर को भेज देता है।

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

✕ कमज़ोर कार्यान्वयन
// VULNERABLE: Direct snprintf and open without realpath check
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int serve_user_file(const char *user_filename) {
    char full_path[1024];
    const char *base_dir = "/var/app/public/files";

    // Attacker input: "../../../../etc/shadow"
    snprintf(full_path, sizeof(full_path), "%s/%s", base_dir, user_filename);

    // Directly opens system shadow file!
    int fd = open(full_path, O_RDONLY);
    return fd;
}
✓ सुरक्षित और सुदृढ़ फ़िक्स
// HARDENED: Canonicalize with realpath and verify directory prefix bounds
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>

int serve_user_file(const char *user_filename) {
    const char *base_dir = "/var/app/public/files";
    char base_canonical[PATH_MAX];
    char raw_path[PATH_MAX];
    char target_canonical[PATH_MAX];

    // 1. Resolve canonical base directory
    if (realpath(base_dir, base_canonical) == NULL) {
        return -1;
    }

    // 2. Safely construct raw path
    if (snprintf(raw_path, sizeof(raw_path), "%s/%s", base_canonical, user_filename) >= (int)sizeof(raw_path)) {
        return -1; // Path truncated
    }

    // 3. Resolve canonical target path
    if (realpath(raw_path, target_canonical) == NULL) {
        return -1; // File does not exist or access error
    }

    // 4. Verify canonical target begins with base_canonical + '/'
    size_t base_len = strlen(base_canonical);
    if (strncmp(target_canonical, base_canonical, base_len) != 0 || 
        target_canonical[base_len] != '/') {
        return -1; // Traversal detected!
    }

    return open(target_canonical, O_RDONLY | O_CLOEXEC);
}

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

References