All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.scanners.c.FileOpRace.scala Maven / Gradle / Ivy

There is a newer version: 1.2.44
Show newest version
package io.joern.scanners.c

import io.joern.scanners.{Crew, QueryTags}
import io.shiftleft.codepropertygraph.generated.nodes._
import io.joern.console._
import io.joern.console._
import io.joern.dataflowengineoss.queryengine.EngineContext
import io.joern.dataflowengineoss.semanticsloader.Semantics
import io.shiftleft.semanticcpg.language._
import io.joern.macros.QueryMacros._
import overflowdb.traversal.Traversal

object FileOpRace extends QueryBundle {

  @q
  def fileOperationRace()(implicit context: EngineContext): Query = {

    Query.make(
      name = "file-operation-race",
      author = Crew.malte,
      title = "Two file operations on the same path can act on different files",
      description = """
        |Two subsequent file operations are performed on the same path. Depending on the permissions
        |on this path, an attacker can exploit a race condition and replace the file or directory
        |the path refers to between these calls.
        |Use file operations based on file descriptor/pointer/handles instead of paths to avoid this issue.
        |""".stripMargin,
      score = 3.0,
      withStrRep({ cpg =>
        val operations: Map[String, Seq[Int]] = Map(
          "access" -> Seq(1),
          "chdir" -> Seq(1),
          "chmod" -> Seq(1),
          "chown" -> Seq(1),
          "creat" -> Seq(1),
          "faccessat" -> Seq(2),
          "fchmodat" -> Seq(2),
          "fopen" -> Seq(1),
          "fstatat" -> Seq(2),
          "lchown" -> Seq(1),
          "linkat" -> Seq(2, 4),
          "link" -> Seq(1, 2),
          "lstat" -> Seq(1),
          "mkdirat" -> Seq(2),
          "mkdir" -> Seq(1),
          "mkfifoat" -> Seq(2),
          "mkfifo" -> Seq(1),
          "mknodat" -> Seq(2),
          "mknod" -> Seq(1),
          "openat" -> Seq(2),
          "open" -> Seq(1),
          "readlinkat" -> Seq(2),
          "readlink" -> Seq(1),
          "renameat" -> Seq(2, 4),
          "rename" -> Seq(1, 2),
          "rmdir" -> Seq(1),
          "stat" -> Seq(1),
          "unlinkat" -> Seq(2),
          "unlink" -> Seq(1)
        )

        def fileCalls(calls: Traversal[Call]) =
          calls.nameExact(operations.keys.toSeq: _*)

        def fileArgs(c: Call) =
          c.argument.whereNot(_.isLiteral).argumentIndex(operations(c.name): _*)

        fileCalls(cpg.call)
          .filter(call => {
            val otherCalls = fileCalls(call.method.ast.isCall).filter(_ != call)
            val argsForOtherCalls =
              otherCalls.flatMap(c => fileArgs(c)).code.toSet

            fileArgs(call).code.exists(arg => argsForOtherCalls.contains(arg))
          })
      }),
      tags = List(QueryTags.raceCondition, QueryTags.default),
      codeExamples = CodeExamples(
        List("""
          |
          |void insecure_race(char *path) {
          |    chmod(path, 0);
          |    rename(path, "/some/new/path");
          |}
          |
          |""".stripMargin),
        List("""
          |
          |void secure_handle(char *path) {
          |    FILE *file = fopen(path, "r");
          |    fchown(fileno(file), 0, 0);
          |}
          |
          |""".stripMargin)
      )
    )
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy