|
1 | 1 | import sys |
2 | | -import pytest |
| 2 | +import struct |
3 | 3 | import os.path |
4 | 4 | import time |
5 | 5 |
|
@@ -125,37 +125,79 @@ def _get_response_effective_data(self, response): |
125 | 125 | return super(DbgRpcClient, self)._get_response_effective_data(response) |
126 | 126 |
|
127 | 127 |
|
128 | | -FIREWALL_RPC_IID = "2fb92682-6599-42dc-ae13-bd2ca89bd11c" |
129 | | - |
130 | | -Proc0_RPC_FWOpenPolicyStore = 0 |
131 | | -Proc9_RPC_FWEnumFirewallRules = 9 |
| 128 | +EVTX_RPC_IID = "82273FDC-E32A-18C3-3F78-827929DC23EA" |
| 129 | + |
| 130 | +Proc7_ElfRpc_s_OpenELW = 7 |
| 131 | +Proc10_ElfRpc_s_ReadELW = 10 |
| 132 | + |
| 133 | +class NdrCustomSizedString(object): |
| 134 | + @classmethod |
| 135 | + def pack(cls, data): |
| 136 | + """Pack string ``data``. append ``\\x00`` if not present at the end of the string""" |
| 137 | + if not data: |
| 138 | + return None |
| 139 | + result = struct.pack("<3I", data[0], 0, data[1]) |
| 140 | + result += data[2] |
| 141 | + return ndr.dword_pad(result) |
| 142 | + |
| 143 | + @classmethod |
| 144 | + def get_alignment(self): |
| 145 | + # Not sure, but size is on 4 bytes so... |
| 146 | + return 4 |
| 147 | + |
| 148 | +class RPC_UNICODE_STRING(ndr.NdrStructure): |
| 149 | + MEMBERS = [ |
| 150 | + ndr.NdrShort, # Length (in bytes) |
| 151 | + ndr.NdrShort, # MaximumLength (in bytes) |
| 152 | + ndr.NdrUniquePTR(NdrCustomSizedString) # Pointer to actual wide string character buffer |
| 153 | + ] |
| 154 | + |
| 155 | +class ElfrOpenELWPayload(ndr.NdrParameters): |
| 156 | + MEMBERS = [ |
| 157 | + ndr.NdrUniquePTR(ndr.NdrWString), # UNCServerName (Pass None for local execution) |
| 158 | + RPC_UNICODE_STRING, # ModuleName (The target Log Name) |
| 159 | + RPC_UNICODE_STRING, # RegModuleName (Must be an empty string) |
| 160 | + ndr.NdrLong, # MajorVersion |
| 161 | + ndr.NdrLong # MinorVersion |
| 162 | + ] |
| 163 | + |
| 164 | +class ElfrReadELWPayload(ndr.NdrParameters): |
| 165 | + MEMBERS = [ |
| 166 | + ndr.NdrContextHandle, |
| 167 | + ndr.NdrLong, # Flags |
| 168 | + ndr.NdrLong, # RecordOffset |
| 169 | + ndr.NdrLong, # NumberOfBytesToRead |
| 170 | + ] |
132 | 171 |
|
133 | 172 | def test_rpc_response_as_view(): |
134 | 173 | """Check that parsing response as view in RPC Client works. Testing after a bug in 32b RPCCLient""" |
135 | 174 | # We test what by using a RPC endpoint that returns a lot of info : forcing a response in a view |
136 | | - # In this case we use the Firewall RPC and we list all Firerules. |
| 175 | + # In this case we use the EVTX RPC & read method that allow us to ask for a sized return buffer |
137 | 176 | # We use a custom RPCClient subclasse to track if last response was a view |
138 | | - client = windows.rpc.find_alpc_endpoint_and_connect(FIREWALL_RPC_IID, sid=gdef.WinLocalSid) |
| 177 | + client = windows.rpc.RPCClient(r"\RPC Control\eventlog") |
139 | 178 | client.__class__ = DbgRpcClient |
140 | | - iid = client.bind(FIREWALL_RPC_IID) |
141 | | - |
142 | | - # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fasp/230d1ae7-b42e-4d9c-b997-b1463aaa0ded |
143 | | - # !\x02\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00 |
144 | | - # Binaryversion : 0x022f |
145 | | - # FW_STORE_TYPE_LOCAL |
146 | | - # FW_POLICY_ACCESS_RIGHT_READ |
147 | | - # Flags = 0 |
148 | | - resp1 = client.call(iid, Proc0_RPC_FWOpenPolicyStore, params=b"!\x02\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00") |
149 | | - rawpolstore = resp1[:20] |
| 179 | + iid = client.bind(EVTX_RPC_IID, (0, 0)) |
| 180 | + |
| 181 | + # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-even/1898e85b-10a1-43b9-a27e-92fc833cebea |
| 182 | + |
| 183 | + |
| 184 | + target_evtx = "Application" |
| 185 | + tsize = len(target_evtx) |
| 186 | + params = ElfrOpenELWPayload.pack([ |
| 187 | + None, # UNCServerName (None resolves to a NULL pointer / Local) |
| 188 | + (tsize * 2, (tsize + 1) * 2, (tsize + 1, tsize, target_evtx.encode("utf-16-le") + b"\x00")), # ModuleName |
| 189 | + (0, 0, ""), # RegModuleName |
| 190 | + 1, # MajorVersion |
| 191 | + 1 # MinorVersion |
| 192 | + ]) |
| 193 | + |
| 194 | + resp1 = client.call(iid, Proc7_ElfRpc_s_OpenELW, params=params) |
| 195 | + stream = ndr.NdrStream(resp1) |
| 196 | + handle = ndr.NdrContextHandle.unpack(stream) |
150 | 197 | assert not client.last_response_was_view |
151 | 198 |
|
152 | | - # Proc9_RPC_FWEnumFirewallRules |
153 | | - # \x00\x00\x03\x00\xff\xff\xff\x7f\x07\x00 |
154 | | - # https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fasp/36cddff4-c427-4863-a58d-3d913a12b221 |
155 | | - # FW_PROFILE_TYPE_ALL : 0x7FFFFFFF |
156 | | - # FW_RULE_STATUS_CLASS_OK + FW_RULE_STATUS_PARTIALLY_IGNORED = 0x00010000 + 0x00020000 |
157 | | - # Flags = 7 ? |
158 | | - resp2 = client.call(iid, Proc9_RPC_FWEnumFirewallRules, params=rawpolstore + b"\x00\x00\x03\x00\xff\xff\xff\x7f\x07\x00") |
| 199 | + payload = ElfrReadELWPayload.pack([handle, 1 + 8, 0, 5000]) # Ask for a buffer of 5000 bytes -> response is view |
| 200 | + res = client.call(iid, Proc10_ElfRpc_s_ReadELW, payload) |
159 | 201 | assert client.last_response_was_view |
160 | 202 |
|
161 | 203 | # This cannot work as is: as juste calling NtAlpcDeleteSectionView does not works. |
|
0 commit comments