CVE-2017-0929 (Other, published 2018-07-03T21:29Z)
Description: value: DNN (aka DotNetNuke) before 9.2.0 suffers from a Server-Side Request Forgery (SSRF) vulnerability in the DnnImageHandler class. Attackers may be able to access information about internal network resources.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Repository: https://github.com/dnnsoftware/Dnn.Platform
Files changed:
DNN Platform/Library/Services/GeneratedImage/DnnImageHandler.cs	
DNN Platform/Library/Services/GeneratedImage/FilterTransform/ImageResizeTransform.cs	
DNN Platform/Library/Services/GeneratedImage/ImageHandlerInternal.cs	
DNN Platform/Tests/DNN.Integration.Test.Framework/DNN.Integration.Test.Framework.csproj	
DNN Platform/Tests/DNN.Integration.Test.Framework/Helpers/PortalAliasHelper.cs	
DNN Platform/Tests/DotNetNuke.Tests.Integration/DotNetNuke.Tests.Integration.csproj	
DNN Platform/Tests/DotNetNuke.Tests.Integration/Tests/Library/ImageHandlerTests.cs	
DNN Platform/Tests/DotNetNuke.Tests.Integration/Tests/Security/AuthCookieTests.cs	
Commit message: DNN-10601: preventing handling external URLs in image handler.


Vulnerable code:
                            if (!parameters["Url"].StartsWith("http"))
                            imgUrl = parameters["Url"];
		{
                    image.Save(outStream, ici, eps);
﻿using System;
using System.Net.Http.Headers;
using System.Threading;

Fixed code:
using System.Collections.Generic;
                            var url = parameters["Url"];
                            // allow only site resources when using the url parameter
                            if (!url.StartsWith("http") || !UriBelongsToSite(new Uri(url)))

                            imgUrl = url;

        // checks whether the uri belongs to any of the site-wide aliases
        private static bool UriBelongsToSite(Uri uri)
        {
            IEnumerable<string> hostAliases =
                from PortalAliasInfo alias in PortalAliasController.Instance.GetPortalAliases().Values
                 select alias.HTTPAlias.ToLowerInvariant();

            // if URI, for example, = "http(s)://myDomain:80/DNNDev/myPage?var=name" , then the two strings will be
            // uriNoScheme1 = "mydomain/dnndev/mypage"  -- lower case
            // uriNoScheme2 = "mydomain:80/dnndev/mypage"  -- lower case
            var uriNoScheme1 = (uri.DnsSafeHost + uri.LocalPath).ToLowerInvariant();
            var uriNoScheme2 = (uri.Authority + uri.LocalPath).ToLowerInvariant();
            return hostAliases.Any(alias => uriNoScheme1.StartsWith(alias) || uriNoScheme2.StartsWith(alias));
        }
        {
            if (image == null)
                return null;    

                    image?.Save(outStream, ici, eps);
    <Compile Include="Helpers\PortalAliasHelper.cs" />
﻿// DotNetNuke® - http://www.dnnsoftware.com
// Copyright (c) 2002-2018, DNN Corp.
// All Rights Reserved

using System.Collections.Generic;
using System.Linq;
using DotNetNuke.Entities.Portals;

namespace DNN.Integration.Test.Framework.Helpers
{
    public class PortalAliasHelper
    {
        public static PortalAliasInfo GetPrimaryPortalAlias(int portalId = 0)
        {
            return DatabaseHelper.ExecuteStoredProcedure<PortalAliasInfo>("GetPortalAliases").First(p => p.PortalID == portalId && p.IsPrimary);
        }

        public static IEnumerable<PortalAliasInfo> GetPortalAliaes(int portalId = 0)
        {
            return DatabaseHelper.ExecuteStoredProcedure<PortalAliasInfo>("GetPortalAliases").Where(p => p.PortalID == portalId);
        }

        public static IEnumerable<PortalAliasInfo> GetAllPortalAliaes()
        {
            return DatabaseHelper.ExecuteStoredProcedure<PortalAliasInfo>("GetPortalAliases");
        }

        public static void AddPortalAlias(PortalAliasInfo portal)
        {
            DatabaseHelper.ExecuteStoredProcedure("AddPortalAlias",
                portal.PortalID,
                portal.HTTPAlias,
                portal.CultureCode,
                portal.Skin,
                portal.BrowserType,
                portal.IsPrimary,
                -1);
        }
    }
}
    <Compile Include="Tests\Library\ImageHandlerTests.cs" />
﻿// DotNetNuke® - http://www.dnnsoftware.com
// Copyright (c) 2002-2018, DNN Corp.
// All Rights Reserved

using System.Linq;
using DNN.Integration.Test.Framework;
using DNN.Integration.Test.Framework.Helpers;
using DotNetNuke.Entities.Portals;
using NUnit.Framework;

namespace DotNetNuke.Tests.Integration.Tests.Library
{
    [TestFixture]
    public class ImageHandlerTests : IntegrationTestBase
    {
        // we assume this GIF image exists in all DNN sites and it shouldn't be PNG
        private const string HandlerPath = "/DnnImageHandler.ashx?mode=file&w=1&url={0}/images/1x1.GIF";

        [Test]
        public void Using_Image_Handler_For_Foreign_Site_ShouldFail()
        {
            var session = WebApiTestHelper.GetAnnonymousConnector();
            var relativeUrl = string.Format(HandlerPath, "https://google.com");

            var response = session.GetContent(relativeUrl).Content.ReadAsStringAsync().Result;
            Assert.IsTrue(response.StartsWith("�PNG\r\n"), $"Content = {response}");
        }

        [Test]
        public void Using_Image_Handler_For_Main_Alias_ShouldPass()
        {
            var session = WebApiTestHelper.GetAnnonymousConnector();
            var relativeUrl = string.Format(HandlerPath, AppConfigHelper.SiteUrl);

            var response = session.GetContent(relativeUrl).Content.ReadAsStringAsync().Result;
            Assert.IsTrue(response.StartsWith("GIF89a"), $"Content = {response}");
        }

        [Test]
        public void Using_Image_Handler_From_All_Alias_ShouldPass()
        {
            PortalAliasInfo pai;
            var aliases = PortalAliasHelper.GetPortalAliaes().ToList();
            if (aliases.Count == 1)
            {
                var primary = aliases.First();
                pai = new PortalAliasInfo
                {
                    PortalID = primary.PortalID,
                    HTTPAlias = "my-" + primary.HTTPAlias,
                    CultureCode = primary.CultureCode,
                    Skin = primary.Skin,
                    BrowserType = primary.BrowserType,
                    IsPrimary = false,

                };
            }
            else
            {
                pai = aliases.First(a => a.PortalAliasID > 1);
            }

            var session = WebApiTestHelper.GetAnnonymousConnector();
            aliases = PortalAliasHelper.GetPortalAliaes().ToList();
            foreach (var alias in aliases)
            {
                var relativeUrl = string.Format(HandlerPath, AppConfigHelper.SiteUrl);
                var absoluteUrl = $"http://{alias.HTTPAlias}{relativeUrl}";
                LogText("Getting image from " + absoluteUrl);

                var response = session.GetContent(absoluteUrl).Content.ReadAsStringAsync().Result;
                Assert.IsTrue(response.StartsWith("GIF89a"), $"Url: {absoluteUrl} / Content = {response}");
            }
        }
    }
}
﻿// DotNetNuke® - http://www.dnnsoftware.com
// Copyright (c) 2002-2018, DNN Corp.
// All Rights Reserved

using System;