|
|
Message-ID: <1167011785.77274.1740140560949@privateemail.com>
Date: Fri, 21 Feb 2025 13:22:40 +0100 (CET)
From: Jordy Zomer <jordy@...ing.systems>
To: "oss-security@...ts.openwall.com" <oss-security@...ts.openwall.com>,
"qsa@...lys.com" <qsa@...lys.com>
Subject: Re: MitM attack against OpenSSH's
VerifyHostKeyDNS-enabled client
Hey all,
First of all, cool findings! I've been working on the CodeQL query and have a revised version that I think improves accuracy and might offer some performance gains (though I haven't done rigorous benchmarking). The key change is the use of `StackVariableReachability` and making sure that there's a path where `var` is not reassigned before taking a `goto _;`. Ran it on an older database, found some of the same bugs with no false-positives so far.
This is the revised query.
```
import cpp
import semmle.code.cpp.controlflow.StackVariableReachability
// A call that can return 0
class CallReturningOK extends FunctionCall {
CallReturningOK() {
exists(ReturnStmt ret | this.getTarget() = ret.getEnclosingFunction() and ret.getExpr().getValue().toInt() = 0)
}
}
class GotoWrongRetvalConfiguration extends StackVariableReachability {
GotoWrongRetvalConfiguration() { this = "GotoWrongRetvalConfiguration" }
// Source is an assigment of an "OK" return value to an access of v
// To not get FP's we get a false successor
override predicate isSource(ControlFlowNode node, StackVariable v) {
exists(AssignExpr ae, IfStmt ifst | ae.getRValue() instanceof CallReturningOK
and v.getAnAccess() = ae.getLValue() and ifst.getCondition().getAChild() = ae and
ifst.getCondition().getAFalseSuccessor() = node)
}
// Our intermediate sink is a `goto _` statement, but it should have a successor that's a return of `v`
override predicate isSink(ControlFlowNode node, StackVariable v) {
exists(ReturnStmt ret | ret.getExpr() = v.getAnAccess() and
node instanceof GotoStmt and node.getASuccessor+() = ret.getExpr())
}
// We don't want `v` to be reassigned
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
exists(AssignExpr ae | ae.getLValue() = node and v.getAnAccess() = node)
}
}
from ControlFlowNode source, ControlFlowNode sink, GotoWrongRetvalConfiguration conf, Variable v, Expr retval
where
// We want a call that can `return 0` to reach a goto that has a ret of `v` sucessor
conf.reaches(source, v, sink)
and
// We don't want `v` to be reassigned after the goto
not conf.isBarrier(sink.getASuccessor+(), v)
// this goes from our intermediate sink to retval
and sink.getASuccessor+() = retval
// Just making sure that it's returning v
and exists(ReturnStmt ret | ret.getExpr() = retval and retval = v.getAnAccess())
select retval.getEnclosingFunction(), source, sink, retval
```
Hope that's helpful, please reach out if you have any questions :)
Cheers,
Jordy Zomer
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.