Skip to content
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

[Java] CodeQL query to detect Log Injection #3882

Closed
wants to merge 6 commits into from

Conversation

dellalibera
Copy link
Contributor

Log Injection query is available in c# query, javascript (experimental) query but it is not available in java query.
I created a query to detect a log injection vulnerability in java code.

This query addresses the scenario where user controlled input is logged-as is.

Examples of scenarios detected:

    private final Logger log = LoggerFactory.getLogger(LogInjection.class);

    @GetMapping("/bad")
    public String bad(@RequestParam(value = "username", defaultValue = "name") String username) {
        log.warn("User:'{}'", username);
        return username;
    }

Any feedback is welcome.
I hope this will help.

from LogInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Outputting $@ to log.", source.getNode(),
"sensitive information"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is a left-over from another query?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right! I was inspired by a similar query (SensitiveInfoLog.ql). Thanks for pointing this out. I fixed the message.

Copy link
Contributor

@Marcono1234 Marcono1234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#3090 already added some logger types and #3600 will add more.
Maybe it would make sense to define logger types at one place (though in a later pull request?) to prevent code duplication.
For example what about:

abstract class LoggerType extends RefType {
  abstract Method getALoggingMethod();
}

Where getALoggingMethod() then returns the respective logging methods for that type.
What do the maintainers of this project think about this?

Comment on lines 33 to 40
<p>In the example, a username, provided by the user, is logged using `logger.warn` (from `org.slf4j.Logger`).
In the first case (`\bad` endpoint), the username is logged without any sanitization.
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
the log entry will be splitted in two different lines, where the first line will be `User:'Guest', while the second one will be `User:'Admin'`.
</p>
<p> In the second case (`\good` endpoint), <code>replace</code> is used to ensure no line endings are present in the user input.
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
the log entry will not be splitted in two different lines, resulting in a single line `User:'Guest'User:'Admin'`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does ` work for formatting text as code, or does <code>...</code> have to be used?
Either way, to be consistent, it would probably be good to use <code> for formatting.

</p>
<p> In the second case (`\good` endpoint), <code>replace</code> is used to ensure no line endings are present in the user input.
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
the log entry will not be splitted in two different lines, resulting in a single line `User:'Guest'User:'Admin'`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two different lines

"two separate lines" might be more correct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I made the change.

<p>In the example, a username, provided by the user, is logged using `logger.warn` (from `org.slf4j.Logger`).
In the first case (`\bad` endpoint), the username is logged without any sanitization.
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter,
the log entry will be splitted in two different lines, where the first line will be `User:'Guest', while the second one will be `User:'Admin'`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

splitted in two different lines

"two separate lines" might be more correct

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I made the change.

import semmle.code.java.dataflow.FlowSources

module LogInjection {
string logLevel() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JBoss Logging also has ...f and ...v variants.

* A class of popular logging utilities.
*/
class LoggingType extends RefType {
LoggingType() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably be good to cover the following ones as well:

And with that many types it might be good to add comments here explaining which type belongs to which framework, since especially for Log4j1, Log4j2 and Apache Commons Logging it can become quite confusing.


class TypeSanitizer extends Sanitizer {
TypeSanitizer() {
this.getType() instanceof PrimitiveType or this.getType() instanceof BoxedType
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A primitive type could be '\n' which is still problematic and not a sanitizier I assume

// /good?username=Guest'%0AUser:'Admin
@GetMapping("/good")
public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
username = username.replace("\n", "");
Copy link
Contributor

@Marcono1234 Marcono1234 Jul 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be good if a sanitizer would detect this.
Though it might be difficult to reduce false positives without introducing false negatives at the same time.

public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
username = username.replace("\n", "");
log.warn("User:'{}'", username);
return new String(username);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return a new string (same in line 27)? String is immutable so there is no need to create a copy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I was doing some experiments and I forgot to change the return type. I made the change. Thanks for all the other feedback. I really appreciated. I will do the other changes/suggestions as soon as I will have some time (unfortunately I am a little bit busy at the moment).

// /good?username=Guest'%0AUser:'Admin
@GetMapping("/good")
public String good(@RequestParam(value = "username", defaultValue = "name") String username) {
username = username.replace("\n", "");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing \n will likely not be enough, you probably also have to replace \r

@dellalibera
Copy link
Contributor Author

Hi @Marcono1234,
sorry for the long delay.
I realized that I need more experience in Java CodeQL, and at this moment I don't have so much "free" time to spend on it.
Also during the "free" time that I have, I prefer to continue focusing on CodeQL for JS.

This mean that I will not be able to continue to work on this query in the next days/weeks/months.
Let me know if it's better to close this pull requests, for me there is no problem.
In this way I could reopen it later (if I will decide to work on it again) or maybe someone else could work on this.

Thank you for all the feedback and sorry for the inconvenience.

@Marcono1234
Copy link
Contributor

No problem and no worries. Note that I am not a member of this project so they might have different requirements for the queries. My review comments were mostly improvement suggestions.
I think leaving the pull request open for now should be fine (though maybe the project members disagree?). In case you consider the query incomplete yourself, you can also convert the pull request to a draft to indicate that.

@adityasharad adityasharad changed the base branch from master to main August 14, 2020 18:33
Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a fine port of https://github.com/github/codeql/blob/main/csharp/ql/src/Security%20Features/CWE-117/LogForging.ql -- since my review suggests factoring in an existing logger definition with much more coverage, I'll wait for you to respond (either using it or opting to leave that until after merging to experimental) before I start an evaluation for this query.

</example>

<references>
<li>OWASP: <a href="https://www.owasp.org/index.php/Log_Injection">Log Injection</a>.</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend linking straight to the new URL (https://owasp.org/www-community/attacks/Log_Injection) -- perhaps update other links in other qhelp files at the same time.

Comment on lines +10 to +14
result = "trace" or
result = "info" or
result = "warn" or
result = "error" or
result = "fatal"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
result = "trace" or
result = "info" or
result = "warn" or
result = "error" or
result = "fatal"
result in ["trace", "info", "warn", "error", "fatal"]

/**
* A class of popular logging utilities.
*/
class LoggingType extends RefType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining this, suggest factoring out https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql#L31 as well as the logging methods used there (though we may not need the restriction to only low-priority messages seen in that query)

I'd say it belongs at java/ql/src/semmle/code/java/dataflow/FlowSinks.qll, alongside the existing FlowSources.qll.

* An argument to a logging mechanism.
*/
class LoggingSink extends Sink {
LoggingSink() { this.asExpr() = any(LoggingCall console).getAnArgument() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LoggingSink() { this.asExpr() = any(LoggingCall console).getAnArgument() }
LoggingSink() { this.asExpr() = any(LoggingCall lc).getAnArgument() }

Since it's not necessarily a console

@smowton smowton self-assigned this Oct 20, 2020
@smowton
Copy link
Contributor

smowton commented Nov 6, 2020

@dellalibera are you still working on this one?

@dellalibera
Copy link
Contributor Author

@smowton I apologize for the late reply. At the moment I'm very busy. To answer your question: I will not have enough time now to work on this PR (and also on others). Feel free to close it or reuse the code as a starting point for another PR.

@smowton
Copy link
Contributor

smowton commented Nov 7, 2020

If you're likely to come back to it at some point I'm happy to leave open; if not then you can close it.

@dellalibera
Copy link
Contributor Author

Ok great. If in the next few months I will not come back on it, I will let know so we can close it. But for now, for me it's ok to leave it open.

@atorralba
Copy link
Contributor

Superseded by #5099.

@atorralba atorralba closed this Nov 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants