Skip to content

Commit a130224

Browse files
authored
Merge pull request #287 from microsoft/add-zipslip-query-ps
PS: Add ZipSlip query
2 parents 7d9a77a + abf320b commit a130224

File tree

23 files changed

+581
-45
lines changed

23 files changed

+581
-45
lines changed

powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,15 @@ module API {
249249
result = this.getContent(contents.getAReadContent())
250250
}
251251

252+
/**
253+
* Gets a representative for the instanceof field of the given `name`.
254+
*/
255+
pragma[inline]
256+
Node getField(string name) {
257+
// This predicate is currently not 'inline_late' because 'name' can be an input or output
258+
Impl::fieldEdge(this.getAnEpsilonSuccessor(), name, result)
259+
}
260+
252261
/**
253262
* Gets a representative for an arbitrary element of this collection.
254263
*/
@@ -615,6 +624,15 @@ module API {
615624
contentEdge(pred, any(DataFlow::ContentSet set | set.isAnyElement()).getAReadContent(), succ)
616625
}
617626

627+
cached
628+
predicate fieldEdge(Node pred, string name, Node succ) {
629+
exists(DataFlow::ContentSet set, DataFlow::Content::FieldContent fc |
630+
fc.getLowerCaseName() = name and
631+
set.isSingleton(fc) and
632+
contentEdge(pred, set.getAReadContent(), succ)
633+
)
634+
}
635+
618636
cached
619637
predicate parameterEdge(Node pred, DataFlowDispatch::ParameterPosition paramPos, Node succ) {
620638
exists(DataFlowPrivate::ParameterNodeImpl parameter, DataFlow::CallableNode callable |

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,25 @@ abstract private class ChildMapping extends Ast {
7272
*/
7373
abstract predicate relevantChild(Ast child);
7474

75+
/**
76+
* Holds if `child` appears before its parent in the control-flow graph.
77+
* This always holds for expressions, and _almost_ never for statements.
78+
*/
79+
abstract predicate precedesParent(Ast child);
80+
7581
pragma[nomagic]
76-
abstract predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb);
82+
final predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
83+
this.relevantChild(child) and
84+
cfn.getAstNode() = this and
85+
bb.getANode() = cfn
86+
or
87+
exists(BasicBlock mid |
88+
this.reachesBasicBlock(child, cfn, mid) and
89+
not mid.getANode().getAstNode() = child
90+
|
91+
if this.precedesParent(child) then bb = mid.getAPredecessor() else bb = mid.getASuccessor()
92+
)
93+
}
7794

7895
/**
7996
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
@@ -93,18 +110,7 @@ abstract private class ChildMapping extends Ast {
93110
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
94111
*/
95112
abstract private class ExprChildMapping extends Expr, ChildMapping {
96-
pragma[nomagic]
97-
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
98-
this.relevantChild(child) and
99-
cfn.getAstNode() = this and
100-
bb.getANode() = cfn
101-
or
102-
exists(BasicBlock mid |
103-
this.reachesBasicBlock(child, cfn, mid) and
104-
bb = mid.getAPredecessor() and
105-
not mid.getANode().getAstNode() = child
106-
)
107-
}
113+
final override predicate precedesParent(Ast child) { this.relevantChild(child) }
108114
}
109115

110116
/**
@@ -113,18 +119,7 @@ abstract private class ExprChildMapping extends Expr, ChildMapping {
113119
abstract private class NonExprChildMapping extends ChildMapping {
114120
NonExprChildMapping() { not this instanceof Expr }
115121

116-
pragma[nomagic]
117-
override predicate reachesBasicBlock(Ast child, CfgNode cfn, BasicBlock bb) {
118-
this.relevantChild(child) and
119-
cfn.getAstNode() = this and
120-
bb.getANode() = cfn
121-
or
122-
exists(BasicBlock mid |
123-
this.reachesBasicBlock(child, cfn, mid) and
124-
bb = mid.getASuccessor() and
125-
not mid.getANode().getAstNode() = child
126-
)
127-
}
122+
override predicate precedesParent(Ast child) { none() } // this is not final because it is overriden by ForEachStmt
128123
}
129124

130125
private class AttributeBaseChildMapping extends NonExprChildMapping, AttributeBase {
@@ -1208,7 +1203,7 @@ module StmtNodes {
12081203
StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) }
12091204
}
12101205

1211-
private class LoopStmtChildMapping extends NonExprChildMapping, LoopStmt {
1206+
abstract private class LoopStmtChildMapping extends ChildMapping, LoopStmt {
12121207
override predicate relevantChild(Ast child) { child = this.getBody() }
12131208
}
12141209

@@ -1222,7 +1217,9 @@ module StmtNodes {
12221217
StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) }
12231218
}
12241219

1225-
private class DoUntilStmtChildMapping extends LoopStmtChildMapping, DoUntilStmt {
1220+
private class DoUntilStmtChildMapping extends LoopStmtChildMapping, NonExprChildMapping,
1221+
DoUntilStmt
1222+
{
12261223
override predicate relevantChild(Ast child) {
12271224
child = this.getCondition() or super.relevantChild(child)
12281225
}
@@ -1238,7 +1235,9 @@ module StmtNodes {
12381235
ExprCfgNode getCondition() { s.hasCfgChild(s.getCondition(), this, result) }
12391236
}
12401237

1241-
private class DoWhileStmtChildMapping extends LoopStmtChildMapping, DoWhileStmt {
1238+
private class DoWhileStmtChildMapping extends LoopStmtChildMapping, NonExprChildMapping,
1239+
DoWhileStmt
1240+
{
12421241
override predicate relevantChild(Ast child) {
12431242
child = this.getCondition() or super.relevantChild(child)
12441243
}
@@ -1300,10 +1299,14 @@ module StmtNodes {
13001299
ExprCfgNode getHashTableExpr() { s.hasCfgChild(s.getHashTableExpr(), this, result) }
13011300
}
13021301

1303-
private class ForEachStmtChildMapping extends LoopStmtChildMapping, ForEachStmt {
1302+
private class ForEachStmtChildMapping extends LoopStmtChildMapping, NonExprChildMapping,
1303+
ForEachStmt
1304+
{
13041305
override predicate relevantChild(Ast child) {
13051306
child = this.getVarAccess() or child = this.getIterableExpr() or super.relevantChild(child)
13061307
}
1308+
1309+
override predicate precedesParent(Ast child) { child = this.getIterableExpr() }
13071310
}
13081311

13091312
class ForEachStmtCfgNode extends LoopStmtCfgNode {
@@ -1313,12 +1316,12 @@ module StmtNodes {
13131316

13141317
override ForEachStmt getStmt() { result = s }
13151318

1316-
ExprCfgNode getVarAccess() { s.hasCfgChild(s.getVarAccess(), this, result) }
1319+
ExprNodes::VarAccessCfgNode getVarAccess() { s.hasCfgChild(s.getVarAccess(), this, result) }
13171320

13181321
ExprCfgNode getIterableExpr() { s.hasCfgChild(s.getIterableExpr(), this, result) }
13191322
}
13201323

1321-
private class ForStmtChildMapping extends LoopStmtChildMapping, ForStmt {
1324+
private class ForStmtChildMapping extends LoopStmtChildMapping, NonExprChildMapping, ForStmt {
13221325
override predicate relevantChild(Ast child) {
13231326
child = this.getInitializer() or
13241327
child = this.getCondition() or
@@ -1476,7 +1479,7 @@ module StmtNodes {
14761479
override UsingStmt getStmt() { result = s }
14771480
}
14781481

1479-
private class WhileStmtChildMapping extends LoopStmtChildMapping, WhileStmt {
1482+
private class WhileStmtChildMapping extends LoopStmtChildMapping, NonExprChildMapping, WhileStmt {
14801483
override predicate relevantChild(Ast child) {
14811484
child = this.getCondition() or
14821485
super.relevantChild(child)

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,13 +1025,15 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
10251025
* Holds if there is a read step of content `c` from `node1` to `node2`.
10261026
*/
10271027
predicate readStep(Node node1, ContentSet c, Node node2) {
1028+
// Qualifier -> Member read
10281029
exists(CfgNodes::ExprNodes::MemberExprReadAccessCfgNode var, Content::FieldContent fc |
10291030
node2.asExpr() = var and
10301031
node1.asExpr() = var.getQualifier() and
10311032
fc.getLowerCaseName() = var.getLowerCaseMemberName() and
10321033
c.isSingleton(fc)
10331034
)
10341035
or
1036+
// Qualifier -> Index read
10351037
exists(CfgNodes::ExprNodes::IndexExprReadAccessCfgNode var, CfgNodes::ExprCfgNode e |
10361038
node2.asExpr() = var and
10371039
node1.asExpr() = var.getBase() and
@@ -1046,32 +1048,48 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
10461048
c.isAnyElement()
10471049
)
10481050
or
1051+
// Implicit read before a return
10491052
exists(CfgNode cfgNode |
10501053
node1 = TPreReturnNodeImpl(cfgNode, true) and
10511054
node2 = TImplicitWrapNode(cfgNode, true) and
10521055
c.isSingleton(any(Content::KnownElementContent ec | exists(ec.getIndex().asInt())))
10531056
)
10541057
or
1058+
// Implicit read before a process block
10551059
c.isAnyPositional() and
10561060
exists(CfgNodes::ProcessBlockCfgNode processBlock |
10571061
processBlock.getPipelineParameterAccess() = node1.asExpr() and
10581062
node2 = TProcessNode(processBlock)
10591063
)
10601064
or
1065+
// Implicit read of a positional before a property-by-name process iteration
10611066
c.isAnyPositional() and
10621067
exists(CfgNodes::ProcessBlockCfgNode pb, CfgNodes::ExprNodes::VarReadAccessCfgNode va |
10631068
va = pb.getAPipelineByPropertyNameParameterAccess() and
10641069
node1.asExpr() = va and
10651070
node2 = TProcessPropertyByNameNode(va.getVariable(), false)
10661071
)
10671072
or
1073+
// Implicit read of a property before a property-by-name process iteration
10681074
exists(PipelineByPropertyNameParameter p, Content::KnownElementContent ec |
10691075
c.isKnownOrUnknownElement(ec) and
10701076
ec.getIndex().asString() = p.getLowerCaseName() and
10711077
node1 = TProcessPropertyByNameNode(p, false) and
10721078
node2 = TProcessPropertyByNameNode(p, true)
10731079
)
10741080
or
1081+
// Read from a collection into a `foreach` loop
1082+
exists(
1083+
CfgNodes::StmtNodes::ForEachStmtCfgNode forEach, Content::KnownElementContent ec, BasicBlock bb,
1084+
int i
1085+
|
1086+
c.isKnownOrUnknownElement(ec) and
1087+
node1.asExpr() = forEach.getIterableExpr() and
1088+
bb.getNode(i) = forEach.getVarAccess() and
1089+
node2.asDefinition().definesAt(_, bb, i)
1090+
)
1091+
or
1092+
// Summary read steps
10751093
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
10761094
node2.(FlowSummaryNode).getSummaryNode())
10771095
}

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import powershell
22
private import DataFlowDispatch
33
private import DataFlowPrivate
4+
private import semmle.code.powershell.dataflow.Ssa
45
private import semmle.code.powershell.typetracking.internal.TypeTrackingImpl
56
private import semmle.code.powershell.ApiGraphs
67
private import semmle.code.powershell.Cfg
@@ -13,6 +14,9 @@ class Node extends TNode {
1314
/** Gets the expression corresponding to this node, if any. */
1415
CfgNodes::ExprCfgNode asExpr() { result = this.(ExprNode).getExprNode() }
1516

17+
/** Gets the definition corresponding to this node, if any. */
18+
Ssa::Definition asDefinition() { result = this.(SsaDefinitionNodeImpl).getDefinition() }
19+
1620
ScriptBlock asCallable() { result = this.(CallableNode).asCallableAstNode() }
1721

1822
/** Gets the parameter corresponding to this node, if any. */
@@ -477,14 +481,10 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
477481
*
478482
* For example, `[Foo]::new()` or `New-Object Foo`.
479483
*/
480-
class ObjectCreationNode extends ExprNode {
481-
CfgNodes::ExprNodes::ObjectCreationCfgNode objectCreation;
482-
483-
ObjectCreationNode() { this.getExprNode() = objectCreation }
484+
class ObjectCreationNode extends CallNode {
485+
override CfgNodes::ExprNodes::ObjectCreationCfgNode call;
484486

485-
final CfgNodes::ExprNodes::ObjectCreationCfgNode getObjectCreationNode() {
486-
result = objectCreation
487-
}
487+
final CfgNodes::ExprNodes::ObjectCreationCfgNode getObjectCreationNode() { result = call }
488488

489489
/**
490490
* Gets the node corresponding to the expression that decides which type
@@ -493,7 +493,7 @@ class ObjectCreationNode extends ExprNode {
493493
* For example, in `[Foo]::new()`, this would be `Foo`, and in
494494
* `New-Object Foo`, this would be `Foo`.
495495
*/
496-
Node getConstructedTypeNode() { result.asExpr() = objectCreation.getConstructedTypeExpr() }
496+
Node getConstructedTypeNode() { result.asExpr() = call.getConstructedTypeExpr() }
497497

498498
bindingset[result]
499499
pragma[inline_late]

powershell/ql/lib/semmle/code/powershell/frameworks/Microsoft.PowerShell.model.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ extensions:
4444
- ["microsoft.powershell.utility!", "Method[format-wide]", "Argument[-inputobject,pipeline]", "ReturnValue", "taint"]
4545
- ["microsoft.powershell.utility!", "Method[get-unique]", "Argument[-inputobject,pipeline]", "ReturnValue", "taint"]
4646
- ["microsoft.powershell.utility!", "Method[join-string]", "Argument[-inputobject,pipeline]", "ReturnValue", "taint"]
47+
- ["microsoft.powershell.management!", "Method[join-path]", "Argument[-path,0]", "ReturnValue", "taint"]
48+
- ["microsoft.powershell.management!", "Method[join-path]", "Argument[-childpath,1]", "ReturnValue", "taint"]
49+
- ["microsoft.powershell.management!", "Method[join-path]", "Argument[-additionalchildpath,2]", "ReturnValue", "taint"]
4750

4851
- addsTo:
4952
pack: microsoft/powershell-all

powershell/ql/lib/semmle/code/powershell/frameworks/System.IO.model.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ extensions:
3030
- ["system.io.filestream", "Instance", "file"]
3131
- ["system.io.filestream", "Instance", "file-write"]
3232
- ["system.io.streamwriter", "Instance", "file-write"]
33-
- ["system.io.streamwriter", "Instance", "file-write"]
33+
- ["system.io.streamwriter", "Instance", "file-write"]
34+
35+
- addsTo:
36+
pack: microsoft/powershell-all
37+
extensible: summaryModel
38+
data:
39+
- ["system.io.path!", "Method[getfullpath]", "Argument[0]", "ReturnValue", "taint"]

0 commit comments

Comments
 (0)