This repository was archived by the owner on Jan 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 126
Add Email Content Injection Query #108
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/smtp" | ||
| ) | ||
|
|
||
| func mail(w http.ResponseWriter, r *http.Request) { | ||
| host := r.Header.Get("Host") | ||
| token := backend.getUserSecretResetToken(email) | ||
| body := "Click to reset password: " + host + "/" + token | ||
| smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/smtp" | ||
| ) | ||
|
|
||
| func mailGood(w http.ResponseWriter, r *http.Request) { | ||
| host := config.Get("Host") | ||
| token := backend.getUserSecretResetToken(email) | ||
| body := "Click to reset password: " + host + "/" + token | ||
| smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body)) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| <!DOCTYPE qhelp SYSTEM "qhelp.dtd"> | ||
| <qhelp> | ||
| <overview> | ||
| <p> | ||
| Using untrusted input to construct an email induces multiple security | ||
| vulnerabilities. For instance, inclusion of an untrusted input in a email body | ||
| may allow an attacker to conduct Cross Site Scripting (XSS) Attacks. While | ||
| inclusion of an HTTP Header in the email body may allow a full account | ||
| compromise as shown in the example below. | ||
| </p> | ||
| </overview> | ||
| <recommendation> | ||
| <p> | ||
| Any data which is passed to an email subject or body must be sanitized before use. | ||
| </p> | ||
| </recommendation> | ||
| <example> | ||
| <p> | ||
| In the following example snippet, the | ||
| <code>host</code> | ||
| field is user controlled. | ||
| </p> | ||
| <p> | ||
| A malicious user can send an HTTP request to the targeted web site, | ||
| but with a Host header that refers to his own web site. This means the | ||
| emails will be sent out to potential victims, originating from a server | ||
| they trust, but with links leading to a malicious web site. | ||
| </p> | ||
| <p> | ||
| If the email contains a password reset link, and should the victim click | ||
| the link, the secret reset token will be leaked to the attacker. Using the | ||
| leaked token, the attacker can then construct the real reset link and use it to | ||
| change the victim's password. | ||
max-schaefer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </p> | ||
| <sample src="EmailBad.go" /> | ||
| <p> | ||
| One way to prevent this is to load the host name from a trusted configuration file instead. | ||
| </p> | ||
| <sample src="EmailGood.go" /> | ||
| </example> | ||
| <references> | ||
| <li> | ||
| OWASP | ||
| <a href="https://owasp.org/www-community/attacks/Content_Spoofing">Content Spoofing</a> | ||
| . | ||
| </li> | ||
| </references> | ||
| </qhelp> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** | ||
| * @name Email content injection | ||
| * @description Incorporating untrusted input directly into an email message can enable | ||
| * content spoofing, which in turn may lead to information leaks and other | ||
| * security issues. | ||
| * @id go/email-injection | ||
| * @kind path-problem | ||
| * @problem.severity error | ||
| * @tags security | ||
| * external/cwe/cwe-640 | ||
| */ | ||
|
|
||
| import go | ||
| import DataFlow::PathGraph | ||
| import EmailInjection::EmailInjection | ||
This conversation was marked as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config | ||
| where config.hasFlowPath(source, sink) | ||
| select sink, source, sink, "Email content may contain $@.", source.getNode(), "untrusted input" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /** | ||
| * Provides a taint-tracking configuration for reasoning about | ||
| * server-side email-injection vulnerabilities. | ||
| * | ||
| * Note, for performance reasons: only import this file if | ||
| * `EmailInjection::Configuration` is needed, otherwise | ||
| * `EmailInjectionCustomizations` should be imported instead. | ||
| */ | ||
|
|
||
| import go | ||
|
|
||
| /** | ||
| * Provides a taint-tracking configuration for reasoning about | ||
| * email-injection vulnerabilities. | ||
| */ | ||
| module EmailInjection { | ||
| import EmailInjectionCustomizations::EmailInjection | ||
|
|
||
| /** | ||
| * A taint-tracking configuration for reasoning about email-injection vulnerabilities. | ||
| */ | ||
| class Configuration extends TaintTracking::Configuration { | ||
| Configuration() { this = "Email Injection" } | ||
|
|
||
| override predicate isSource(DataFlow::Node source) { source instanceof Source } | ||
|
|
||
| override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } | ||
| } | ||
| } |
30 changes: 30 additions & 0 deletions
30
ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| /** Provides classes for reasoning about email-injection vulnerabilities. */ | ||
|
|
||
| import go | ||
|
|
||
| /** | ||
| * Provides a library for reasoning about email-injection vulnerabilities. | ||
| */ | ||
| module EmailInjection { | ||
| /** | ||
| * A data-flow node that should be considered a source of untrusted data for email-injection vulnerabilities. | ||
| */ | ||
| abstract class Source extends DataFlow::Node { } | ||
|
|
||
| /** | ||
| * A data-flow node that should be considered a sink for email-injection vulnerabilities. | ||
| */ | ||
| abstract class Sink extends DataFlow::Node { } | ||
|
|
||
| /** A source of untrusted data, considered as a taint source for email injection. */ | ||
| class UntrustedFlowSourceAsSource extends Source { | ||
| UntrustedFlowSourceAsSource() { this instanceof UntrustedFlowSource } | ||
| } | ||
|
|
||
| /** | ||
| * A data-flow node that becomes part of an email considered as a taint sink for email injection. | ||
| */ | ||
| class MailDataAsSink extends Sink { | ||
| MailDataAsSink() { this instanceof MailData } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /** Provides classes for working with email-related APIs. */ | ||
|
|
||
| import go | ||
|
|
||
| /** | ||
| * A data-flow node that represents data written to an email. | ||
This conversation was marked as resolved.
Show resolved
Hide resolved
|
||
| * Data in this case includes the email headers and the mail body | ||
| * | ||
| * Extend this class to refine existing API models. If you want to model new APIs, | ||
| * extend `MailDataCall::Range` instead. | ||
| */ | ||
| class MailData extends DataFlow::Node { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have removed the |
||
| MailDataCall::Range self; | ||
|
|
||
| MailData() { this = self.getData() } | ||
| } | ||
|
|
||
| /** Provides classes for working with calls which write data to an email. */ | ||
| module MailDataCall { | ||
| /** | ||
| * A data-flow node that represents a call which writes data to an email. | ||
| * Data in this case refers to email headers and the mail body | ||
| * | ||
| */ | ||
| abstract class Range extends DataFlow::CallNode { | ||
| /** Gets data written to an email connection. */ | ||
| abstract DataFlow::Node getData(); | ||
| } | ||
|
|
||
| /** Get the package name `github.com/sendgrid/sendgrid-go/helpers/mail`. */ | ||
| bindingset[result] | ||
| private string sendgridMail() { result = "github.com/sendgrid/sendgrid-go/helpers/mail" } | ||
|
|
||
| /** A Client.Data expression string used in an API function of the net/smtp package. */ | ||
| private class SmtpData extends Range { | ||
| SmtpData() { | ||
| // func (c *Client) Data() (io.WriteCloser, error) | ||
| this.getTarget().(Method).hasQualifiedName("net/smtp", "Client", "Data") | ||
| } | ||
|
|
||
| override DataFlow::Node getData() { | ||
| exists(DataFlow::CallNode write, DataFlow::Node writer, int i | | ||
| this.getResult(0) = writer and | ||
| ( | ||
| write.getTarget().hasQualifiedName("fmt", "Fprintf") | ||
| or | ||
| write.getTarget().hasQualifiedName("io", "WriteString") | ||
| ) and | ||
| writer.getASuccessor*() = write.getArgument(0) and | ||
| i > 0 and | ||
| write.getArgument(i) = result | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /** A send mail expression string used in an API function of the net/smtp package. */ | ||
| private class SmtpSendMail extends Range { | ||
| SmtpSendMail() { | ||
| // func SendMail(addr string, a Auth, from string, to []string, msg []byte) error | ||
| this.getTarget().hasQualifiedName("net/smtp", "SendMail") | ||
| } | ||
|
|
||
| override DataFlow::Node getData() { result = this.getArgument(4) } | ||
| } | ||
|
|
||
| /** A call to `NewSingleEmail` API function of the Sendgrid mail package. */ | ||
| private class SendGridSingleEmail extends Range { | ||
| SendGridSingleEmail() { | ||
| // func NewSingleEmail(from *Email, subject string, to *Email, plainTextContent string, htmlContent string) *SGMailV3 | ||
| this.getTarget().hasQualifiedName(sendgridMail(), "NewSingleEmail") | ||
| } | ||
|
|
||
| override DataFlow::Node getData() { result = this.getArgument([1, 3, 4]) } | ||
| } | ||
|
|
||
| /* Gets the value of the `i`-th content parameter of the given `call` */ | ||
| private DataFlow::Node getContent(DataFlow::CallNode call, int i) { | ||
| exists(DataFlow::CallNode cn, DataFlow::Node content | | ||
| // func NewContent(contentType string, value string) *Content | ||
| cn.getTarget().hasQualifiedName(sendgridMail(), "NewContent") and | ||
| cn.getResult() = content and | ||
| content.getASuccessor*() = call.getArgument(i) and | ||
| result = cn.getArgument(1) | ||
| ) | ||
| } | ||
|
|
||
| /** A call to `NewV3MailInit` API function of the Sendgrid mail package. */ | ||
| private class SendGridV3Init extends Range { | ||
| SendGridV3Init() { | ||
| // func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3 | ||
| this.getTarget().hasQualifiedName(sendgridMail(), "NewV3MailInit") | ||
| } | ||
|
|
||
| override DataFlow::Node getData() { | ||
| exists(int i | result = getContent(this, i) and i >= 3) | ||
| or | ||
| result = this.getArgument(1) | ||
| } | ||
| } | ||
|
|
||
| /** A call to `AddContent` API function of the Sendgrid mail package. */ | ||
| private class SendGridAddContent extends Range { | ||
| SendGridAddContent() { | ||
| // func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3 | ||
| this.getTarget().(Method).hasQualifiedName(sendgridMail(), "SGMailV3", "AddContent") | ||
| } | ||
|
|
||
| override DataFlow::Node getData() { result = getContent(this, _) } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| edges | ||
| | email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion | | ||
| | email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion | | ||
| | email.go:42:21:42:31 | call to Referer : string | email.go:46:25:46:38 | untrustedInput | | ||
| | email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput | | ||
| | email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput | | ||
| | email.go:63:21:63:31 | call to Referer : string | email.go:65:47:65:60 | untrustedInput | | ||
| | email.go:73:21:73:31 | call to Referer : string | email.go:79:47:79:60 | untrustedInput | | ||
| | email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput | | ||
| | email.go:87:21:87:31 | call to Referer : string | email.go:96:48:96:61 | untrustedInput | | ||
| nodes | ||
| | email.go:24:10:24:17 | selection of Header : Header | semmle.label | selection of Header : Header | | ||
| | email.go:27:56:27:67 | type conversion | semmle.label | type conversion | | ||
| | email.go:34:21:34:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:36:57:36:78 | type conversion | semmle.label | type conversion | | ||
| | email.go:42:21:42:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:46:25:46:38 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:51:21:51:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:57:46:57:59 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:58:52:58:65 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:63:21:63:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:65:47:65:60 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:73:21:73:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:79:47:79:60 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:87:21:87:31 | call to Referer : string | semmle.label | call to Referer : string | | ||
| | email.go:94:37:94:50 | untrustedInput | semmle.label | untrustedInput | | ||
| | email.go:96:48:96:61 | untrustedInput | semmle.label | untrustedInput | | ||
| #select | ||
| | email.go:27:56:27:67 | type conversion | email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion | Email content may contain $@. | email.go:24:10:24:17 | selection of Header | untrusted input | | ||
| | email.go:36:57:36:78 | type conversion | email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion | Email content may contain $@. | email.go:34:21:34:31 | call to Referer | untrusted input | | ||
| | email.go:46:25:46:38 | untrustedInput | email.go:42:21:42:31 | call to Referer : string | email.go:46:25:46:38 | untrustedInput | Email content may contain $@. | email.go:42:21:42:31 | call to Referer | untrusted input | | ||
| | email.go:57:46:57:59 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input | | ||
| | email.go:58:52:58:65 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input | | ||
| | email.go:65:47:65:60 | untrustedInput | email.go:63:21:63:31 | call to Referer : string | email.go:65:47:65:60 | untrustedInput | Email content may contain $@. | email.go:63:21:63:31 | call to Referer | untrusted input | | ||
| | email.go:79:47:79:60 | untrustedInput | email.go:73:21:73:31 | call to Referer : string | email.go:79:47:79:60 | untrustedInput | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input | | ||
| | email.go:94:37:94:50 | untrustedInput | email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input | | ||
| | email.go:96:48:96:61 | untrustedInput | email.go:87:21:87:31 | call to Referer : string | email.go:96:48:96:61 | untrustedInput | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| experimental/CWE-640/EmailInjection.ql |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.