• 学习笔记-.net安全之ueditor远程下载分析


    0x00 简介

    UEditor 的.NET版本N年前爆出一个远程下载漏洞。

    0x01 漏洞成因

    URL:

    http://192.168.110.129:520/gbk-net/net/controller.ashx?action=catchimage

    POC中请求的URL如上,直接定位到文件controller.ashx

    utf8-net\net\controller.ashx

    1. case "listfile":
    2. action = new ListFileManager(context, Config.GetString("fileManagerListPath"), Config.GetStringList("fileManagerAllowFiles"));
    3. break;
    4. case "catchimage":
    5. action = new CrawlerHandler(context);
    6. break;

    catchimage 调用CrawlerHandler 进而跟进CrawlerHandler

    utf8-net\net\App_Code\CrawlerHandler.cs

    1. public class CrawlerHandler : Handler
    2. {
    3. private string[] Sources;
    4. private Crawler[] Crawlers;
    5. public CrawlerHandler(HttpContext context) : base(context) { }
    6. public override void Process()
    7. {
    8. Sources = Request.Form.GetValues("source[]");
    9. if (Sources == null || Sources.Length == 0)
    10. {
    11. WriteJson(new
    12. {
    13. state = "参数错误:没有指定抓取源"
    14. });
    15. return;
    16. }
    17. Crawlers = Sources.Select(x => new Crawler(x, Server).Fetch()).ToArray();
    18. WriteJson(new
    19. {
    20. state = "SUCCESS",
    21. list = Crawlers.Select(x => new
    22. {
    23. state = x.State,
    24. source = x.SourceUrl,
    25. url = x.ServerUrl
    26. })
    27. });
    28. }
    29. }

    CrawlerHandler获取Sources判断是否为空,这里只需要构造Sources[]=url即可,然后后调用Crawler Fetch

    Crawler Fetch()

    1. public Crawler Fetch()
    2. {
    3. if (!IsExternalIPAddress(this.SourceUrl))
    4. {
    5. State = "INVALID_URL";
    6. return this;
    7. }
    8. var request = HttpWebRequest.Create(this.SourceUrl) as HttpWebRequest;
    9. using (var response = request.GetResponse() as HttpWebResponse)
    10. {
    11. if (response.StatusCode != HttpStatusCode.OK)
    12. {
    13. State = "Url returns " + response.StatusCode + ", " + response.StatusDescription;
    14. return this;
    15. }
    16. if (response.ContentType.IndexOf("image") == -1)
    17. {
    18. State = "Url is not an image";
    19. return this;
    20. }
    21. ServerUrl = PathFormatter.Format(Path.GetFileName(this.SourceUrl), Config.GetString("catcherPathFormat"));
    22. var savePath = Server.MapPath(ServerUrl);
    23. if (!Directory.Exists(Path.GetDirectoryName(savePath)))
    24. {
    25. Directory.CreateDirectory(Path.GetDirectoryName(savePath));
    26. }
    27. try
    28. {
    29. var stream = response.GetResponseStream();
    30. var reader = new BinaryReader(stream);
    31. byte[] bytes;
    32. using (var ms = new MemoryStream())
    33. {
    34. byte[] buffer = new byte[4096];
    35. int count;
    36. while ((count = reader.Read(buffer, 0, buffer.Length)) != 0)
    37. {
    38. ms.Write(buffer, 0, count);
    39. }
    40. bytes = ms.ToArray();
    41. }
    42. File.WriteAllBytes(savePath, bytes);
    43. State = "SUCCESS";
    44. }
    45. catch (Exception e)
    46. {
    47. State = "抓取错误:" + e.Message;
    48. }
    49. return this;
    50. }
    51. }

    这里首先调用函数IsExternalIPAddress 判断你是不是一个正常的域名,不是则return
    然后请求传入的URL查看是否响应200,再判断ContentType是否为image,在这里其实只要构建服务器返回的Content-Typeimage/xxxx即可,重点在PathFormatter我们跟进。

    utf8-net\net\App_Code\PathFormater.cs

    1. public static string Format(string originFileName, string pathFormat)
    2. {
    3. if (String.IsNullOrWhiteSpace(pathFormat))
    4. {
    5. pathFormat = "{filename}{rand:6}";
    6. }
    7. var invalidPattern = new Regex(@"[\\\/\:\*\?\042\<\>\|]");
    8. originFileName = invalidPattern.Replace(originFileName, ""); //替换正则匹配到的为空1.png.aspx
    9. string extension = Path.GetExtension(originFileName); //.aspx
    10. string filename = Path.GetFileNameWithoutExtension(originFileName);
    11. pathFormat = pathFormat.Replace("{filename}", filename);
    12. pathFormat = new Regex(@"\{rand(\:?)(\d+)\}", RegexOptions.Compiled).Replace(pathFormat, new MatchEvaluator(delegate(Match match)
    13. {
    14. var digit = 6;
    15. if (match.Groups.Count > 2)
    16. {
    17. digit = Convert.ToInt32(match.Groups[2].Value);
    18. }
    19. var rand = new Random();
    20. return rand.Next((int)Math.Pow(10, digit), (int)Math.Pow(10, digit + 1)).ToString();
    21. }));
    22. pathFormat = pathFormat.Replace("{time}", DateTime.Now.Ticks.ToString());
    23. pathFormat = pathFormat.Replace("{yyyy}", DateTime.Now.Year.ToString());
    24. pathFormat = pathFormat.Replace("{yy}", (DateTime.Now.Year % 100).ToString("D2"));
    25. pathFormat = pathFormat.Replace("{mm}", DateTime.Now.Month.ToString("D2"));
    26. pathFormat = pathFormat.Replace("{dd}", DateTime.Now.Day.ToString("D2"));
    27. pathFormat = pathFormat.Replace("{hh}", DateTime.Now.Hour.ToString("D2"));
    28. pathFormat = pathFormat.Replace("{ii}", DateTime.Now.Minute.ToString("D2"));
    29. pathFormat = pathFormat.Replace("{ss}", DateTime.Now.Second.ToString("D2"));
    30. return pathFormat + extension;
    31. }

    整个逻辑流程就是 originFileName: 1.png?.aspx -> 1.png.aspx - > .aspx

    pathFormat: 获取config.json的格式 然后拼接返回保存文件的路径,可以把代码单独扣出来运行。

    其实我们直接控制Content-Typeimage/xxxx 后缀名为我们想要的即可,比如

    http://192.168.110.1:8000/1.ashx

    0x02 总结

    有的网上的poc只是为了方便大多数环境,当我们细读源码后知道新的方法往往能找到一些WAF绕过的技巧。

    点击关注,共同学习!安全狗的自我修养

    github haidragon

    https://github.com/haidragon

  • 相关阅读:
    Maven简介
    计算机毕业设计Java自驾游网站系统(源码+系统+mysql数据库+lw文档)
    基于AT89S52的俄罗斯方块游戏设计与实现
    【Python爬虫技巧】快速格式化请求头Request Headers
    个人云服务的搭建(折腾)之旅
    跨境商城源码有哪些独特的功能和优势
    ZYNQ上的简单 FSK 基带发射器
    C++笔记2(内存分区模型,引用)
    Kafka 安装与配置
    健康之路押注医药零售:毛利率下滑亏损扩大,医疗咨询人次大幅减少
  • 原文地址:https://blog.csdn.net/sinat_35360663/article/details/127730450