作者:Y4er
原文鏈接:https://y4er.com/post/fileupload-bypass-with-dotnet/
前言
看了賽博群的《從commons-fileupload源碼看文件上傳繞waf》,文末提到了dotnet也有這種問題,于是看了下dotnet的源碼。
環境
public ActionResult Index()
{
if (Request.Files.Count>0)
{
var file = Request.Files[0];
var filename = file.FileName;
var contenttype = file.ContentType;
var reader = new StreamReader(file.InputStream);
var content = reader.ReadToEnd();
var filepath = Request.MapPath("~/ ") + filename;
file.SaveAs(filepath);
var resp = $" filename:{filename}\n save file path:{filepath}\n file content:{content}\n file content type:{contenttype}";
return Content(resp);
}
else
{
return Content("no file");
}
}
分析
對于上傳的文件處理類位于System.Web.HttpMultipartContentTemplateParser.Parse()函數
internal static MultipartContentElement[] Parse(HttpRawUploadedContent data, int length, byte[] boundary, Encoding encoding)
{
HttpMultipartContentTemplateParser httpMultipartContentTemplateParser = new HttpMultipartContentTemplateParser(data, length, boundary, encoding);
httpMultipartContentTemplateParser.ParseIntoElementList();
return (MultipartContentElement[])httpMultipartContentTemplateParser._elements.ToArray(typeof(MultipartContentElement));
}
和Request.Files的層級調用關系如圖

在FillInFilesCollection()中,content-type必須以multipart/form-data開頭

這里和common fileupload的處理不同,然后進入this.GetMultipartContent()

1來處理boundary 2來解析上傳文件流 主要看1
private byte[] GetMultipartBoundary()
{
string text = HttpRequest.GetAttributeFromHeader(this.ContentType, "boundary");
if (text == null)
{
return null;
}
text = "--" + text;
return Encoding.ASCII.GetBytes(text.ToCharArray());
}
GetAttributeFromHeader是關鍵函數

分號逗號和等于號作為分隔符,并根據字符集忽略一些空白字符

所以content-type可以這么寫
Content-Type: multipart/form-data\u0085,;;,,,,,;;, boundary = aaa
接著看GetMultipartContent函數,解析完boundary和文件內容流之后,進入3 Parse函數也就是我們開篇提到的函數。

Parse函數就直接跟進了ParseIntoElementList函數

其中1 ParsePartHeaders是關鍵函數

能看到這個函數用來解析Content-Disposition和Content-Type,先以冒號分割拿到冒號后的部分
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain
即form-data; name="file"; filename="1.txt"
再看ExtractValueFromContentDispositionHeader函數

string text = " " + name + "=";
int num = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, text, pos, CompareOptions.IgnoreCase);
if (num < 0)
{
text = ";" + name + "=";
num = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, text, pos, CompareOptions.IgnoreCase);
if (num < 0)
{
text = name + "=";
num = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, text, pos, CompareOptions.IgnoreCase);
}
}
會自動加上分號和等號,所以可以隨便構造
Content-Disposition:\u0;;;;!@#$%^&*(;asdas\u0085d;085filename=11.aspx
Content-Disposition:filename=11.aspx
Content-Disposition:aaaaaaaaaaafilename=11.aspx;aaaaaaaaa
兩個對比一下就知道
Content-Disposition: form-data; name="file";filename="11.aspx"
- form-data字段可以不要
- 可以隨便在
filename和name前隨意填充字段 - 但是
filename和name后必須跟隨等號,并且末尾有分號標識結束。
在ExtractValueFromContentDispositionHeader函數中會對取的值進行Trim()處理,也能用\u0085來處理
content-type同上
最后貼一張構造的圖

寫在文后
Request.Files[0]的name字段是忽略大小寫的,Request.Files[0]和Request.Files[“file”]兩種寫法繞過時可能會出一些拿不到name的問題。
dotnet的特殊空白符如上文,但是位置一般只能放在兩側來用Trim去除。
文筆垃圾,措辭輕浮,內容淺顯,操作生疏。不足之處歡迎大師傅們指點和糾正,感激不盡。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1879/
暫無評論