Skip to content

Commit 9877294

Browse files
committed
V2, this version is really better than V1
1 parent 1936935 commit 9877294

1 file changed

Lines changed: 159 additions & 60 deletions

File tree

java/ql/src/experimental/Security/CWE/CWE-522-DecompressionBombs/DecompressionBomb.ql

Lines changed: 159 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -293,12 +293,25 @@ module Zip4j {
293293
call.(MethodAccess).getReceiverType() instanceof TypeZipInputStream
294294
) and
295295
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
296-
call.getArgument(0) = n1.asExpr() and
297-
call = n2.asExpr()
296+
call.getArgument(0) = n2.asExpr() and
297+
call.getQualifier() = n1.asExpr()
298298
)
299299
}
300300
}
301301

302+
module CommonsIO {
303+
class IOUtils extends MethodAccess {
304+
IOUtils() {
305+
this.getMethod()
306+
.hasName([
307+
"copy", "copyLarge", "read", "readFully", "readLines", "toBufferedInputStream",
308+
"toByteArray", "toCharArray", "toString", "buffer"
309+
]) and
310+
this.getMethod().getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils")
311+
}
312+
}
313+
}
314+
302315
module Zip {
303316
class TypeInputStream extends RefType {
304317
TypeInputStream() {
@@ -308,10 +321,42 @@ module Zip {
308321
}
309322
}
310323

324+
class ReadInputStreamCall extends MethodAccess {
325+
ReadInputStreamCall() {
326+
this.getReceiverType() instanceof TypeInputStream and
327+
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
328+
}
329+
330+
Expr getAWriteArgument() { result = this.getArgument(0) }
331+
332+
// look at Zip4j comments for this method
333+
predicate isControlledRead() { none() }
334+
}
335+
336+
// *InputStream Izis = new *InputStream(inputStream)
337+
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
338+
exists(Call call |
339+
(
340+
call.getCallee().getDeclaringType() instanceof TypeInputStream or
341+
call.(MethodAccess).getReceiverType() instanceof TypeInputStream
342+
) and
343+
call.getArgument(0) = n1.asExpr() and
344+
call = n2.asExpr()
345+
)
346+
}
347+
311348
class TypeInflator extends RefType {
312349
TypeInflator() { this.hasQualifiedName("java.util.zip", "Inflater") }
313350
}
314351

352+
class Inflatorsource extends Call {
353+
Inflatorsource() {
354+
exists(Call c | c.getCallee().(Constructor).getDeclaringType() instanceof TypeInflator |
355+
this = c
356+
)
357+
}
358+
}
359+
315360
predicate inflatorAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
316361
// inflater.inflate(n2)
317362
exists(MethodAccess ma |
@@ -343,48 +388,78 @@ module Zip {
343388
predicate isControlledRead() { none() }
344389
}
345390

346-
// InflaterInputStream Izis = new InflaterInputStream(inputStream)
347-
predicate inputStreamAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
348-
exists(Call call |
349-
(
350-
call.getCallee().getDeclaringType() instanceof TypeInputStream or
351-
call.(MethodAccess).getReceiverType() instanceof TypeInputStream
352-
) and
353-
call.getArgument(0) = n1.asExpr() and
354-
call = n2.asExpr()
355-
)
391+
class TypeZipFile extends RefType {
392+
TypeZipFile() { this.hasQualifiedName("java.util.zip", "ZipFile") }
356393
}
357394

358-
class ReadInputStreamCall extends MethodAccess {
359-
ReadInputStreamCall() {
360-
this.getReceiverType() instanceof TypeInputStream and
361-
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
395+
class ZipFilesource extends Call {
396+
ZipFilesource() {
397+
exists(Call c | c.getCallee().(Constructor).getDeclaringType() instanceof TypeZipFile |
398+
this = c
399+
)
362400
}
401+
}
363402

364-
Expr getAWriteArgument() { result = this.getArgument(0) }
365-
366-
// look at Zip4j comments for this method
367-
predicate isControlledRead() { none() }
403+
// ZipFile zipFileAsn2 = new ZipFile(n1);
404+
// InputStream n2 = zipFile.getInputStream(n1);
405+
predicate zipFileadditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
406+
exists(MethodAccess ma |
407+
ma.getReceiverType() instanceof TypeZipFile and
408+
ma = n2.asExpr() and
409+
ma.getQualifier() = n1.asExpr() and
410+
ma.getCallee().hasName("getInputStream")
411+
)
412+
or
413+
exists(Call c |
414+
c.getCallee().getDeclaringType() instanceof TypeZipFile and
415+
c.getArgument(0) = n1.asExpr() and
416+
c = n2.asExpr()
417+
)
368418
}
369419
}
370420

371-
module CommonsIO {
372-
class IOUtils extends MethodAccess {
373-
IOUtils() {
374-
this.getMethod()
375-
.hasName([
376-
"copy", "copyLarge", "read", "readFully", "readLines", "toBufferedInputStream",
377-
"toByteArray", "toCharArray", "toString", "buffer"
378-
]) and
379-
this.getMethod().getDeclaringType().hasQualifiedName("org.apache.commons.io", "IOUtils")
421+
module InputStream {
422+
class TypeInputStream extends RefType {
423+
TypeInputStream() { this.getASupertype*().hasQualifiedName("java.io", "InputStream") }
424+
}
425+
426+
class Source extends Call {
427+
Source() {
428+
exists(Call c | c.getCallee().getDeclaringType() instanceof TypeInputStream | this = c)
380429
}
430+
431+
DataFlow::Node getInputArgument() { result.asExpr() = this.(ConstructorCall).getArgument(0) }
432+
}
433+
434+
class Read extends MethodAccess {
435+
Read() {
436+
this.getReceiverType() instanceof TypeInputStream and
437+
this.getCallee().hasName(["read", "readNBytes", "readAllBytes"])
438+
}
439+
}
440+
441+
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
442+
exists(Call call |
443+
(
444+
call.getCallee().getDeclaringType() instanceof TypeInputStream or
445+
call.(MethodAccess).getReceiverType() instanceof TypeInputStream
446+
) and
447+
call.getCallee().hasName(["read", "readNBytes", "readAllBytes"]) and
448+
call.getQualifier() = n1.asExpr() and
449+
(
450+
call.getArgument(0) = n2.asExpr() or
451+
call = n2.asExpr()
452+
)
453+
)
381454
}
382455
}
383456

384457
module DecompressionBombsConfig implements DataFlow::StateConfigSig {
385458
class FlowState = DataFlow::FlowState;
386459

387460
predicate isSource(DataFlow::Node source, FlowState state) {
461+
// any()
462+
// or
388463
(
389464
source instanceof RemoteFlowSource
390465
or
@@ -393,29 +468,37 @@ module DecompressionBombsConfig implements DataFlow::StateConfigSig {
393468
source instanceof FormRemoteFlowSource
394469
or
395470
source instanceof FileUploadRemoteFlowSource
471+
or
472+
// TODO: we have to add Zip*InputStreams instead of general inputStream because of Flow State
473+
source = any(InputStream::Source i).getInputArgument()
474+
or
475+
source.asExpr() instanceof Zip::Inflatorsource
396476
) and
397-
state = ["Zip4j", "Zip", "ApacheCommons", "XserialSnappy"]
477+
state = ["Zip4j", "inflator", "Zip", "ApacheCommons", "XserialSnappy"]
478+
or
479+
source.asExpr() instanceof Zip::ZipFilesource and
480+
state = "ZipFile"
398481
}
399482

400-
predicate isBarrier(DataFlow::Node sanitizer, FlowState state) { none() }
401-
402-
/**
403-
* if getNumArgument > 1 then we can check for sanitizers before reading each Buffer of byte
404-
* otherwise it can be hard to write sanitizers
405-
*/
406483
predicate isSink(DataFlow::Node sink, FlowState state) {
407484
(
408-
sink.asExpr() = any(Zip::InflateCall r).getAWriteArgument() and
409-
state = "Zip"
410-
or
485+
// any() and
486+
// state = "Zip"
487+
// or
411488
exists(CommonsIO::IOUtils ma |
412489
sink.asExpr() = ma.getArgument(0) and
413-
state = ["Zip4j", "Zip", "ApacheCommons", "XserialSnappy"]
490+
state = ["Zip4j", "inflator", "Zip", "ApacheCommons", "XserialSnappy"]
414491
)
415492
or
416493
sink.asExpr() = any(Zip4j::ReadInputStreamCall r).getAWriteArgument() and
417494
state = "Zip4j"
418495
or
496+
sink.asExpr() = any(Zip::InflateCall r).getAWriteArgument() and
497+
state = "inflator"
498+
or
499+
sink.asExpr() instanceof InputStream::Read and
500+
state = "ZipFile"
501+
or
419502
sink.asExpr() = any(Zip::ReadInputStreamCall r).getAWriteArgument() and
420503
state = "Zip"
421504
or
@@ -433,34 +516,50 @@ module DecompressionBombsConfig implements DataFlow::StateConfigSig {
433516
)
434517
}
435518

519+
// predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
520+
// inputStreamAdditionalTaintStep(nodeFrom, nodeTo)
521+
// }
436522
predicate isAdditionalFlowStep(
437523
DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo
438524
) {
525+
InputStream::additionalTaintStep(nodeFrom, nodeTo) and
526+
stateFrom = "ZipFile" and
527+
stateTo = "ZipFile"
528+
or
439529
Zip::inflatorAdditionalTaintStep(nodeFrom, nodeTo) and
530+
stateFrom = "inflator" and
531+
stateTo = "inflator"
532+
or
533+
Zip::zipFileadditionalTaintStep(nodeFrom, nodeTo) and
534+
stateFrom = "ZipFile" and
535+
stateTo = "ZipFile"
536+
or
537+
Zip::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
440538
stateFrom = "Zip" and
441539
stateTo = "Zip"
442540
or
443-
(
444-
Zip::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
445-
stateFrom = "Zip"
446-
or
447-
Zip4j::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
448-
stateFrom = "Zip4j"
449-
or
450-
ApacheCommons::Factory::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
451-
stateFrom = "ApacheCommons"
452-
or
453-
ApacheCommons::Compressors::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
454-
stateFrom = "ApacheCommons"
455-
or
456-
ApacheCommons::Archivers::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
457-
stateFrom = "ApacheCommons"
458-
or
459-
XserialSnappy::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
460-
stateFrom = "XserialSnappy"
461-
) and
462-
stateTo = ""
541+
Zip4j::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
542+
stateFrom = "Zip4j" and
543+
stateTo = "Zip4j"
544+
or
545+
ApacheCommons::Factory::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
546+
stateFrom = "ApacheCommons" and
547+
stateTo = "ApacheCommons"
548+
or
549+
ApacheCommons::Compressors::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
550+
stateFrom = "ApacheCommons" and
551+
stateTo = "ApacheCommons"
552+
or
553+
ApacheCommons::Archivers::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
554+
stateFrom = "ApacheCommons" and
555+
stateTo = "ApacheCommons"
556+
or
557+
XserialSnappy::inputStreamAdditionalTaintStep(nodeFrom, nodeTo) and
558+
stateFrom = "ApacheCommons" and
559+
stateTo = "ApacheCommons"
463560
}
561+
562+
predicate isBarrier(DataFlow::Node sanitizer, FlowState state) { none() }
464563
}
465564

466565
module DecompressionBombsFlow = TaintTracking::GlobalWithState<DecompressionBombsConfig>;

0 commit comments

Comments
 (0)