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

Potential path traversal when using filename from Zip archive #514

Open
h3xstream opened this issue Oct 3, 2019 · 12 comments
Open

Potential path traversal when using filename from Zip archive #514

h3xstream opened this issue Oct 3, 2019 · 12 comments

Comments

@h3xstream
Copy link
Member

h3xstream commented Oct 3, 2019

Description

The class ZipEntry describe one file inside a Zip archive. getName() return the file name from this file. It could contain a malicious string such as "../../../". If the value is used to build a file path, it could lead to a path traversal attack. An attacker would be able to create file or override files with the content of his own.

Good References:

Vulnerable code

Enumeration entries = zip.entries();
while (entries.hasMoreElements()) {
    ZipEntry zipEntry = entries.nextElement();
    File file = new File(outputDir, zipEntry.getName())
[...]

API to cover :

  • java.util.zip.ZipEntry.getName()
  • org.apache.commons.compress.archivers.ar.ArArchiveEntry.getName()
  • org.apache.commons.compress.archivers.arj.ArjArchiveEntry.getName()
  • org.apache.commons.compress.archivers.cpio.CpioArchiveEntry.getName()
  • org.apache.commons.compress.archivers.dump.DumpArchiveEntry.getName()
  • org.apache.commons.compress.archivers.jar.JarArchiveEntry.getName()
  • org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry.getName()
  • org.apache.commons.compress.archivers.tar.TarArchiveEntry.getName()
  • org.apache.commons.compress.archivers.zip.ZipArchiveEntry.getName()
  • org.apache.commons.compress.archivers.ArchiveEntry.getName()

Similar detector

If anybody is interested in the implementation of such rule. It is very similar to the rule to detect FileItem.getName() from multipart upload.

public class FileUploadFilenameDetector extends OpcodeStackDetector {
private static String FILE_UPLOAD_FILENAME_TYPE = "FILE_UPLOAD_FILENAME";
private BugReporter bugReporter;
public FileUploadFilenameDetector(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
@Override
public void sawOpcode(int seen) {
if (seen == Const.INVOKEINTERFACE &&
(getClassConstantOperand().equals("org/apache/wicket/util/upload/FileItem") ||
getClassConstantOperand().equals("org/apache/commons/fileupload/FileItem")) &&
getNameConstantOperand().equals("getName")) {
bugReporter.reportBug(new BugInstance(this, FILE_UPLOAD_FILENAME_TYPE, Priorities.NORMAL_PRIORITY) //
.addClass(this).addMethod(this).addSourceLine(this));
}
}

@h3xstream
Copy link
Member Author

Bonus : Could be implemented in the same enhancements..
zip4j is a popular library. I think that the equivalent concept would be AbstractFileHeader.getFileName()

@thiyagu-7
Copy link
Contributor

Can I take this up? :)

@h3xstream
Copy link
Member Author

@thiyagu-7 Yes

@thiyagu-7
Copy link
Contributor

I modified one of the existing test classes adding

Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
    ZipEntry e = entries.nextElement();
    File f = new File("C:/Code/", e.getName());
}

This raised PATH_TRAVERSAL_IN bug as implemented by PathTraversalDetector.

New Bug Instance: [1]
  message=SECPTI: This API (java/io/File.<init>(Ljava/lang/String;Ljava/lang/String;)V) reads a file whose location might be specified by user input
  bugType=PATH_TRAVERSAL_IN  priority=Medium  category=S
  class=testcode.FileUploadCommon  method=handleFile  line=33
  sources=[[java/util/zip/ZipEntry.getName()Ljava/lang/String;]]

Would this then also cover the ZipEntry cases too? 🤔

@h3xstream
Copy link
Member Author

h3xstream commented Oct 7, 2019

@thiyagu-7 Yes the fact that there is both a PATH_TRAVERSAL_IN and the new ZIP_ENTRY_NAME? are being raised is perfect.

Both have a different purpose. ZipEntry.getName() is the risky source. File() is the method from which malicious value should not reach unfiltered (sink).

@h3xstream
Copy link
Member Author

This issue is up for grab.

@akshaycbor
Copy link

Can I take it?

@akshaycbor
Copy link

How do I detect that the value read in getName reaches the File() (sink) unfiltered?

@h3xstream
Copy link
Member Author

h3xstream commented Dec 2, 2019

Can I take it?

@akshaycbor Yes

How do I detect that the value read in getName reaches the File() (sink) unfiltered?

In this specific case, you can do two things.

  • Report a Low Bug instance (informational) to highlight to the reviewer that it is a risky source.
  • You could mark the return value as tainted. (Example) This has the potention of rising the severity from Normal to High for a path traversal bug.

@akshaycbor
Copy link

@h3xstream Maybe a silly question but how do I get a single test to run. Whenever I try to run a single test using mvn -Dtest=FileUploadFilenameDetectorTest test it fails

@h3xstream
Copy link
Member Author

h3xstream commented Dec 5, 2019

@akshaycbor Running the test individually also failed on my side. I did not find a solution to run a single test with Maven.

I have mostly use Maven to run the complete suite and use my IDE IntelliJ to run the test individually.

I will investigated this later.

@Ayan07
Copy link

Ayan07 commented Jun 18, 2023

@h3xstream I 'm interested to work on this issue.
I see this issue is currently unassigned.
Can I take this up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants