fix a commit mistake

This commit is contained in:
amammad
2023-06-25 20:28:45 +10:00
parent 4a37da3593
commit 430375e2f0
5 changed files with 0 additions and 350 deletions

View File

@@ -1,121 +0,0 @@
/**
* @name User-controlled file decompression
* @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @precision medium
* @id cs/user-controlled-file-decompression
* @tags security
* experimental
* external/cwe/cwe-409
*/
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Remote
/**
* A data flow source for unsafe Decompression extraction.
*/
abstract class DecompressionSource extends DataFlow::Node { }
class ZipOpenReadSource extends DecompressionSource {
ZipOpenReadSource() {
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFile", ["OpenRead", "Open"]) and
this.asExpr() = mc.getArgument(0) and
not mc.getArgument(0).getType().isConst()
)
}
}
/** A path argument to a call to the `ZipArchive` constructor call. */
class ZipArchiveArgSource extends DecompressionSource {
ZipArchiveArgSource() {
exists(ObjectCreation oc |
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive")
|
this.asExpr() = oc.getArgument(0)
)
}
}
/**
* A data flow sink for unsafe zip extraction.
*/
abstract class DecompressionSink extends DataFlow::Node { }
/** A Caller of the `ExtractToFile` method. */
class ExtractToFileCallSink extends DecompressionSink {
ExtractToFileCallSink() {
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
this.asExpr() = mc.getArgumentForName("source")
)
}
}
/** A Qualifier of the `Open()` method. */
class OpenCallSink extends DecompressionSink {
OpenCallSink() {
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipArchiveEntry", "Open") and
this.asExpr() = mc.getQualifier()
)
}
}
/** A Call to the `GZipStreamSink` first arugument of Constructor Call . */
class GZipStreamSink extends DecompressionSink, DecompressionSource {
GZipStreamSink() {
exists(Constructor mc |
mc.getDeclaringType().hasQualifiedName("System.IO.Compression", "GZipStream") and
this.asExpr() = mc.getACall().getArgument(0)
)
}
}
/**
* A taint tracking configuration for Decompression Bomb.
*/
private module DecompressionBombConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof DecompressionSource
or
source instanceof RemoteFlowSource
}
predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionSink }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// var node2 = new ZipArchive(node1, ZipArchiveMode.Read);
exists(ObjectCreation oc |
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchive") and
node2.asExpr() = oc and
node1.asExpr() = oc.getArgumentForName("stream")
)
or
// var node2 = node1.ExtractToFile("./output.txt", true)
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
node2.asExpr() = mc and
node1.asExpr() = mc.getArgumentForName("source")
)
or
// var node2 = node1.OpenReadStream()
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormFile", "OpenReadStream") and
node2.asExpr() = mc and
node1.asExpr() = mc.getQualifier()
)
}
}
module DecompressionBomb = TaintTracking::Global<DecompressionBombConfig>;
import DecompressionBomb::PathGraph
from DecompressionBomb::PathNode source, DecompressionBomb::PathNode sink
where DecompressionBomb::flowPath(source, sink)
select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
"potentially untrusted source"

View File

@@ -1,26 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.</p>
<p>Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.</p>
</overview>
<recommendation>
<p>When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.</p>
</recommendation>
<example>
<p>A good Blog Post about decompression bombs and recommended method is already written by Gérald Barré in <a href="https://www.meziantou.net/prevent-zip-bombs-in-dotnet.htm">this</a> blog post</p>
<references>
<li>
<a href="https://www.bamsoftware.com/hacks/zipbomb/">A great research to gain more impact by this kind of attack</a>
</li>
</references>
</qhelp>

View File

@@ -1,62 +0,0 @@
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Remote
/** A data flow source of remote user input by Form File (ASP.NET unvalidated request data). */
class FormFile extends AspNetRemoteFlowSource {
FormFile() {
exists(MethodCall mc |
mc.getTarget()
.hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
"IFormFile", ["OpenReadStream", "ContentType", "ContentDisposition", "Name", "FileName"]) and
this.asExpr() = mc
)
or
exists(MethodCall mc |
mc.getTarget()
.hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
"IFormFile", "CopyTo") and
this.asParameter() = mc.getTarget().getParameter(0)
)
or
exists(Property fa |
fa.getDeclaringType()
.hasQualifiedName(["Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Http.Features"],
"IFormFile") and
fa.hasName(["ContentType", "ContentDisposition", "Name", "FileName"]) and
this.asExpr() = fa.getAnAccess()
)
}
override string getSourceType() {
result = "ASP.NET unvalidated request data from multipart request"
}
}
/** A data flow source of remote user input by Form (ASP.NET unvalidated request data). */
class FormCollection extends AspNetRemoteFlowSource {
FormCollection() {
exists(Property fa |
fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IFormCollection") and
fa.hasName("Keys") and
this.asExpr() = fa.getAnAccess()
)
}
override string getSourceType() {
result = "ASP.NET unvalidated request data from multipart request Form Keys"
}
}
/** A data flow source of remote user input by Headers (ASP.NET unvalidated request data). */
class HeaderDictionary extends AspNetRemoteFlowSource {
HeaderDictionary() {
exists(Property fa |
fa.getDeclaringType().hasQualifiedName("Microsoft.AspNetCore.Http", "IHeaderDictionary") and
this.asExpr() = fa.getAnAccess()
)
}
override string getSourceType() {
result = "ASP.NET unvalidated request data from Headers of request"
}
}

View File

@@ -1,140 +0,0 @@
using System.IO.Compression;
using Microsoft.AspNetCore.Mvc;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace MultipartFormWebAPITest.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ZipFile1Controller : ControllerBase
{
// POST api/<ImagesController>
[HttpPost]
public string Post(List<IFormFile> files)
{
if (!Request.ContentType!.StartsWith("multipart/form-data"))
return "400";
if (files.Count == 0)
return "400";
foreach (var formFile in files)
{
using var readStream = formFile.OpenReadStream();
if (readStream.Length == 0) return "400";
ZipHelpers.Bomb3(readStream);
ZipHelpers.Bomb2(formFile.FileName);
ZipHelpers.Bomb1(formFile.FileName);
}
var tmp = Request.Form["aa"];
var tmp2 = Request.Form.Keys;
// when we don't have only one file as body
ZipHelpers.Bomb3(Request.Body);
ZipHelpers.Bomb2(Request.Query["param1"].ToString());
var headers = Request.Headers;
ZipHelpers.Bomb1(headers.ETag);
return "200";
}
}
}
internal static class ZipHelpers
{
public static void Bomb3(Stream compressedFileStream)
{
// using FileStream compressedFileStream = File.Open(CompressedFileName, FileMode.Open);
// using FileStream outputFileStream = File.Create(DecompressedFileName);
using var decompressor = new GZipStream(compressedFileStream, CompressionMode.Decompress);
using var ms = new MemoryStream();
decompressor.CopyTo(ms);
}
public static void Bomb2(string filename)
{
using var zipToOpen = new FileStream(filename, FileMode.Open);
using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read);
foreach (var entry in archive.Entries) entry.ExtractToFile("./output.txt", true); // Sensitive
}
public static void Bomb1(string filename)
{
const long maxLength = 10 * 1024 * 1024; // 10MB
// var filename = "/home/am/0_WorkDir/Payloads/Bombs/bombs-bones-codes-BH-2016/archives/evil-headers/10GB.zip";
using var zipFile = ZipFile.OpenRead(filename);
// Quickly check the value from the zip header
var declaredSize = zipFile.Entries.Sum(entry => entry.Length);
if (declaredSize > maxLength)
throw new Exception("Archive is too big");
foreach (var entry in zipFile.Entries)
{
using var entryStream = entry.Open();
// Use MaxLengthStream to ensure we don't read more than the declared length
using var maxLengthStream = new MaxLengthStream(entryStream, entry.Length);
// Be sure to use the maxLengthSteam variable to read the content of the entry, not entryStream
using var ms = new MemoryStream();
maxLengthStream.CopyTo(ms);
}
}
}
internal sealed class MaxLengthStream : Stream
{
private readonly Stream _stream;
private long _length;
public MaxLengthStream(Stream stream, long maxLength)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
MaxLength = maxLength;
}
private long MaxLength { get; }
public override bool CanRead => _stream.CanRead;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => _stream.Length;
public override long Position
{
get => _stream.Position;
set => throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
var result = _stream.Read(buffer, offset, count);
_length += result;
if (_length > MaxLength)
throw new Exception("Stream is larger than the maximum allowed size");
return result;
}
// TODO ReadAsync
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
protected override void Dispose(bool disposing)
{
_stream.Dispose();
base.Dispose(disposing);
}
}

View File

@@ -1 +0,0 @@
experimental/CWE-502-DecompressionBombs/DecompressionBomb.ql