├── Lantern
├── Models
│ ├── DeviceCodeResp.cs
│ ├── ErrorResponse.cs
│ ├── DeviceEnrollmentResp.cs
│ ├── DeviceEnrollmentReq.cs
│ └── OpenIDConfigurationResp.cs
├── Lantern.csproj
├── Utils.cs
├── AzResourceEnum.cs
├── AzClientIDEnum.cs
├── MEManager.cs
├── Options.cs
├── Tokenator.cs
├── Helper.cs
└── Program.cs
├── LICENSE
├── Lantern.sln
├── README.md
└── .gitignore
/Lantern/Models/DeviceCodeResp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern.Models
6 | {
7 | public class DeviceCodeResp
8 | {
9 | public string user_code { get; set; }
10 | public string device_code { get; set; }
11 | public string verification_url { get; set; }
12 | public int expires_in { get; set; }
13 | public int interval { get; set; }
14 | public string message { get; set; }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/Lantern/Models/ErrorResponse.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern.Models
6 | { public class ErrorResponse
7 | {
8 | public string error { get; set; }
9 | public string error_description { get; set; }
10 | public int[] error_codes { get; set; }
11 | public string timestamp { get; set; }
12 | public string trace_id { get; set; }
13 | public string correlation_id { get; set; }
14 | public string error_uri { get; set; }
15 | }
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Lantern/Models/DeviceEnrollmentResp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern.Models
6 | {
7 | public class DeviceEnrollmentResp
8 | {
9 | public Certificate Certificate { get; set; }
10 | public User User { get; set; }
11 | public Membershipchange[] MembershipChanges { get; set; }
12 | }
13 |
14 | public class Certificate
15 | {
16 | public string Thumbprint { get; set; }
17 | public string RawBody { get; set; }
18 | }
19 |
20 | public class User
21 | {
22 | public string Upn { get; set; }
23 | }
24 |
25 | public class Membershipchange
26 | {
27 | public string LocalSID { get; set; }
28 | public string[] AddSIDs { get; set; }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Lantern/Lantern.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 | 0.0.1
7 | @caspar___
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Lantern/Utils.cs:
--------------------------------------------------------------------------------
1 | using Lantern.Models;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 |
8 | namespace Lantern
9 | {
10 | class Utils
11 | {
12 |
13 | public static String GetTenantIdToDomain(string Domain, String Proxy)
14 | {
15 | String result = null;
16 | result = Helper.GetOpenIDConfiguration(Domain, Proxy);
17 | if (result == null)
18 | {
19 | return null;
20 | }
21 | var resultParsed = JsonConvert.DeserializeObject(result);
22 | if (resultParsed.authorization_endpoint != null) {
23 | result = resultParsed.authorization_endpoint.Split("/")[3];
24 | }else{
25 | return null;
26 | }
27 | return result;
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Lantern/Models/DeviceEnrollmentReq.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern.Models
6 | {
7 |
8 | public class DeviceEnrollmentReq
9 | {
10 | public string TransportKey { get; set; }
11 | public int JoinType { get; set; }
12 | public string DeviceDisplayName { get; set; }
13 | public string OSVersion { get; set; }
14 | public Certificaterequest CertificateRequest { get; set; }
15 | public string TargetDomain { get; set; }
16 | public string DeviceType { get; set; }
17 | public Attributes Attributes { get; set; }
18 | }
19 |
20 | public class Certificaterequest
21 | {
22 | public string Type { get; set; }
23 | public string Data { get; set; }
24 | }
25 |
26 | public class Attributes
27 | {
28 | public string ReuseDevice { get; set; }
29 | public string ReturnClientSid { get; set; }
30 | public string SharedDevice { get; set; }
31 | }
32 | }
--------------------------------------------------------------------------------
/Lantern/AzResourceEnum.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern
6 | {
7 | public static class AzResourceEnum
8 | {
9 | public const string GraphAPI = "https://graph.windows.net";
10 | public const string AzureMDM = "https://enrollment.manage.microsoft.com";
11 | public const string Outlook = "https://outlook.office365.com";
12 | public const string Substrate = "https://substrate.office.com";
13 | public const string Teams = "https://api.spaces.skype.com";
14 | public const string MSGraph = "https://graph.microsoft.com";
15 | public const string WebShell = "https://webshell.suite.office.com";
16 | public const string Core = "https://management.core.windows.net";
17 | public const string OfficeApps = "https://officeapps.live.com";
18 | public const string Intune = "https://intunemam.microsoftonline.com";
19 | public const string WindowsClient = "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Constantin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Lantern/AzClientIDEnum.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern
6 | {
7 | class AzClientIDEnum
8 | {
9 | public const string DeviceMgmt = "01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9";
10 | public const string GraphAPI = "1b730954-1685-4b74-9bfd-dac224a7b894";
11 | public const string AzureMDM = "29d9ed98-a469-4536-ade2-f981bc1d605e";
12 | public const string Outlook = "d3590ed6-52b3-4102-aeff-aad2292ab01c";
13 | public const string Substrate = "d3590ed6-52b3-4102-aeff-aad2292ab01c";
14 | public const string Teams = "d3590ed6-52b3-4102-aeff-aad2292ab01c";
15 | public const string MSGraph = "d3590ed6-52b3-4102-aeff-aad2292ab01c";
16 | public const string WebShell = "89bee1f7-5e6e-4d8a-9f3d-ecd601259da7";
17 | public const string Core = "d3590ed6-52b3-4102-aeff-aad2292ab01c";
18 | public const string WindowsClient = "1b730954-1685-4b74-9bfd-dac224a7b894";
19 | public const string OfficeApps = "ab9b8c07-8f02-4f72-87fa-80105867a763";
20 | public const string Intune = "6c7e8096-f593-4d72-807f-a5f86dcc9c77";
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Lantern.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30717.126
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lantern", "Lantern\Lantern.csproj", "{9B865CD9-4593-4713-8205-A50042F47CCF}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {9B865CD9-4593-4713-8205-A50042F47CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {9B865CD9-4593-4713-8205-A50042F47CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {9B865CD9-4593-4713-8205-A50042F47CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {9B865CD9-4593-4713-8205-A50042F47CCF}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {D87ACE8C-23F0-4D8C-87D3-4B627E3D2D3C}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Lantern/Models/OpenIDConfigurationResp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Lantern.Models
6 | {
7 | public class OpenIDConfigurationResp
8 | {
9 | public string token_endpoint { get; set; }
10 | public string[] token_endpoint_auth_methods_supported { get; set; }
11 | public string jwks_uri { get; set; }
12 | public string[] response_modes_supported { get; set; }
13 | public string[] subject_types_supported { get; set; }
14 | public string[] id_token_signing_alg_values_supported { get; set; }
15 | public string[] response_types_supported { get; set; }
16 | public string[] scopes_supported { get; set; }
17 | public string issuer { get; set; }
18 | public bool microsoft_multi_refresh_token { get; set; }
19 | public string authorization_endpoint { get; set; }
20 | public string device_authorization_endpoint { get; set; }
21 | public bool http_logout_supported { get; set; }
22 | public bool frontchannel_logout_supported { get; set; }
23 | public string end_session_endpoint { get; set; }
24 | public string[] claims_supported { get; set; }
25 | public string check_session_iframe { get; set; }
26 | public string userinfo_endpoint { get; set; }
27 | public string kerberos_endpoint { get; set; }
28 | public string tenant_region_scope { get; set; }
29 | public string cloud_instance_name { get; set; }
30 | public string cloud_graph_host_name { get; set; }
31 | public string msgraph_host { get; set; }
32 | public string rbac_url { get; set; }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/Lantern/MEManager.cs:
--------------------------------------------------------------------------------
1 | using Lantern.Models;
2 | using Newtonsoft.Json;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Net.Http;
6 | using System.Net.Http.Headers;
7 | using System.Text;
8 |
9 | namespace Lantern
10 | {
11 | class MEManager
12 | {
13 | public static DeviceEnrollmentResp addNewDeviceToAzure(string proxy, string accesstoken, string certificaterequest, string transportKey, string targetDomain, string deviceDisplayName, bool registerDevice)
14 | {
15 | using (var client = Helper.getDefaultClient(proxy, false, "https://enterpriseregistration.windows.net"))
16 | using (var message = new HttpRequestMessage(HttpMethod.Post, "/EnrollmentServer/device/?api-version=1.0"))
17 | {
18 | //message.Headers.Add("Authorization", "Bearer " + accesstoken);
19 | message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
20 | message.Headers.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
21 |
22 | int jointype = 0;
23 | if (registerDevice)
24 | {
25 | jointype = 4;
26 | }
27 |
28 | var body = new DeviceEnrollmentReq
29 | {
30 | TransportKey = transportKey,
31 | JoinType = jointype,
32 | DeviceDisplayName = deviceDisplayName,
33 | OSVersion = "10.0.19041.804",
34 | CertificateRequest = new Certificaterequest
35 | {
36 | Data = certificaterequest,
37 | Type = "pkcs10"
38 | },
39 | TargetDomain = targetDomain,
40 | DeviceType = "Windows",
41 | Attributes = new Attributes
42 | {
43 | ReturnClientSid = "true",
44 | ReuseDevice = "true",
45 | SharedDevice = "false"
46 | }
47 | };
48 |
49 | var content = new StringContent(JsonConvert.SerializeObject(body, Formatting.Indented));
50 | message.Content = content;
51 | var response = client.SendAsync(message).Result;
52 | if (response.IsSuccessStatusCode)
53 | {
54 | var result = response.Content.ReadAsStringAsync().Result;
55 | var devEnrollmentResp = JsonConvert.DeserializeObject(result);
56 | return devEnrollmentResp;
57 | }
58 | }
59 | return null;
60 | }
61 |
62 | public static int MarkDeviceAsCompliant(string ObjectID, String accesstoken, String Proxy)
63 | {
64 | String tenantid = Helper.GetTenantFromAccessToken(accesstoken);
65 | String uri = "/" + tenantid + "/devices/" + ObjectID + "?api-version=1.61-internal";
66 | return Helper.PatchRequest(uri, accesstoken, Proxy);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lantern
2 |
3 | This repo is not maintained anymore. Moved to [SharpAzToken](https://github.com/HackmichNet/SharpAzToken).
4 |
5 | Lantern is a small tool I created to learn about Azure authentication, tokens and C#. Maybe It helps you to learn, too. The code for authentication, is mainly adapted from [auth.py](https://github.com/dirkjanm/ROADtools/blob/master/roadlib/roadtools/roadlib/auth.py) of [roadtools](https://github.com/dirkjanm/ROADtools) from [Dirk-Jan](https://twitter.com/_dirkjan) and ported to c#. All credits for the authentication part goes to him.
6 |
7 | How Azure PRT works is mainly described in these two articles:
8 |
9 | * [https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/](https://dirkjanm.io/abusing-azure-ad-sso-with-the-primary-refresh-token/)
10 | * [https://dirkjanm.io/digging-further-into-the-primary-refresh-token/](https://dirkjanm.io/digging-further-into-the-primary-refresh-token/)
11 |
12 | Additionally, I started to implement Azure Device Join and to learn about that. Here I copied and adapted the code mainly from [AADInternals](https://github.com/Gerenios/AADInternals). Here all credits goes to [Dr. Nestori Syynimaa](https://twitter.com/DrAzureAD). If you want to learn more about device join I can recommend reading [this](https://o365blog.com/) blog.
13 |
14 | At the moment you can request some tokens in various ways and join a device to Azure. Additionally you can use this device the get PRT and a session key. More is coming.
15 |
16 | **Note:** This tools is for learning and it is in pre-, pre-, pre- (what comes before alpha?) status.
17 |
18 | ## Compiling
19 |
20 | You can build it with VisualStudio 2019 and .NetCore. Simple open the project and compile it. I tested it for Windows and Linux.
21 |
22 | ## Usage
23 |
24 | ### Proxy
25 |
26 | You can always see whats going on if you add a proxy. For example like:
27 |
28 | ```
29 | --proxy http://127.0.0.1:8080
30 | ```
31 |
32 | Tipp: Disable HTTP2 support on your proxy. The library I use does not support HTTP2 and I had problems with burp, if I didn't disable HTTP2.
33 |
34 | ### Help
35 |
36 | ```
37 | .\Lantern.exe --help
38 |
39 |
40 | .____ __
41 | | | _____ _____/ |_ ___________ ____
42 | | | \__ \ / \ __\/ __ \_ __ \/ \
43 | | |___ / __ \| | \ | \ ___/| | \/ | \
44 | |_______ (____ /___| /__| \___ >__| |___| /
45 | \/ \/ \/ \/ \/
46 |
47 | Lantern 0.0.1-alpha
48 |
49 | p2pcert Ask for a P2P Certificate.
50 | nonce Request a nonce from Azure.
51 | cookie Create a PRT Cookie for further usage or your browser.
52 | token Play with Azure Tokens.
53 | mdm Do things with Intune like joining a device
54 | devicekeys Play with Device Keys - Ask for PRT and SessionKey for a
55 | certificate.
56 | utils Some arbitrary usefull functions.
57 | help Display more information on a specific command.
58 | version Display version information.
59 |
60 |
61 | ```
62 |
63 | ### Nonce
64 |
65 | Request a nonce you can use the following command:
66 |
67 | ```cmd
68 | Lantern.exe nonce
69 | ```
70 |
71 | ### PRT-Cookie
72 |
73 | Create a PRT-Cookie for the browser you can use:
74 |
75 | ```cmd
76 | Lantern.exe cookie --derivedkey --context --prt
77 | ```
78 |
79 | ```cmd
80 | Lantern.exe cookie --sessionkey --prt
81 | ```
82 |
83 | ### Token
84 |
85 | Create tokens in various combination and play with them:
86 |
87 | ```cmd
88 | Lantern.exe token --derivedkey --context --prt
89 | ```
90 |
91 | ```cmd
92 | Lantern.exe token --prtcookie
93 | ```
94 |
95 | ```cmd
96 | Lantern.exe token --username --password
97 | ```
98 |
99 | ```cmd
100 | Lantern.exe token --refreshtoken
101 | ```
102 |
103 | ```cmd
104 | Lantern.exe token --refreshtoken --clientname Office
105 | ```
106 |
107 | ### Join a device or mark a device as compliant
108 |
109 | Join a device:
110 |
111 | ```cmd
112 | Lantern.exe mdm --joindevice --accesstoken (or some combination from the token part) --devicename --outpfxfile
113 | ```
114 |
115 | ### Device Keys
116 |
117 | Generate PRT and Session Key
118 |
119 | ```cmd
120 |
121 | Lanter.exe devicekeys --pfxpath XXXX.pfx --refreshtoken (--prtcookie / ---username + --password )
122 |
123 | ```
124 |
--------------------------------------------------------------------------------
/Lantern/Options.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 |
3 |
4 | namespace Lantern
5 | {
6 |
7 | [Verb("default", HelpText = "Default options, not visible")]
8 | class DefaultOptions
9 | {
10 | [Option(HelpText = "Set Proxy")]
11 | public string Proxy { get; set; }
12 | }
13 |
14 | [Verb("devicekeys", HelpText = "Play with Device Keys - Ask for PRT and SessionKey for a certificate.")]
15 | class DeviceKeyOptions : DefaultOptions
16 | {
17 | [Option(HelpText = "Specify path to device certificate (PFX).", Required = true)]
18 | public string PFXPath { get; set; }
19 |
20 | [Option(HelpText = "Set Tenant")]
21 | public string Tenant { get; set; }
22 |
23 | [Option(HelpText = "Set username")]
24 | public string UserName { get; set; }
25 |
26 | [Option(HelpText = "Set password")]
27 | public string Password { get; set; }
28 |
29 | [Option(HelpText = "Set Refreshtoken")]
30 | public string RefreshToken { get; set; }
31 | }
32 |
33 | [Verb("p2pcert", HelpText = "Ask for a P2P Certificate.")]
34 | class P2POptions : DefaultOptions
35 | {
36 | [Option(HelpText = "Specify path to device certificate (PFX).")]
37 | public string PFXPath { get; set; }
38 | [Option(HelpText = "Specify a password for the certificate", Default = "")]
39 | public string PFXPassword { get; set; }
40 |
41 | [Option(HelpText = "Device Name")]
42 | public string DeviceName { get; set; }
43 |
44 | [Option(HelpText = "Set Tenant")]
45 | public string Tenant { get; set; }
46 |
47 | [Option(HelpText = "Set username")]
48 | public string UserName { get; set; }
49 |
50 | [Option(HelpText = "Set password")]
51 | public string Password { get; set; }
52 |
53 | [Option(HelpText = "Set PRT")]
54 | public string PRT { get; set; }
55 |
56 | [Option(HelpText = "Set Session Key")]
57 | public string SessionKey { get; set; }
58 |
59 | [Option(HelpText = "Set DerivedKey")]
60 | public string DerivedKey { get; set; }
61 |
62 | [Option(HelpText = "Set Context")]
63 | public string Context { get; set; }
64 | }
65 |
66 | [Verb("nonce", HelpText = "Request a nonce from Azure.")]
67 | class NonceOptions : DefaultOptions
68 | {
69 | }
70 |
71 | [Verb("utils", HelpText = "Some arbitrary usefull functions.")]
72 | class UtilsOptions : DefaultOptions
73 | {
74 | [Option(HelpText = "Resolve a domain to a TenantID")]
75 | public string Domain { get; set; }
76 | }
77 |
78 | [Verb("cookie", HelpText = "Create a PRT Cookie for further usage or your browser.")]
79 | class CookieOptions : DefaultOptions
80 | {
81 | [Option(HelpText = "Set PRT")]
82 | public string PRT { get; set; }
83 |
84 | [Option(HelpText = "Set DerivedKey")]
85 | public string DerivedKey { get; set; }
86 |
87 | [Option(HelpText = "Set Context")]
88 | public string Context { get; set; }
89 |
90 | [Option(HelpText = "Set Session Key")]
91 | public string SessionKey { get; set; }
92 | }
93 | [Verb("token", HelpText = "Play with Azure Tokens.")]
94 | class TokenOptions : DefaultOptions
95 | {
96 | [Option(HelpText = "Set PRT")]
97 | public string PRT { get; set; }
98 |
99 | [Option(HelpText = "Set Session Key")]
100 | public string SessionKey { get; set; }
101 |
102 | [Option(HelpText = "Use DeviceCode authentication", Default = false)]
103 | public bool Devicecode{ get; set; }
104 |
105 | [Option(HelpText = "Set DerivedKey")]
106 | public string DerivedKey { get; set; }
107 |
108 | [Option(HelpText = "Set Context")]
109 | public string Context { get; set; }
110 |
111 | [Option(HelpText = "Set Refreshtoken")]
112 | public string RefreshToken { get; set; }
113 |
114 | [Option(HelpText = "Set PRTCookie")]
115 | public string PrtCookie { get; set; }
116 |
117 | [Option(HelpText = "Set ClientID (ApplicationID), for example GraphAPI (1b730954-1685-4b74-9bfd-dac224a7b894)", Default = "1b730954-1685-4b74-9bfd-dac224a7b894")]
118 | public string ClientID { get; set; }
119 |
120 | [Option(HelpText = "Set Client Secret")]
121 | public string ClientSecret { get; set; }
122 |
123 | [Option(HelpText = "Set Tenant")]
124 | public string Tenant { get; set; }
125 |
126 | [Option(HelpText = "Set username")]
127 | public string UserName { get; set; }
128 |
129 | [Option(HelpText = "Set password")]
130 | public string Password { get; set; }
131 |
132 | [Option(HelpText = "Set resource ID for access token, for example for Device Management (01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9)", Default = "https://graph.windows.net")]
133 | public string ResourceID { get; set; }
134 |
135 | [Option(HelpText = "Set a client used for token request, you can choose between: Outlook, Substrate, Teams, Graph, MSGraph, Webshell, Core, Office, Intune or Windows. Or you can set custom values with --clientid and --resourceid")]
136 | public string ClientName { get; set; }
137 |
138 | }
139 |
140 | [Verb("mdm", HelpText = "Do things with Intune like joining a device")]
141 | class DeviceOptions : DefaultOptions
142 | {
143 | [Option(HelpText = "Join a device, then you need to set at least a devicename (--devicename)", Default = false)]
144 | public bool JoinDevice { get; set; }
145 |
146 | [Option(HelpText = "Mark a device as compliant, then you need to set at least the deviceid (--objectid)", Default = false)]
147 | public bool MarkCompliant { get; set; }
148 |
149 | [Option(HelpText = "Specifiy the ObjectID of the device")]
150 | public string ObjectID{ get; set; }
151 |
152 | [Option(HelpText = "Specifiy device name")]
153 | public string DeviceName { get; set; }
154 |
155 | [Option(HelpText = "Set this, if you want only register the device", Default = false)]
156 | public bool RegisterDevice { get; set; }
157 |
158 | [Option(HelpText = "Set access token - use token with --clientid 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9 and --resourceid 01cb2876-7ebd-4aa4-9cc9-d28bd4d359a9 or --clientname Windows")]
159 | public string AccessToken { get; set; }
160 |
161 | [Option(HelpText = "Set Tenant")]
162 | public string Tenant { get; set; }
163 |
164 | [Option(HelpText = "Set username")]
165 | public string UserName { get; set; }
166 |
167 | [Option(HelpText = "Set password")]
168 | public string Password { get; set; }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 | launchSettings.json
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Mono auto generated files
18 | mono_crash.*
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # StyleCop
66 | StyleCopReport.xml
67 |
68 | # Files built by Visual Studio
69 | *_i.c
70 | *_p.c
71 | *_h.h
72 | *.ilk
73 | *.meta
74 | *.obj
75 | *.iobj
76 | *.pch
77 | *.pdb
78 | *.ipdb
79 | *.pgc
80 | *.pgd
81 | *.rsp
82 | *.sbr
83 | *.tlb
84 | *.tli
85 | *.tlh
86 | *.tmp
87 | *.tmp_proj
88 | *_wpftmp.csproj
89 | *.log
90 | *.vspscc
91 | *.vssscc
92 | .builds
93 | *.pidb
94 | *.svclog
95 | *.scc
96 |
97 | # Chutzpah Test files
98 | _Chutzpah*
99 |
100 | # Visual C++ cache files
101 | ipch/
102 | *.aps
103 | *.ncb
104 | *.opendb
105 | *.opensdf
106 | *.sdf
107 | *.cachefile
108 | *.VC.db
109 | *.VC.VC.opendb
110 |
111 | # Visual Studio profiler
112 | *.psess
113 | *.vsp
114 | *.vspx
115 | *.sap
116 |
117 | # Visual Studio Trace Files
118 | *.e2e
119 |
120 | # TFS 2012 Local Workspace
121 | $tf/
122 |
123 | # Guidance Automation Toolkit
124 | *.gpState
125 |
126 | # ReSharper is a .NET coding add-in
127 | _ReSharper*/
128 | *.[Rr]e[Ss]harper
129 | *.DotSettings.user
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # NuGet Symbol Packages
189 | *.snupkg
190 | # The packages folder can be ignored because of Package Restore
191 | **/[Pp]ackages/*
192 | # except build/, which is used as an MSBuild target.
193 | !**/[Pp]ackages/build/
194 | # Uncomment if necessary however generally it will be regenerated when needed
195 | #!**/[Pp]ackages/repositories.config
196 | # NuGet v3's project.json files produces more ignorable files
197 | *.nuget.props
198 | *.nuget.targets
199 |
200 | # Microsoft Azure Build Output
201 | csx/
202 | *.build.csdef
203 |
204 | # Microsoft Azure Emulator
205 | ecf/
206 | rcf/
207 |
208 | # Windows Store app package directories and files
209 | AppPackages/
210 | BundleArtifacts/
211 | Package.StoreAssociation.xml
212 | _pkginfo.txt
213 | *.appx
214 | *.appxbundle
215 | *.appxupload
216 |
217 | # Visual Studio cache files
218 | # files ending in .cache can be ignored
219 | *.[Cc]ache
220 | # but keep track of directories ending in .cache
221 | !?*.[Cc]ache/
222 |
223 | # Others
224 | ClientBin/
225 | ~$*
226 | *~
227 | *.dbmdl
228 | *.dbproj.schemaview
229 | *.jfm
230 | *.pfx
231 | *.publishsettings
232 | orleans.codegen.cs
233 |
234 | # Including strong name files can present a security risk
235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
236 | #*.snk
237 |
238 | # Since there are multiple workflows, uncomment next line to ignore bower_components
239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
240 | #bower_components/
241 |
242 | # RIA/Silverlight projects
243 | Generated_Code/
244 |
245 | # Backup & report files from converting an old project file
246 | # to a newer Visual Studio version. Backup files are not needed,
247 | # because we have git ;-)
248 | _UpgradeReport_Files/
249 | Backup*/
250 | UpgradeLog*.XML
251 | UpgradeLog*.htm
252 | ServiceFabricBackup/
253 | *.rptproj.bak
254 |
255 | # SQL Server files
256 | *.mdf
257 | *.ldf
258 | *.ndf
259 |
260 | # Business Intelligence projects
261 | *.rdl.data
262 | *.bim.layout
263 | *.bim_*.settings
264 | *.rptproj.rsuser
265 | *- [Bb]ackup.rdl
266 | *- [Bb]ackup ([0-9]).rdl
267 | *- [Bb]ackup ([0-9][0-9]).rdl
268 |
269 | # Microsoft Fakes
270 | FakesAssemblies/
271 |
272 | # GhostDoc plugin setting file
273 | *.GhostDoc.xml
274 |
275 | # Node.js Tools for Visual Studio
276 | .ntvs_analysis.dat
277 | node_modules/
278 |
279 | # Visual Studio 6 build log
280 | *.plg
281 |
282 | # Visual Studio 6 workspace options file
283 | *.opt
284 |
285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
286 | *.vbw
287 |
288 | # Visual Studio LightSwitch build output
289 | **/*.HTMLClient/GeneratedArtifacts
290 | **/*.DesktopClient/GeneratedArtifacts
291 | **/*.DesktopClient/ModelManifest.xml
292 | **/*.Server/GeneratedArtifacts
293 | **/*.Server/ModelManifest.xml
294 | _Pvt_Extensions
295 |
296 | # Paket dependency manager
297 | .paket/paket.exe
298 | paket-files/
299 |
300 | # FAKE - F# Make
301 | .fake/
302 |
303 | # CodeRush personal settings
304 | .cr/personal
305 |
306 | # Python Tools for Visual Studio (PTVS)
307 | __pycache__/
308 | *.pyc
309 |
310 | # Cake - Uncomment if you are using it
311 | # tools/**
312 | # !tools/packages.config
313 |
314 | # Tabs Studio
315 | *.tss
316 |
317 | # Telerik's JustMock configuration file
318 | *.jmconfig
319 |
320 | # BizTalk build output
321 | *.btp.cs
322 | *.btm.cs
323 | *.odx.cs
324 | *.xsd.cs
325 |
326 | # OpenCover UI analysis results
327 | OpenCover/
328 |
329 | # Azure Stream Analytics local run output
330 | ASALocalRun/
331 |
332 | # MSBuild Binary and Structured Log
333 | *.binlog
334 |
335 | # NVidia Nsight GPU debugger configuration file
336 | *.nvuser
337 |
338 | # MFractors (Xamarin productivity tool) working folder
339 | .mfractor/
340 |
341 | # Local History for Visual Studio
342 | .localhistory/
343 |
344 | # BeatPulse healthcheck temp database
345 | healthchecksdb
346 |
347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
348 | MigrationBackup/
349 |
350 | # Ionide (cross platform F# VS Code tools) working folder
351 | .ionide/
352 |
--------------------------------------------------------------------------------
/Lantern/Tokenator.cs:
--------------------------------------------------------------------------------
1 | using Lantern.Models;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Net.Http;
7 | using System.Text;
8 |
9 | namespace Lantern
10 | {
11 | class Tokenator
12 | {
13 |
14 | private static string RequestForPendingAuthentication(string code, string clientID, string proxy)
15 | {
16 | var formContent = new FormUrlEncodedContent(new[]
17 | {
18 | new KeyValuePair("code",code),
19 | new KeyValuePair("grant_type","urn:ietf:params:oauth:grant-type:device_code"),
20 | new KeyValuePair("client_id", clientID)
21 | });
22 |
23 | return Helper.PostToTokenEndpoint(formContent, proxy);
24 |
25 | }
26 |
27 | private static string RequestDeviceCode(string clientid, string resourceid, string proxy)
28 | {
29 | var formContent = new FormUrlEncodedContent(new[]
30 | {
31 | new KeyValuePair("client_id", clientid),
32 | new KeyValuePair("resource", resourceid)
33 | });
34 | return Helper.PostToDeviceCodeEndpoint(formContent, proxy);
35 | }
36 |
37 | private static string RequestP2PCertificate(string JWT, string tenant, string proxy)
38 | {
39 | var formContent = new FormUrlEncodedContent(new[]
40 | {
41 | new KeyValuePair("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"),
42 | new KeyValuePair("request", JWT)
43 | });
44 |
45 | return Helper.PostToTokenEndpoint(formContent, proxy, tenant);
46 | }
47 |
48 | private static string AuthenticateWithClientIDandSecret(string clientID, string clientSecret, string tenant, string proxy, string ressourceId)
49 | {
50 | var formContent = new FormUrlEncodedContent(new[]
51 | {
52 | new KeyValuePair("grant_type", "client_credentials"),
53 | new KeyValuePair("client_id", clientID),
54 | new KeyValuePair(ressourceId, ressourceId),
55 | new KeyValuePair("client_secret", clientSecret)
56 | });
57 | return Helper.PostToTokenEndpoint(formContent, proxy, tenant);
58 | }
59 |
60 | private static string AuthenticateWithUserNameAndPassword(string username, string password, string tenant, string proxy, string clientID, string ressourceId)
61 | {
62 | var formContent = new FormUrlEncodedContent(new[]
63 | {
64 | new KeyValuePair("grant_type", "password"),
65 | new KeyValuePair("scope", "openid"),
66 | new KeyValuePair("resource", ressourceId),
67 | new KeyValuePair("client_id", clientID),
68 | new KeyValuePair("username", username),
69 | new KeyValuePair("password", password)
70 | });
71 | return Helper.PostToTokenEndpoint(formContent, proxy, tenant);
72 | }
73 |
74 | private static string AuthenticateWithRefreshTokenToTenant(string refreshToken, string tenant, string proxy, string clientID, string ressourceId)
75 | {
76 | var formContent = new FormUrlEncodedContent(new[]
77 | {
78 | new KeyValuePair("scope", "openid"),
79 | new KeyValuePair("grant_type", "refresh_token"),
80 | new KeyValuePair("client_id", clientID),
81 | new KeyValuePair("resource", ressourceId),
82 | new KeyValuePair("refresh_token", refreshToken)
83 | });
84 | return Helper.PostToTokenEndpoint(formContent, proxy, tenant);
85 | }
86 |
87 | private static string AuthenticateWithRefreshToken(string refreshToken, string proxy, string clientID, string ressourceId)
88 | {
89 | var formContent = new FormUrlEncodedContent(new[]
90 | {
91 | new KeyValuePair("scope", "openid"),
92 | new KeyValuePair("grant_type", "refresh_token"),
93 | new KeyValuePair("resource", ressourceId),
94 | new KeyValuePair("client_id", clientID),
95 | new KeyValuePair("refresh_token", refreshToken)
96 | });
97 | return Helper.PostToTokenEndpoint(formContent, proxy);
98 | }
99 | private static string AuthenticateWithCode(string code, string proxy, string clientID, string ressourceId)
100 | {
101 | var formContent = new FormUrlEncodedContent(new[]
102 | {
103 | new KeyValuePair("grant_type", "authorization_code"),
104 | new KeyValuePair("resource", ressourceId),
105 | new KeyValuePair("client_id", clientID),
106 | new KeyValuePair("redirect_uri", "urn:ietf:wg:oauth:2.0:oob"),
107 | new KeyValuePair("code", code)
108 | });
109 |
110 | return Helper.PostToTokenEndpoint(formContent, proxy);
111 | }
112 |
113 | public static string GetP2PCertificate(string JWT, string tenant, string proxy)
114 | {
115 | string result;
116 | result = RequestP2PCertificate(JWT, tenant, proxy);
117 | return result;
118 | }
119 |
120 | public static string GetTokenFromPRTAndDerivedKey(string PRT, string DerivedKey, string Context, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
121 | {
122 | string result = null;
123 | string prtCookie = Helper.createPRTCookie(PRT, Context, DerivedKey, Proxy);
124 | string code = Helper.getCodeFromPRTCookie(prtCookie, Proxy, resourceID, clientID);
125 | result = AuthenticateWithCode(code, Proxy, clientID, resourceID);
126 | return result;
127 | }
128 |
129 | public static string GetTokenFromPRTAndSessionKey(string PRT, string SessionKey, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
130 | {
131 | string result = null;
132 | var context = Helper.GetByteArray(24);
133 | var decodedKey = Helper.Base64Decode(SessionKey);
134 | var derivedKey = Helper.CreateDerivedKey(decodedKey, context);
135 |
136 | var contextHex = Helper.Binary2Hex(context);
137 | var derivedSessionKeyHex = Helper.Binary2Hex(derivedKey);
138 |
139 | string prtCookie = Helper.createPRTCookie(PRT, contextHex, derivedSessionKeyHex, Proxy);
140 | string code = Helper.getCodeFromPRTCookie(prtCookie, Proxy, resourceID, clientID);
141 | result = AuthenticateWithCode(code, Proxy, clientID, resourceID);
142 | return result;
143 | }
144 |
145 | public static string GetTokenFromPRTCookie(string PRTCookie, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
146 | {
147 | string result = null;
148 | string code = Helper.getCodeFromPRTCookie(PRTCookie, Proxy, resourceID, clientID);
149 | result = AuthenticateWithCode(code, Proxy, clientID, resourceID);
150 | return result;
151 | }
152 |
153 | public static string GetTokenFromRefreshToken(string RefreshToken, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
154 | {
155 | string result = null;
156 | result = AuthenticateWithRefreshToken(RefreshToken, Proxy, clientID, resourceID);
157 | return result;
158 | }
159 |
160 | public static string GetTokenFromRefreshTokenToTenant(string RefreshToken, string Tenant, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
161 | {
162 | string result = null;
163 | result = AuthenticateWithRefreshTokenToTenant(RefreshToken, Tenant, Proxy, clientID, resourceID);
164 | return result;
165 | }
166 |
167 |
168 | public static string GetTokenFromUsernameAndPassword(string Username, string Password, string Tenant, string Proxy, string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894", string resourceID = "https://graph.windows.net")
169 | {
170 | string result = null;
171 | result = AuthenticateWithUserNameAndPassword(Username, Password, Tenant, Proxy, clientID, resourceID);
172 | return result;
173 | }
174 |
175 | public static string GetTokenWithClientIDAndSecret(string ClientID, string ClientSecret, string Tenant, string Proxy, string resourceID = "https://graph.windows.net")
176 | {
177 | string result = null;
178 | result = AuthenticateWithClientIDandSecret(ClientID, ClientSecret, Tenant, Proxy, resourceID);
179 | return result;
180 | }
181 |
182 | public static string GetTokenFromDeviceCode(string ClientID, string ResourceID, string Proxy)
183 | {
184 | string result = null;
185 | result = RequestDeviceCode(ClientID, ResourceID, Proxy);
186 | var InitDeviceCode = JsonConvert.DeserializeObject(result);
187 | Console.WriteLine(JToken.FromObject(InitDeviceCode).ToString(Formatting.Indented));
188 |
189 | var SecondsToWait = InitDeviceCode.interval;
190 | int WaitedTime = 0;
191 | while (WaitedTime < InitDeviceCode.expires_in)
192 | {
193 | result = RequestForPendingAuthentication(InitDeviceCode.device_code, ClientID, Proxy);
194 | JToken parsedesults = JToken.Parse(result);
195 | if (parsedesults["error"] != null)
196 | {
197 | Console.WriteLine("[+] Response from Azure: " + parsedesults["error"]);
198 | }else
199 | {
200 | return result;
201 | }
202 | System.Threading.Thread.Sleep(SecondsToWait * 1000);
203 | WaitedTime += SecondsToWait;
204 | result = null;
205 | }
206 | return null;
207 | }
208 |
209 | public static string getToken(TokenOptions opts, string clientID = null, string resourceID = null)
210 | {
211 | string result = null;
212 |
213 | if (clientID == null && resourceID == null)
214 | {
215 | clientID = opts.ClientID;
216 | resourceID = opts.ResourceID;
217 | }
218 |
219 | if (opts.Devicecode)
220 | {
221 | result = GetTokenFromDeviceCode(clientID, resourceID, opts.Proxy);
222 | }
223 | else if (opts.PRT != null & opts.DerivedKey != null & opts.Context != null)
224 | {
225 | result = GetTokenFromPRTAndDerivedKey(opts.PRT, opts.DerivedKey, opts.Context, opts.Proxy, clientID, resourceID);
226 | }
227 | else if (opts.PRT != null & opts.SessionKey != null)
228 | {
229 | result = GetTokenFromPRTAndSessionKey(opts.PRT, opts.SessionKey, opts.Proxy, clientID, resourceID);
230 | }
231 | else if (opts.PrtCookie != null)
232 | {
233 | result = GetTokenFromPRTCookie(opts.PrtCookie, opts.Proxy, clientID, resourceID);
234 | }
235 | else if (opts.RefreshToken != null)
236 | {
237 | result = GetTokenFromRefreshTokenToTenant(opts.RefreshToken, opts.Tenant, opts.Proxy, clientID, resourceID);
238 | }
239 | else if (opts.UserName != null & opts.Password != null)
240 | {
241 | result = GetTokenFromUsernameAndPassword(opts.UserName, opts.Password, opts.Tenant, opts.Proxy, clientID, resourceID);
242 | }
243 | else if (opts.Tenant != null & opts.ClientID != null & opts.ClientSecret != null)
244 | {
245 | result = GetTokenWithClientIDAndSecret(opts.ClientID, opts.ClientSecret, opts.Tenant, opts.Proxy, opts.ResourceID);
246 | }
247 | else
248 | {
249 | Console.WriteLine("[-] Please set the corect arguments.");
250 | return null;
251 | }
252 | return result;
253 | }
254 |
255 |
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/Lantern/Helper.cs:
--------------------------------------------------------------------------------
1 | using JWT;
2 | using JWT.Algorithms;
3 | using JWT.Serializers;
4 | using Lantern.Models;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Globalization;
10 | using System.Linq;
11 | using System.Net;
12 | using System.Net.Http;
13 | using System.Net.Http.Headers;
14 | using System.Text;
15 |
16 | namespace Lantern
17 | {
18 | class Helper
19 | {
20 | public static string getCodeFromPRTCookie(string cookie, string proxy, string resourceID = "https://graph.windows.net", string clientID = "1b730954-1685-4b74-9bfd-dac224a7b894")
21 | {
22 | String uri = string.Format(@"/Common/oauth2/authorize?resource={0}&client_id={1}&response_type={2}&haschrome={3}&redirect_uri={4}&client-request-id={5}&x-client-SKU={6}&x-client-Ver={7}&x-client-CPU={8}&x-client-OS={9}&site_id={10}&mscrid={11}",
23 | resourceID,
24 | clientID,
25 | "code",
26 | "1",
27 | "urn:ietf:wg:oauth:2.0:oob",
28 | //Guid.NewGuid(),
29 | "AAAAAAA",
30 | "PCL.Desktop",
31 | "3.19.7.16602",
32 | "x64",
33 | "Microsoft Windows NT 10.0.19569.0",
34 | "501358",
35 | Guid.NewGuid());
36 | HttpClient client = getDefaultClient(proxy);
37 | using (client)
38 | {
39 | var message = new HttpRequestMessage(HttpMethod.Get, uri);
40 | String xcookie = "x-ms-RefreshTokenCredential=" + cookie;
41 | message.Headers.Add("Cookie", xcookie);
42 | var response = client.SendAsync(message).Result;
43 | if (response.StatusCode.Equals("200"))
44 | {
45 | Console.WriteLine("[-] Something went wrong, cannot fetch code with PRT cookie, maybe Conditional Access Policy blocks.");
46 | return null;
47 | }
48 | string location = "";
49 | if (response.Headers.Contains("Location"))
50 | {
51 | location = response.Headers.Location.ToString();
52 | }
53 | else
54 | {
55 | Console.WriteLine("[-] Something went wrong, cannot fetch code with PRT cookie, maybe Conditional Access Policy blocks.");
56 | return "";
57 | }
58 |
59 | int startOf = location.IndexOf("code=");
60 | if (startOf == -1)
61 | {
62 | Console.WriteLine("[-] Something went wrong, cannot fetch code with PRT cookie, maybe Conditional Access Policy blocks.");
63 | return null;
64 | }
65 | int endOf = location.IndexOf("&", startOf + 5);
66 | int len = endOf - startOf;
67 | string code = location.Substring(startOf + 5, len - 5);
68 | client.Dispose();
69 | return code;
70 | }
71 | }
72 |
73 | public static HttpClient getDefaultClient(String proxy = null, bool useCookies = true, String baseAdress = "https://login.microsoftonline.com")
74 | {
75 | HttpClientHandler handler = new HttpClientHandler();
76 | if (proxy != null)
77 | {
78 | handler.Proxy = new WebProxy(proxy);
79 | handler.UseProxy = true;
80 | }
81 |
82 | handler.ClientCertificateOptions = ClientCertificateOption.Manual;
83 | handler.ServerCertificateCustomValidationCallback =
84 | (httpRequestMessage, cert, cetChain, policyErrors) =>
85 | {
86 | return true;
87 | };
88 | handler.AllowAutoRedirect = false;
89 |
90 | handler.UseCookies = useCookies;
91 | var client = new HttpClient(handler);
92 | client.BaseAddress = new Uri(baseAdress);
93 | client.DefaultRequestHeaders.Clear();
94 | //client.DefaultRequestHeaders.Add("UA-CPU", "AMD64");
95 | client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; Win64; x64; Trident/7.0; .NET4.0C; .NET4.0E)");
96 | return client;
97 |
98 | }
99 |
100 | // https://stackoverflow.com/questions/1459006/is-there-a-c-sharp-equivalent-to-pythons-unhexlify
101 | public static byte[] Hex2Binary(string hex)
102 | {
103 | var chars = hex.ToCharArray();
104 | var bytes = new List();
105 | for (int index = 0; index < chars.Length; index += 2)
106 | {
107 | var chunk = new string(chars, index, 2);
108 | bytes.Add(byte.Parse(chunk, NumberStyles.AllowHexSpecifier));
109 | }
110 | return bytes.ToArray();
111 | }
112 |
113 | //https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa
114 | public static string Binary2Hex(byte[] ba)
115 | {
116 | return BitConverter.ToString(ba).Replace("-", "");
117 | }
118 |
119 | //https://stackoverflow.com/questions/1228701/code-for-decoding-encoding-a-modified-base64-url
120 | public static byte[] Base64Decode(string arg)
121 | {
122 | string s = arg;
123 | s = s.Replace('-', '+'); // 62nd char of encoding
124 | s = s.Replace('_', '/'); // 63rd char of encoding
125 | switch (s.Length % 4) // Pad with trailing '='s
126 | {
127 | case 0: break; // No pad chars in this case
128 | case 2: s += "=="; break; // Two pad chars
129 | case 3: s += "="; break; // One pad char
130 | default:
131 | throw new System.Exception(
132 | "Illegal base64prt string!");
133 | }
134 | return Convert.FromBase64String(s); // Standard base64 decoder
135 | }
136 |
137 | public static string Base64UrlEncode(byte[] arg)
138 | {
139 | string s = Convert.ToBase64String(arg); // Regular base64 encoder
140 | s = s.Split('=')[0]; // Remove any trailing '='s
141 | s = s.Replace('+', '-'); // 62nd char of encoding
142 | s = s.Replace('/', '_'); // 63rd char of encoding
143 | return s;
144 | }
145 |
146 | public static string createPRTCookie(string prt, string context, string derived_sessionkey, string proxy, byte[] contextBytes = null, byte[] sessionKeyBytes = null)
147 | {
148 |
149 | string nonce = getNonce(proxy);
150 |
151 | byte[] data = Base64Decode(prt);
152 |
153 | string prtdecoded = Encoding.UTF8.GetString(data);
154 |
155 | var payload = new Dictionary
156 | {
157 | { "refresh_token", prtdecoded },
158 | { "is_primary", "true" },
159 | { "request_nonce", nonce }
160 | };
161 |
162 | Dictionary header = null;
163 | if (context != null)
164 | {
165 | header = new Dictionary
166 | {
167 | { "ctx", Hex2Binary(context) }
168 | };
169 | }
170 | else
171 | {
172 | header = new Dictionary
173 | {
174 | { "ctx", contextBytes }
175 | };
176 | }
177 | IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
178 | IJsonSerializer serializer = new JsonNetSerializer();
179 | IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
180 | IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
181 |
182 | byte[] sdata = null;
183 | if (derived_sessionkey != null)
184 | {
185 | string secret = derived_sessionkey.Replace(" ", "");
186 | sdata = Hex2Binary(secret);
187 | } else
188 | {
189 | sdata = sessionKeyBytes;
190 | }
191 | var cookie = encoder.Encode(header, payload, sdata);
192 | return cookie;
193 | }
194 |
195 | public static string signJWT(Dictionary header, Dictionary payload, string key)
196 | {
197 | IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric
198 | IJsonSerializer serializer = new JsonNetSerializer();
199 | IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
200 | IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
201 | string secret = key.Replace(" ", "");
202 | byte[] sdata = Hex2Binary(secret);
203 | return encoder.Encode(header, payload, sdata);
204 | }
205 |
206 | public static String getNonce(string proxy)
207 | {
208 | using (var client = getDefaultClient(proxy))
209 | {
210 | String uri = string.Format(@"/Common/oauth2/authorize?client_id={0}", "1b730954-1685-4b74-9bfd-dac224a7b894");
211 | var response = client.GetAsync(uri).Result;
212 | var responseContent = response.Content;
213 | string responseString = responseContent.ReadAsStringAsync().Result;
214 | int startOf = responseString.IndexOf("\"nonce\":\"");
215 | int endOf = responseString.IndexOf("\"", startOf + 9);
216 | int len = endOf - startOf;
217 | string nonce = responseString.Substring(startOf + 9, len - 9);
218 | client.Dispose();
219 | return nonce;
220 | }
221 | }
222 |
223 |
224 | private static string PostTo(string uri, FormUrlEncodedContent formContent, string proxy)
225 | {
226 | using (var message = new HttpRequestMessage(HttpMethod.Post, uri))
227 | using (var client = Helper.getDefaultClient(proxy, false))
228 | {
229 | //message.Headers.Add("client-request-id", Guid.NewGuid().ToString());
230 | //message.Headers.Add("return-client-request-id", "true");
231 | message.Content = formContent;
232 | var response = client.SendAsync(message).Result;
233 | var result = response.Content.ReadAsStringAsync().Result;
234 | return result;
235 | }
236 | }
237 |
238 | private static string GetFrom(string uri, string proxy)
239 | {
240 | using (var message = new HttpRequestMessage(HttpMethod.Get, uri))
241 | using (var client = Helper.getDefaultClient(proxy, false))
242 | {
243 | var response = client.SendAsync(message).Result;
244 | var result = response.Content.ReadAsStringAsync().Result;
245 | return result;
246 | }
247 | }
248 |
249 | public static int PatchRequest(string uri, string accesstoken, string proxy)
250 | {
251 | using(var content = new StringContent("{}", Encoding.UTF8, "application/json"))
252 | using (var message = new HttpRequestMessage(HttpMethod.Patch, uri))
253 | using (var client = Helper.getDefaultClient(proxy, false, "https://graph.windows.net"))
254 | {
255 | message.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
256 | message.Content = content;
257 | var response = client.SendAsync(message).Result;
258 | return (int)response.StatusCode;
259 | }
260 | }
261 |
262 | public static string GetOpenIDConfiguration(string domain, string proxy)
263 | {
264 | string uri = "/" + domain + "/.well-known/openid-configuration";
265 | return GetFrom(uri,proxy);
266 | }
267 |
268 | public static string PostToDeviceCodeEndpoint(FormUrlEncodedContent formContent, string proxy)
269 | {
270 | string uri = "/common/oauth2/devicecode";
271 | return PostTo(uri, formContent, proxy);
272 | }
273 |
274 | public static string PostToTokenEndpoint(FormUrlEncodedContent formContent, string proxy, string tenant = null)
275 | {
276 | string uri = "/common/oauth2/token";
277 | if (tenant != null)
278 | {
279 | uri = "/" + tenant + "/oauth2/token";
280 | }
281 | return PostTo(uri, formContent, proxy);
282 | }
283 |
284 | public static string GetNonce2(string proxy)
285 | {
286 | var formContent = new FormUrlEncodedContent(new[]
287 | {
288 | new KeyValuePair("grant_type", "srv_challenge")
289 | });
290 | string result = PostToTokenEndpoint(formContent, proxy);
291 | JToken parsedNonce = JToken.Parse(result);
292 | return parsedNonce["Nonce"].ToString();
293 | }
294 |
295 | public static string GetTenantFromAccessToken(string accesstoken)
296 | {
297 | return GetInfoFromBase64JSON(accesstoken, "tid");
298 | }
299 |
300 | public static string GetAudienceFromAccessToken(string accesstoken)
301 | {
302 | return GetInfoFromBase64JSON(accesstoken, "aud");
303 | }
304 | public static string getUPNFromAccessToken(string accesstoken)
305 | {
306 | return GetInfoFromBase64JSON(accesstoken, "upn");
307 | }
308 |
309 | public static string GetInfoFromBase64JSON(string jsonString, string info)
310 | {
311 | var serializer = new JsonNetSerializer();
312 | var urlEncoder = new JwtBase64UrlEncoder();
313 | var decoder = new JwtDecoder(serializer, urlEncoder);
314 | string decodedaccesstoken = decoder.Decode(jsonString);
315 | JToken parsedAccessToken = JToken.Parse(decodedaccesstoken);
316 | return parsedAccessToken[info].ToString();
317 | }
318 |
319 | public static byte[] GetByteArray(int size)
320 | {
321 | Random rnd = new Random();
322 | byte[] b = new byte[size]; // convert kb to byte
323 | rnd.NextBytes(b);
324 | return b;
325 | }
326 |
327 | public static byte[] CombineByteArrays(byte[] first, byte[] second)
328 | {
329 | return first.Concat(second).ToArray();
330 | }
331 |
332 | public static byte[] CreateDerivedKey(byte[] sessionKey, byte[] context)
333 | {
334 | byte[] sessionKeyBytes = sessionKey;
335 | byte[] contextBytes = context;
336 | byte[] label = System.Text.Encoding.UTF8.GetBytes("AzureAD-SecureConversation");
337 |
338 | var first = new byte[]{ 0x00, 0x00, 0x00, 0x01 };
339 | var second = new byte[] { 0x00 };
340 | var third = new byte[] { 0x00, 0x00, 0x01, 0x00 };
341 |
342 | var value = CombineByteArrays(first, label);
343 | value = CombineByteArrays(value, second);
344 | value = CombineByteArrays(value, contextBytes);
345 | value = CombineByteArrays(value, third);
346 |
347 | var hmac = new System.Security.Cryptography.HMACSHA256(sessionKeyBytes);
348 |
349 | return hmac.ComputeHash(value);
350 | }
351 |
352 | public static byte[] ConvertToByteArray(string str, Encoding encoding)
353 | {
354 | return encoding.GetBytes(str);
355 | }
356 |
357 | public static String ToBinary(Byte[] data)
358 | {
359 | return string.Join(" ", data.Select(byt => Convert.ToString(byt, 2).PadLeft(8, '0')));
360 | }
361 | }
362 | }
363 |
--------------------------------------------------------------------------------
/Lantern/Program.cs:
--------------------------------------------------------------------------------
1 | using CommandLine;
2 | using CommandLine.Text;
3 | using JWT;
4 | using JWT.Serializers;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Net.Http;
11 | using System.Security.Cryptography;
12 | using System.Security.Cryptography.X509Certificates;
13 | using System.Text;
14 |
15 | namespace Lantern
16 | {
17 |
18 |
19 |
20 | class Program
21 | {
22 | static void PrintBanner()
23 | {
24 | String banner = @"
25 | .____ __
26 | | | _____ _____/ |_ ___________ ____
27 | | | \__ \ / \ __\/ __ \_ __ \/ \
28 | | |___ / __ \| | \ | \ ___/| | \/ | \
29 | |_______ (____ /___| /__| \___ >__| |___| /
30 | \/ \/ \/ \/ \/ ";
31 | Console.WriteLine("");
32 | Console.WriteLine(banner);
33 | Console.WriteLine("");
34 | }
35 |
36 |
37 | static int DisplayHelp(ParserResult