-
Notifications
You must be signed in to change notification settings - Fork 351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add Outlook HTTP API validation #1194
feat: add Outlook HTTP API validation #1194
Conversation
@@ -193,4 +251,12 @@ mod tests { | |||
let f = f1.try_join(f2).await; | |||
assert!(f.is_ok(), "{:?}", f); | |||
} | |||
|
|||
#[test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No tests for the check_outlook_api()
function—any idea if there's a [email protected]
equivalent that could be used here?
cebf38b
to
0373295
Compare
Hey @PsypherPunk, after some testing, I am a bit reluctant to merge this PR. I tested on some valid Outlook 365 emails, and the results are all incorrect: Reacher returns If you send me your email, I can send you a couple of addresses to test against. You can find mine in the README. |
My email's in the commit message if you want to forward those addresses to test: will be happy to give them a whirl see if there's something I can tweak. |
Done. Yeah, let's play a bit to see if we can make this work 💪 |
I've tried this with those sample emails and yep, it fails miserably. However, I've tried this with another couple of domains—presumably ones where OneDrive use is more ubiquitous—and it's 100% accurate. So, as the article suggests, positive responses are accurate but false-negatives are possible (and seemingly likely.) Is there any value in doing something like this? if input.outlook_use_api && host_lowercase.ends_with(".mail.protection.outlook.com.") {
if let Some(smtp_details) = hotmail::check_outlook_api(to_email, input)
.await
.map_err(|err| err.into()) {
return smtp_details;
}
} Where |
Hi @AmauryM and @PsypherPunk, |
Have you given it a try, @hckhanh? I'd be curious to know how it's working for anyone else. |
and
I think this makes sense @PsypherPunk, let's do this. Can you please also add relevant comments detailing the drawbacks of this |
- check the validity of Outlook/Office 365 email addresses via the method outlined [here](https://www.trustedsec.com/blog/achieving-passive-user-enumeration-with-onedrive/). - run only via the `--outlook-use-api` flags (defaulting to `false`.) relates reacherhq#937
use `.mail.protection.outlook.com.` for domains backed by Outlook/Office 365.
if using `--outlook-use-api`, only return immediately in the event of a positive response: negative responses are ambiguous and the process should fall back to subsequent checks.
Hi @PsypherPunk, I think the result is really good: {
"input": "hckhanh@[redacted].com",
"is_reachable": "safe",
"misc": {
"is_disposable": false,
"is_role_account": false,
"gravatar_url": null
},
"mx": {
"accepts_mail": true,
"records": [
"[redacted]-com.mail.protection.outlook.com."
]
},
"smtp": {
"can_connect_smtp": true,
"has_full_inbox": false,
"is_catch_all": false,
"is_deliverable": true,
"is_disabled": false
},
"syntax": {
"address": "hckhanh@[redacted].com",
"domain": "[redacted].com",
"is_valid_syntax": true,
"username": "hckhanh"
}
} Even the result is much more better than the previous one (it was |
bb11e5e
to
16430dc
Compare
I have another question, @PsypherPunk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM! I have one final comment about the public-facing API. Using outlook_
may seem like this method would work for @outlook.com emails too, which is incorrect.
Would like to here opinions about:
microsoft365_
office365_
I'm not too well-versed in the MS ecosystem to know what terms people usually use.
Good shout, @AmauryM: Wikipedia says Microsoft 365: "Microsoft 365, formerly Office 365…" so I'll go with that…? |
update references to "Microsoft 365" to make is more explicit that this pertains to the underlying services, not Outlook addresses.
661f867
to
ccbb9a6
Compare
pub async fn check_microsoft365_api( | ||
to_email: &EmailAddress, | ||
input: &CheckEmailInput, | ||
) -> Result<Option<SmtpDetails>, HotmailError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just tried this manually, and I believe there's a bug: if you run cargo run -- --microsoft365-use-api=true <email>
with someone that hasn't opened their one drive, it returns a Err(ReqwestError)
. Whereas a Ok(None)
is expected, right? I believe in #1194 (comment) we both meant that in that case the algorithm should just continue as normal, which is currently not the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to simply do this:
) -> Result<Option<SmtpDetails>, HotmailError> { | |
) -> Result<SmtpDetails, HotmailError> { |
and below do:
if input.microsoft365_use_api && host_lowercase.ends_with(".mail.protection.outlook.com.") {
// Continue in the event of an ambiguous result.
if let Ok(smtp_details) = hotmail::check_microsoft365_api(to_email, input).await {
return Ok(smtp_details);
}
}
I.e. continue the normal SMTP logic if any error occurs in this API. Do you have better ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think I know what's happening:
- if the API returns a 403, it returns a positive response (
Ok(Some(smtp_details))
.) - if the API returns a 404, it returns a negative response (
Ok(None)
.) - if the API fails because the OneDrive domain doesn't exist (i.e. the company in question just doesn't use OneDrive), it raises an error (
Err(HotmailError(ReqwestError))
.) - if the API fails for some other reason (e.g. a transient network issue), it raises an error (
Err(HotmailError(ReqwestError))
.)
The problem is that those last two are indistinguishable, although 3. should result in some indication that it's ambiguous, like 1. and 2.
However, I can't of a way to make that distinction so I think you're right: I'll aim to allow it continue in the event of any error (but might add an additional log which might allow things to be narrowed down in the future.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AmauryM, I've pushed up a change which resolves the issues (I think; I've tested the scenarios I'm aware of and they either pass or continue to SMTP checks) but leaves the Result<Option<SmtpDetails>, HotmailError>
signature in place.
In order to have check_microsoft365_api()
return a Result<SmtpDetails, HotmailError>
, I'd have to change the "successful" 404 response into a HotmailError
. That's easy enough but adding another HotmailError
type (e.g. HotmailError::AmbiguousResponse
) didn't seem quite right.
Basically:
- if the HTTP request succeeds and we see a 403, we return the result.
- if the HTTP request succeeds and we see a 404, we continue to SMTP checks.
- if the HTTP request fails, we log the error and continue to SMTP checks.
allow both failures in the HTTP request and 404 responses to continue.
31a2dff
to
4f4570a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM, thanks @PsypherPunk!
Hi @AmauryM, @PsypherPunk error[E0433]: failed to resolve: use of undeclared crate or module `hotmail`
--> C:\Users\hoang\.cargo\git\checkouts\check-if-email-exists-c4445d2e2cd9a1bc\5d3c49f\core\src\smtp\mod.rs:73:9
|
73 | match hotmail::check_microsoft365_api(to_email, input).await {
| ^^^^^^^ use of undeclared crate or module `hotmail`
For more information about this error, try `rustc --explain E0433`.
error: could not compile `check-if-email-exists` due to previous error
warning: build failed, waiting for other jobs to finish...
Internal Error: Command failed: cargo build |
* feat: add Outlook HTTP API validation - check the validity of Outlook/Office 365 email addresses via the method outlined [here](https://www.trustedsec.com/blog/achieving-passive-user-enumeration-with-onedrive/). - run only via the `--outlook-use-api` flags (defaulting to `false`.) relates reacherhq#937 * fix: restrict Office 365 domain use `.mail.protection.outlook.com.` for domains backed by Outlook/Office 365. * fix: continue for non-definitive responses from Outlook API if using `--outlook-use-api`, only return immediately in the event of a positive response: negative responses are ambiguous and the process should fall back to subsequent checks. * fix: amend Outlook references update references to "Microsoft 365" to make is more explicit that this pertains to the underlying services, not Outlook addresses. * fix: continue in the event of a ReqwestError allow both failures in the HTTP request and 404 responses to continue. Co-authored-by: Amaury <[email protected]>
--outlook-use-api
flags (defaulting tofalse
.)relates #937