Skip to content
This repository has been archived by the owner on Oct 27, 2021. It is now read-only.

Commit

Permalink
Merge pull request #40 from maltek/file-path-race
Browse files Browse the repository at this point in the history
query for file path races
  • Loading branch information
maltek authored Apr 14, 2021
2 parents f1a9811 + 53e10a1 commit 39e93d6
Showing 1 changed file with 96 additions and 0 deletions.
96 changes: 96 additions & 0 deletions src/main/scala/io/joern/scanners/c/FileOpRace.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.joern.scanners.c

import io.joern.scanners.Crew
import io.shiftleft.codepropertygraph.generated.nodes._
import io.shiftleft.console._
import io.shiftleft.semanticcpg.language._
import io.shiftleft.dataflowengineoss.queryengine.EngineContext
import overflowdb.traversal.Traversal

object FileOpRace extends QueryBundle {

@q
def fileOperationRace()(implicit context: EngineContext): Query = Query(
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,
traversal = { cpg =>
{
val firstParam = Set(
"open",
"fopen",
"creat",
"access",
"chmod",
"readlink",
"chown",
"lchown",
"stat",
"lstat",
"unlink",
"rmdir",
"mkdir",
"mknod",
"mkfifo",
"chdir",
"link",
"rename"
)
val secondParam = Set(
"openat",
"fstatat",
"fchmodat",
"readlinkat",
"unlinkat",
"mkdirat",
"mknodat",
"mkfifoat",
"faccessat",
"link",
"rename",
"linkat",
"renameat"
)
val fourthParam = Set("linkat", "renameat")

val anyParam = firstParam ++ secondParam ++ fourthParam

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

def fileArgs(c: Call) = {
val res = Traversal.newBuilder[Expression]
// note some functions are in multiple setts because they take multiple paths
if (firstParam.contains(c.name)) {
res.addOne(c.argument(1))
}
if (secondParam.contains(c.name)) {
res.addOne(c.argument(2))
}
if (fourthParam.contains(c.name)) {
res.addOne(c.argument(4))
}
res.result().whereNot(_.isLiteral)
}

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))
})
}
}
)

}

0 comments on commit 39e93d6

Please sign in to comment.