CINXE.COM
Project Zero: August 2021
<!DOCTYPE html> <html class='v2' dir='ltr' lang='en' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <meta content='width=1100' name='viewport'/> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='https://googleprojectzero.blogspot.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='https://googleprojectzero.blogspot.com/2021/08/' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Project Zero - Atom" href="https://googleprojectzero.blogspot.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Project Zero - RSS" href="https://googleprojectzero.blogspot.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Project Zero - Atom" href="https://www.blogger.com/feeds/4838136820032157985/posts/default" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content='https://googleprojectzero.blogspot.com/2021/08/' property='og:url'/> <meta content='Project Zero' property='og:title'/> <meta content='News and updates from the Project Zero team at Google' property='og:description'/> <title>Project Zero: August 2021</title> <style type='text/css'>@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;font-display:swap;src:url(//fonts.gstatic.com/s/opensans/v40/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4gaVY.eot);}</style> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Simple Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#66bbdd"/> <Group description="Page Text" selector="body"> <Variable name="body.font" description="Font" type="font" default="normal normal 12px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="body.text.color" description="Text Color" type="color" default="#222222"/> </Group> <Group description="Backgrounds" selector=".body-fauxcolumns-outer"> <Variable name="body.background.color" description="Outer Background" type="color" default="#66bbdd"/> <Variable name="content.background.color" description="Main Background" type="color" default="#ffffff"/> <Variable name="header.background.color" description="Header Background" type="color" default="transparent"/> </Group> <Group description="Links" selector=".main-outer"> <Variable name="link.color" description="Link Color" type="color" default="#2288bb"/> <Variable name="link.visited.color" description="Visited Color" type="color" default="#888888"/> <Variable name="link.hover.color" description="Hover Color" type="color" default="#33aaff"/> </Group> <Group description="Blog Title" selector=".header h1"> <Variable name="header.font" description="Font" type="font" default="normal normal 60px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="header.text.color" description="Title Color" type="color" default="#3399bb" /> </Group> <Group description="Blog Description" selector=".header .description"> <Variable name="description.text.color" description="Description Color" type="color" default="#777777" /> </Group> <Group description="Tabs Text" selector=".tabs-inner .widget li a"> <Variable name="tabs.font" description="Font" type="font" default="normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="tabs.text.color" description="Text Color" type="color" default="#999999"/> <Variable name="tabs.selected.text.color" description="Selected Color" type="color" default="#000000"/> </Group> <Group description="Tabs Background" selector=".tabs-outer .PageList"> <Variable name="tabs.background.color" description="Background Color" type="color" default="#f5f5f5"/> <Variable name="tabs.selected.background.color" description="Selected Color" type="color" default="#eeeeee"/> </Group> <Group description="Post Title" selector="h3.post-title, .comments h4"> <Variable name="post.title.font" description="Font" type="font" default="normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> </Group> <Group description="Date Header" selector=".date-header"> <Variable name="date.header.color" description="Text Color" type="color" default="#000000"/> <Variable name="date.header.background.color" description="Background Color" type="color" default="transparent"/> <Variable name="date.header.font" description="Text Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="date.header.padding" description="Date Header Padding" type="string" default="inherit"/> <Variable name="date.header.letterspacing" description="Date Header Letter Spacing" type="string" default="inherit"/> <Variable name="date.header.margin" description="Date Header Margin" type="string" default="inherit"/> </Group> <Group description="Post Footer" selector=".post-footer"> <Variable name="post.footer.text.color" description="Text Color" type="color" default="#666666"/> <Variable name="post.footer.background.color" description="Background Color" type="color" default="#f9f9f9"/> <Variable name="post.footer.border.color" description="Shadow Color" type="color" default="#eeeeee"/> </Group> <Group description="Gadgets" selector="h2"> <Variable name="widget.title.font" description="Title Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="widget.title.text.color" description="Title Color" type="color" default="#000000"/> <Variable name="widget.alternate.text.color" description="Alternate Color" type="color" default="#999999"/> </Group> <Group description="Images" selector=".main-inner"> <Variable name="image.background.color" description="Background Color" type="color" default="#ffffff"/> <Variable name="image.border.color" description="Border Color" type="color" default="#eeeeee"/> <Variable name="image.text.color" description="Caption Text Color" type="color" default="#000000"/> </Group> <Group description="Accents" selector=".content-inner"> <Variable name="body.rule.color" description="Separator Line Color" type="color" default="#eeeeee"/> <Variable name="tabs.border.color" description="Tabs Border Color" type="color" default="transparent"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#eeeeee" default="$(color) none repeat scroll top left"/> <Variable name="body.background.override" description="Body Background Override" type="string" default=""/> <Variable name="body.background.gradient.cap" description="Body Gradient Cap" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="body.background.gradient.tile" description="Body Gradient Tile" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/body_gradient_tile_light.png)"/> <Variable name="content.background.color.selector" description="Content Background Color Selector" type="string" default=".content-inner"/> <Variable name="content.padding" description="Content Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.padding.horizontal" description="Content Horizontal Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.shadow.spread" description="Content Shadow Spread" type="length" default="40px" min="0" max="100px"/> <Variable name="content.shadow.spread.webkit" description="Content Shadow Spread (WebKit)" type="length" default="5px" min="0" max="100px"/> <Variable name="content.shadow.spread.ie" description="Content Shadow Spread (IE)" type="length" default="10px" min="0" max="100px"/> <Variable name="main.border.width" description="Main Border Width" type="length" default="0" min="0" max="10px"/> <Variable name="header.background.gradient" description="Header Gradient" type="url" default="none"/> <Variable name="header.shadow.offset.left" description="Header Shadow Offset Left" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.offset.top" description="Header Shadow Offset Top" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.spread" description="Header Shadow Spread" type="length" default="1px" min="0" max="100px"/> <Variable name="header.padding" description="Header Padding" type="length" default="30px" min="0" max="100px"/> <Variable name="header.border.size" description="Header Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.bottom.border.size" description="Header Bottom Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.border.horizontalsize" description="Header Horizontal Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="description.text.size" description="Description Text Size" type="string" default="140%"/> <Variable name="tabs.margin.top" description="Tabs Margin Top" type="length" default="0" min="0" max="100px"/> <Variable name="tabs.margin.side" description="Tabs Side Margin" type="length" default="30px" min="0" max="100px"/> <Variable name="tabs.background.gradient" description="Tabs Background Gradient" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="tabs.border.width" description="Tabs Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="tabs.bevel.border.width" description="Tabs Bevel Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="post.margin.bottom" description="Post Bottom Margin" type="length" default="25px" min="0" max="100px"/> <Variable name="image.border.small.size" description="Image Border Small Size" type="length" default="2px" min="0" max="10px"/> <Variable name="image.border.large.size" description="Image Border Large Size" type="length" default="5px" min="0" max="10px"/> <Variable name="page.width.selector" description="Page Width Selector" type="string" default=".region-inner"/> <Variable name="page.width" description="Page Width" type="string" default="auto"/> <Variable name="main.section.margin" description="Main Section Margin" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding" description="Main Padding" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding.top" description="Main Padding Top" type="length" default="30px" min="0" max="100px"/> <Variable name="main.padding.bottom" description="Main Padding Bottom" type="length" default="30px" min="0" max="100px"/> <Variable name="paging.background" color="#ffffff" description="Background of blog paging area" type="background" default="transparent none no-repeat scroll top center"/> <Variable name="footer.bevel" description="Bevel border length of footer" type="length" default="0" min="0" max="10px"/> <Variable name="mobile.background.overlay" description="Mobile Background Overlay" type="string" default="transparent none repeat scroll top left"/> <Variable name="mobile.background.size" description="Mobile Background Size" type="string" default="auto"/> <Variable name="mobile.button.color" description="Mobile Button Color" type="color" default="#ffffff" /> <Variable name="startSide" description="Side where text starts in blog language" type="automatic" default="left"/> <Variable name="endSide" description="Side where text ends in blog language" type="automatic" default="right"/> */ /* Content ----------------------------------------------- */ body { font: normal normal 12px Open Sans; color: #000000; background: #eeeeee none repeat scroll top left; padding: 0 0 0 0; } html body .region-inner { min-width: 0; max-width: 100%; width: auto; } h2 { font-size: 22px; } a:link { text-decoration:none; color: #2288bb; } a:visited { text-decoration:none; color: #888888; } a:hover { text-decoration:underline; color: #33aaff; } .body-fauxcolumn-outer .fauxcolumn-inner { background: transparent none repeat scroll top left; _background-image: none; } .body-fauxcolumn-outer .cap-top { position: absolute; z-index: 1; height: 400px; width: 100%; } .body-fauxcolumn-outer .cap-top .cap-left { width: 100%; background: transparent none repeat-x scroll top left; _background-image: none; } .content-outer { -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -goog-ms-box-shadow: 0 0 0 #333333; box-shadow: 0 0 0 rgba(0, 0, 0, .15); margin-bottom: 1px; } .content-inner { padding: 10px 40px; } .content-inner { background-color: #ffffff; } /* Header ----------------------------------------------- */ .header-outer { background: transparent none repeat-x scroll 0 -400px; _background-image: none; } .Header h1 { font: normal normal 40px Open Sans; color: #000000; text-shadow: 0 0 0 rgba(0, 0, 0, .2); } .Header h1 a { color: #000000; } .Header .description { font-size: 18px; color: #000000; } .header-inner .Header .titlewrapper { padding: 22px 0; } .header-inner .Header .descriptionwrapper { padding: 0 0; } /* Tabs ----------------------------------------------- */ .tabs-inner .section:first-child { border-top: 0 solid #dddddd; } .tabs-inner .section:first-child ul { margin-top: -1px; border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; } .tabs-inner .widget ul { background: transparent none repeat-x scroll 0 -800px; _background-image: none; border-bottom: 1px solid #dddddd; margin-top: 0; margin-left: -30px; margin-right: -30px; } .tabs-inner .widget li a { display: inline-block; padding: .6em 1em; font: normal normal 12px Open Sans; color: #000000; border-left: 1px solid #ffffff; border-right: 1px solid #dddddd; } .tabs-inner .widget li:first-child a { border-left: none; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; background-color: #eeeeee; text-decoration: none; } /* Columns ----------------------------------------------- */ .main-outer { border-top: 0 solid transparent; } .fauxcolumn-left-outer .fauxcolumn-inner { border-right: 1px solid transparent; } .fauxcolumn-right-outer .fauxcolumn-inner { border-left: 1px solid transparent; } /* Headings ----------------------------------------------- */ div.widget > h2, div.widget h2.title { margin: 0 0 1em 0; font: normal bold 11px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; color: #000000; } /* Widgets ----------------------------------------------- */ .widget .zippy { color: #999999; text-shadow: 2px 2px 1px rgba(0, 0, 0, .1); } .widget .popular-posts ul { list-style: none; } /* Posts ----------------------------------------------- */ h2.date-header { font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } .date-header span { background-color: #bbbbbb; color: #ffffff; padding: 0.4em; letter-spacing: 3px; margin: inherit; } .main-inner { padding-top: 35px; padding-bottom: 65px; } .main-inner .column-center-inner { padding: 0 0; } .main-inner .column-center-inner .section { margin: 0 1em; } .post { margin: 0 0 45px 0; } h3.post-title, .comments h4 { font: normal normal 22px Open Sans; margin: .75em 0 0; } .post-body { font-size: 110%; line-height: 1.4; position: relative; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 2px; background: #ffffff; border: 1px solid #eeeeee; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body .tr-caption-container { color: #666666; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } .post-header { margin: 0 0 1.5em; line-height: 1.6; font-size: 90%; } .post-footer { margin: 20px -2px 0; padding: 5px 10px; color: #666666; background-color: #eeeeee; border-bottom: 1px solid #eeeeee; line-height: 1.6; font-size: 90%; } #comments .comment-author { padding-top: 1.5em; border-top: 1px solid transparent; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } #comments .avatar-image-container img { border: 1px solid #eeeeee; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #999999; border-bottom: 1px solid #999999; } .comments .comment-thread.inline-thread { background-color: #eeeeee; } .comments .continue { border-top: 2px solid #999999; } /* Accents ---------------------------------------------- */ .section-columns td.columns-cell { border-left: 1px solid transparent; } .blog-pager { background: transparent url(//www.blogblog.com/1kt/simple/paging_dot.png) repeat-x scroll top center; } .blog-pager-older-link, .home-link, .blog-pager-newer-link { background-color: #ffffff; padding: 5px; } .footer-outer { border-top: 1px dashed #bbbbbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } .mobile .body-fauxcolumn-outer .cap-top { background-size: 100% auto; } .mobile .content-outer { -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); box-shadow: 0 0 3px rgba(0, 0, 0, .15); } .mobile .tabs-inner .widget ul { margin-left: 0; margin-right: 0; } .mobile .post { margin: 0; } .mobile .main-inner .column-center-inner .section { margin: 0; } .mobile .date-header span { padding: 0.1em 10px; margin: 0 -10px; } .mobile h3.post-title { margin: 0; } .mobile .blog-pager { background: transparent none no-repeat scroll top center; } .mobile .footer-outer { border-top: none; } .mobile .main-inner, .mobile .footer-inner { background-color: #ffffff; } .mobile-index-contents { color: #000000; } .mobile-link-button { background-color: #2288bb; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #ffffff; } .mobile .tabs-inner .section:first-child { border-top: none; } .mobile .tabs-inner .PageList .widget-content { background-color: #eeeeee; color: #000000; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #dddddd; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 1120px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 1120px; max-width: 1120px; _width: 1120px; } .main-inner .columns { padding-left: 0; padding-right: 310px; } .main-inner .fauxcolumn-center-outer { left: 0; right: 310px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0") - parseInt("310px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0; } .main-inner .fauxcolumn-right-outer { width: 310px; } .main-inner .column-left-outer { width: 0; right: 100%; margin-left: -0; } .main-inner .column-right-outer { width: 310px; margin-right: -310px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } body#layout div.add_widget { padding: 8px; } body#layout div.add_widget a { margin-left: 32px; } --></style> <script type='text/javascript'> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-240546891-1', 'auto', 'blogger'); ga('blogger.send', 'pageview'); </script> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&zx=89189630-7e30-43b5-91d3-8fdab32d43bc' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&zx=89189630-7e30-43b5-91d3-8fdab32d43bc' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body class='loading'> <div class='navbar section' id='navbar' name='Navbar'><div class='widget Navbar' data-version='1' id='Navbar1'><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d4838136820032157985\x26blogName\x3dProject+Zero\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dLIGHT\x26layoutType\x3dLAYOUTS\x26searchRoot\x3dhttps://googleprojectzero.blogspot.com/search\x26blogLocale\x3den\x26v\x3d2\x26homepageUrl\x3dhttps://googleprojectzero.blogspot.com/\x26vt\x3d7568236161501195533', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe", messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER, messageHandlers: { 'blogger-ping': function() {} } }); } }); </script><script type="text/javascript"> (function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '//pagead2.googlesyndication.com/pagead/js/google_top_exp.js'; var head = document.getElementsByTagName('head')[0]; if (head) { head.appendChild(script); }})(); </script> </div></div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header' name='Header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner'> <div class='titlewrapper'> <h1 class='title'> <a href='https://googleprojectzero.blogspot.com/'> Project Zero </a> </h1> </div> <div class='descriptionwrapper'> <p class='description'><span>News and updates from the Project Zero team at Google</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol' name='Cross-Column'></div> <div class='tabs no-items section' id='crosscol-overflow' name='Cross-Column 2'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main' name='Main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class="date-outer"> <h2 class='date-header'><span>Thursday, August 19, 2021</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdrhKLVqgZvHWYXMuk3OGSBiicJM0CjhHiN5lpecetctCFzLfbk3iJhpzupig9KOC87QvqyxJOiGgtpCl5XPthk-YM7i57afRn_n2IgmwyB-5-Txd2lcVLmGTE3KXgn2Px9xr2DH7rXOUZuQ-E8uhWl6n_3iIDX_XNiq5GvOTLMHQ9cLvHM9M3IqdO/s1091/Firewall%20Arch.jpg' itemprop='image_url'/> <meta content='4838136820032157985' itemprop='blogId'/> <meta content='1215474650509337553' itemprop='postId'/> <a name='1215474650509337553'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html'>Understanding Network Access in Windows AppContainers</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-1215474650509337553' itemprop='description articleBody'> <style type="text/css">ul.lst-kix_w3t1hchiy4w5-6{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-1{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-5{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-0{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-4{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-3{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-3{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-2{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-5{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-4{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-8{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-7{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-7{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-6{list-style-type:none}ul.lst-kix_a73fbzj8zqo2-8{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-2{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-1{list-style-type:none}ul.lst-kix_w3t1hchiy4w5-0{list-style-type:none}.lst-kix_9fia5rt9e6y-4>li:before{content:"\0025cb "}.lst-kix_9fia5rt9e6y-6>li:before{content:"\0025cf "}.lst-kix_w3t1hchiy4w5-2>li:before{content:"\0025a0 "}.lst-kix_9fia5rt9e6y-3>li:before{content:"\0025cf "}.lst-kix_9fia5rt9e6y-7>li:before{content:"\0025cb "}.lst-kix_w3t1hchiy4w5-3>li:before{content:"\0025cf "}.lst-kix_a73fbzj8zqo2-6>li:before{content:"\0025cf "}.lst-kix_9fia5rt9e6y-0>li:before{content:"\0025cf "}.lst-kix_9fia5rt9e6y-2>li:before{content:"\0025a0 "}.lst-kix_9fia5rt9e6y-8>li:before{content:"\0025a0 "}.lst-kix_w3t1hchiy4w5-4>li:before{content:"\0025cb "}.lst-kix_a73fbzj8zqo2-5>li:before{content:"\0025a0 "}.lst-kix_9fia5rt9e6y-1>li:before{content:"\0025cb "}.lst-kix_w3t1hchiy4w5-5>li:before{content:"\0025a0 "}.lst-kix_w3t1hchiy4w5-7>li:before{content:"\0025cb "}.lst-kix_a73fbzj8zqo2-2>li:before{content:"\0025a0 "}.lst-kix_a73fbzj8zqo2-4>li:before{content:"\0025cb "}.lst-kix_w3t1hchiy4w5-6>li:before{content:"\0025cf "}.lst-kix_a73fbzj8zqo2-3>li:before{content:"\0025cf "}.lst-kix_a73fbzj8zqo2-0>li:before{content:"\0025cf "}.lst-kix_w3t1hchiy4w5-8>li:before{content:"\0025a0 "}.lst-kix_a73fbzj8zqo2-1>li:before{content:"\0025cb "}.lst-kix_9fia5rt9e6y-5>li:before{content:"\0025a0 "}ul.lst-kix_uwx5zr9ju08v-8{list-style-type:none}.lst-kix_a73fbzj8zqo2-7>li:before{content:"\0025cb "}ul.lst-kix_uwx5zr9ju08v-3{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-2{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-1{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-0{list-style-type:none}.lst-kix_w3t1hchiy4w5-1>li:before{content:"\0025cb "}.lst-kix_a73fbzj8zqo2-8>li:before{content:"\0025a0 "}ul.lst-kix_uwx5zr9ju08v-7{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-6{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-5{list-style-type:none}ul.lst-kix_uwx5zr9ju08v-4{list-style-type:none}.lst-kix_w3t1hchiy4w5-0>li:before{content:"\0025cf "}.lst-kix_uwx5zr9ju08v-7>li:before{content:"\0025cb "}.lst-kix_uwx5zr9ju08v-6>li:before{content:"\0025cf "}.lst-kix_uwx5zr9ju08v-8>li:before{content:"\0025a0 "}.lst-kix_uwx5zr9ju08v-5>li:before{content:"\0025a0 "}ul.lst-kix_9fia5rt9e6y-0{list-style-type:none}ul.lst-kix_9fia5rt9e6y-1{list-style-type:none}ul.lst-kix_9fia5rt9e6y-2{list-style-type:none}ul.lst-kix_9fia5rt9e6y-3{list-style-type:none}ul.lst-kix_9fia5rt9e6y-4{list-style-type:none}ul.lst-kix_9fia5rt9e6y-5{list-style-type:none}li.li-bullet-0:before{margin-left:-18pt;white-space:nowrap;display:inline-block;min-width:18pt}ul.lst-kix_9fia5rt9e6y-6{list-style-type:none}ul.lst-kix_9fia5rt9e6y-7{list-style-type:none}ul.lst-kix_9fia5rt9e6y-8{list-style-type:none}.lst-kix_uwx5zr9ju08v-0>li:before{content:"\0025cf "}.lst-kix_uwx5zr9ju08v-1>li:before{content:"\0025cb "}.lst-kix_uwx5zr9ju08v-3>li:before{content:"\0025cf "}.lst-kix_uwx5zr9ju08v-2>li:before{content:"\0025a0 "}.lst-kix_uwx5zr9ju08v-4>li:before{content:"\0025cb "}ol{margin:0;padding:0}table td,table th{padding:0}.c19{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;background-color:#012356;border-left-style:solid;border-bottom-width:1pt;width:468pt;border-top-color:#000000;border-bottom-style:solid}.c40{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:468pt;border-top-color:#000000;border-bottom-style:solid}.c6{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:249.8pt;border-top-color:#000000;border-bottom-style:solid}.c30{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:247.5pt;border-top-color:#000000;border-bottom-style:solid}.c15{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:218.2pt;border-top-color:#000000;border-bottom-style:solid}.c28{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#000000;border-top-width:1pt;border-right-width:1pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:220.5pt;border-top-color:#000000;border-bottom-style:solid}.c9{background-color:#ffffff;color:#171717;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10.5pt;font-family:"Arial";font-style:normal}.c35{background-color:#ffe599;color:#171717;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c39{background-color:#d9ead3;color:#171717;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c24{background-color:#f9cb9c;color:#171717;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c31{background-color:#f6b26b;color:#171717;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c3{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left;height:11pt}.c20{color:#f4cccc;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c5{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10pt;font-family:"Arial";font-style:normal}.c33{padding-top:16pt;padding-bottom:4pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.c26{padding-top:18pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.c25{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:16pt;font-family:"Arial";font-style:normal}.c2{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c8{color:#ffff00;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c16{color:#434343;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:14pt;font-family:"Arial";font-style:normal}.c18{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c0{color:#efefef;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Courier New";font-style:normal}.c34{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial"}.c22{color:#000000;font-weight:700;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial"}.c4{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left}.c10{border-spacing:0;border-collapse:collapse;margin-right:auto}.c13{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.c1{padding-top:0pt;padding-bottom:0pt;line-height:1.0;text-align:left}.c38{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c32{background-color:#ffffff;font-size:10.5pt;color:#171717}.c14{font-family:"Courier New";color:#efefef;font-weight:400}.c23{font-family:"Courier New";color:#ffff00;font-weight:400}.c27{border:1px solid black;margin:5px}.c17{padding:0;margin:0}.c11{color:inherit;text-decoration:inherit}.c29{margin-left:36pt;padding-left:0pt}.c37{background-color:#ff9900}.c12{height:0pt}.c36{background-color:#ff0000}.c21{height:11pt}.c7{font-style:italic}.title{padding-top:0pt;color:#000000;font-size:26pt;padding-bottom:3pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:0pt;color:#666666;font-size:15pt;padding-bottom:16pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Arial"}p{margin:0;color:#000000;font-size:11pt;font-family:"Arial"}h1{padding-top:20pt;color:#000000;font-size:20pt;padding-bottom:6pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-size:16pt;padding-bottom:6pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:16pt;color:#434343;font-size:14pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:14pt;color:#666666;font-size:12pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:12pt;color:#666666;font-size:11pt;padding-bottom:4pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}</style></head><body class="c38"> <p class="c4"><span>Posted by James Forshaw, Project Zero<br><br></span><span>Recently </span><span>I've</span><span class="c2"> been delving into the inner workings of the Windows Firewall. This is interesting to me as it's used to enforce various restrictions such as whether AppContainer sandboxed applications can access the network. Being able to bypass network restrictions in AppContainer sandboxes is interesting as it expands the attack surface available to the application, such as being able to access services on localhost, as well as granting access to intranet resources in an Enterprise.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>I recently discovered a </span><span class="c13"><a class="c111" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2207">configuration issue</a></span><span class="c2"> with the Windows Firewall which allowed the restrictions to be bypassed and allowed an AppContainer process to access the network. Unfortunately Microsoft decided it didn't meet the bar for a security bulletin so it's marked as WontFix. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">As the mechanism that the Windows Firewall uses to restrict access to the network from an AppContainer isn't officially documented as far as I know, I'll provide the details on how the restrictions are implemented. This will provide the background to understanding why my configuration issue allowed for network access.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>I'll also take the opportunity to give an overview of how the Windows Firewall functions and how you can use some of my tooling to inspect the current firewall configuration. This will provide security researchers with the information they need to better understand the firewall and assess its configuration to find other security issues similar to the one I reported. At the same time I'll note some interesting quirks in the implementation which you might find useful.</span></p><h2 class="c26" id="h.dy1a1mgzrf91"><span class="c25">Windows Firewall Architecture Primer</span></h2> <p class="c4"><span>Before we can understand how network access is controlled in an AppContainer we need to understand how the built-in Windows firewall functions. Prior to XP SP2 Windows didn't have a built-in firewall, and you would typically install a third-party firewall such as ZoneAlarm. These firewalls were implemented by hooking into </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-hardware/drivers/network/">Network Driver Interface Specification (NDIS)</a></span><span> drivers or implementing user-mode </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/winsock/about-the-winsock-spi">Winsock Service Providers</a></span><span class="c2"> but this was complex and error prone.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>While XP SP2 introduced the built-in firewall, the basis for the one used in modern versions of Windows was introduced in Vista as the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/windows-filtering-platform-start-page">Windows Filtering Platform (WFP)</a></span><span class="c2">. However, as a user you wouldn't typically interact directly with WFP. Instead you'd use a firewall product which exposes a user interface, and then configures WFP to do the actual firewalling. On a default installation of Windows this would be the Windows Defender Firewall. If you installed a third-party firewall this would replace the Defender component but the actual firewall would still be implemented through configuring WFP.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"></p> <p class="c4"><span class="c34 c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdrhKLVqgZvHWYXMuk3OGSBiicJM0CjhHiN5lpecetctCFzLfbk3iJhpzupig9KOC87QvqyxJOiGgtpCl5XPthk-YM7i57afRn_n2IgmwyB-5-Txd2lcVLmGTE3KXgn2Px9xr2DH7rXOUZuQ-E8uhWl6n_3iIDX_XNiq5GvOTLMHQ9cLvHM9M3IqdO/s1091/Firewall%20Arch.jpg" style="display: block; padding: 1em 0;text-align: center;"><img alt="Architectural diagram of the built-in Windows Firewall. Showing a separation between user components (MPSSVC, BFE) and the kernel components (AFD, TCP/IP, NETIO and Callout Drivers)" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdrhKLVqgZvHWYXMuk3OGSBiicJM0CjhHiN5lpecetctCFzLfbk3iJhpzupig9KOC87QvqyxJOiGgtpCl5XPthk-YM7i57afRn_n2IgmwyB-5-Txd2lcVLmGTE3KXgn2Px9xr2DH7rXOUZuQ-E8uhWl6n_3iIDX_XNiq5GvOTLMHQ9cLvHM9M3IqdO/s1091/Firewall%20Arch.jpg" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The diagram gives an overview of how various components in the OS are connected together to implement the firewall. A user would interact with the Windows Defender firewall using the GUI, or a command line interface such as PowerShell's </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/powershell/module/netsecurity/">NetSecurity</a></span><span> module. This interface communicates with the </span><span class="c7">Windows Defender Firewall Service (MPSSVC)</span><span class="c2"> over RPC to query and modify the firewall rules.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>MPSSVC converts its ruleset to the lower-level WFP firewall filters and sends them over RPC to the </span><span class="c7">Base Filtering Engine (BFE)</span><span> service. These filters are then uploaded to the </span><span class="c7">TCP/IP driver (TCPIP.SYS)</span><span> in the kernel which is where the firewall processing is handled. The device objects (such as </span><span class="c7">\Device\WFP</span><span class="c2">) which the TCP/IP driver exposes are secured so that only the BFE service can access them. This means all access to the kernel firewall needs to be mediated through the service.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">When an application, such as a Web Browser, creates a new network socket the AFD driver responsible for managing sockets will communicate with the TCP/IP driver to configure the socket for IP. At this point the TCP/IP driver will capture the security context of the creating process and store that for later use by the firewall. When an operation is performed on the socket, such as making or accepting a new connection, the firewall filters will be evaluated. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">The evaluation is handled primarily by the NETIO driver as well as registered callout drivers. These callout drivers allow for more complex firewall rules to be implemented as well as inspecting and modifying network traffic. The drivers can also forward checks to user-mode services. As an example, the ability to forward checks to user mode allows the Windows Defender Firewall to display a UI when an unknown application listens on a wildcard address, as shown below.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"></p> <p class="c4"><span class="c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNv1FWMhoSZ5e-wPcjKJV_ipVdX9a_2iuNy_QNl26CtdQWyoZHd9ZSvQ8hO6wdIaz2oG8Vk6YfVwsZyMq_RuJ9baGvP-voqdTEmU46JREeXBBFzZyss8GUHf4lBfDxnqYlXNmGBxdWwdB383FP7XoYYMvb8W0aQ7g1D5esP1jxsczHRCYIsMN0gI7t/s790/firewall_access.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Dialog displayed by the Windows Firewall service when an unknown application tries to listen for incoming connections." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNv1FWMhoSZ5e-wPcjKJV_ipVdX9a_2iuNy_QNl26CtdQWyoZHd9ZSvQ8hO6wdIaz2oG8Vk6YfVwsZyMq_RuJ9baGvP-voqdTEmU46JREeXBBFzZyss8GUHf4lBfDxnqYlXNmGBxdWwdB383FP7XoYYMvb8W0aQ7g1D5esP1jxsczHRCYIsMN0gI7t/s790/firewall_access.png" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The end result of the evaluation is whether the operation is permitted or blocked. The behavior of a block depends on the operation. If an outbound connection is blocked the caller is notified. If an inbound connection is blocked the firewall will drop the packets and provide no notification to the peer, such as a TCP Reset or ICMP response. This default drop behavior can be changed through a </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-gpfas/e0e681d3-0468-4796-b541-c5f9945041d8">system wide configuration change</a></span><span class="c2">. Let's dig into more detail on how the rules are configured for evaluation.</span></p><h2 class="c26" id="h.oda8qztbl83f"><span class="c25">Layers, Sublayers and Filters</span></h2> <p class="c4"><span class="c2">The firewall rules are configured using three types of object: layers, sublayers and filters as shown in the following diagram.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"></p> <p class="c4"><span class="c34 c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm2IvcV_nSSWmbQ1uTzwO03BEMF7wyH8DDc2mWCCu-PViM4jbMpPwah8ebnA1odBcaWVvwekj-cGIYciZ3UZ_pGTMM34lpm2NZIQzgZOKoH07gFIRa97-amF7hD6wzly5vupgES7ZNzS9xUVslOJTF_OT06ip5OqlowrZVFljUwviI6S--F4RjmcWV/s554/Layers%20and%20Filtering.jpg" style="display: block; padding: 1em 0;text-align: center;"><img alt="Diagram showing the relationship between layers, sublayers and filters. Each layer can have one or more sublayers which in turn has one or more associated filters." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm2IvcV_nSSWmbQ1uTzwO03BEMF7wyH8DDc2mWCCu-PViM4jbMpPwah8ebnA1odBcaWVvwekj-cGIYciZ3UZ_pGTMM34lpm2NZIQzgZOKoH07gFIRa97-amF7hD6wzly5vupgES7ZNzS9xUVslOJTF_OT06ip5OqlowrZVFljUwviI6S--F4RjmcWV/s554/Layers%20and%20Filtering.jpg" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The firewall layer is used to categorize the network operation to be evaluated. For example there are separate layers for inbound and outbound packets. This is typically further differentiated by IP version, so there are separate IPv4 and IPv6 layers for inbound and outbound packets. While the firewall is primarily focussed on IP traffic there does exist limited MAC and Virtual Switch layers to perform specialist firewalling operations. You can find the list of pre-defined layers on MSDN </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/management-filtering-layer-identifiers-">here</a></span><span>. As the WFP needs to know what layer handles which operation there's no way for additional layers to be added to the system by a third-party application.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>When a packet is evaluated by a layer the WFP performs </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/filter-arbitration">Filter Arbitration</a></span><span>. This is a set of rules which determine the order of evaluation of the filters. First WFP</span><span class="c2"> enumerates all registered filters which are associated with the layer's unique GUID. Next, WFP groups the filters by their sublayer's GUID and orders the filter groupings by a weight value which was specified when the sublayer was registered. Finally, WFP evaluates each filter according to the order based on a weight value specified when the filter was registered.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">For every filter, WFP checks if the list of conditions match the packet and its associated meta-data. If the conditions match then the filter performs a specified action, which can be one of the following:</span></p><ul style="padding: 0;" class="c17 lst-kix_uwx5zr9ju08v-0 start"><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">Permit</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">Block</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">Callout Terminating</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">Callout Unknown</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">Callout Inspection</span></li></ul> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>If the action is </span><span class="c7">Permit </span><span>or </span><span class="c7">Block</span><span> then the filter evaluation for the current sublayer is terminated with that action as the result. If the action is a callout then WFP will invoke the filter's registered callout driver's </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fwpsk/nc-fwpsk-fwps_callout_classify_fn0">classify function</a></span><span> to perform additional checks. The classify function can evaluate the packet and its meta-data and specify a final result of </span><span class="c7">Permit</span><span>, </span><span class="c7">Block </span><span>or additionally </span><span class="c7">Continue </span><span>which indicates the filter should be ignored. In general if the action is </span><span class="c7">Callout Terminating</span><span> then it should only set </span><span class="c7">Permit </span><span>and </span><span class="c7">Block</span><span>, and if it's </span><span class="c7">Callout Inspection</span><span> then it should only set </span><span class="c7">Continue</span><span>. The </span><span class="c7">Callout Unknown</span><span class="c2"> action is for callouts which might terminate or might not depending on the result of the classification.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Once a terminating filter has been evaluated WFP stops processing that sublayer. However, WFP will continue to process the remaining sublayers in the same way regardless of the final result. In general if any sublayer returns a </span><span class="c7">Block</span><span> result </span><span>then the packet will be blocked, otherwise it'll be permitted. This means that if a higher priority sublayer's result is </span><span class="c7">Permit</span><span class="c2">, it can still be blocked by a lower-priority sublayer.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>A filter can be configured with the </span><span class="c7">FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT</span><span> flag which indicates that the result should be considered “hard” allowing a higher priority filter to permit a packet which can't be overridden by a lower-priority blocking filter. The rules for the final result are even more complex than I make out including soft blocks and vetos, refer to the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/filter-arbitration">page in MSDN</a></span><span class="c2"> for more information.</span></p> <p class="c4"><span class="c2"> </span></p> <p class="c4"><span>To simplify the classification of network traffic, WFP provides a set of stateful layers which correspond to major network events such as TCP connection and port binding. The stateful filtering is referred to as </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/application-layer-enforcement--ale-">Application Layer Enforcement (ALE)</a></span><span>. For example the </span><span class="c7">FWPM_LAYER_ALE_AUTH_CONNECT_V4</span><span class="c2"> layer will be evaluated when a TCP connection using IPv4 is being made.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>For any given connection it will only be evaluated once, not for every packet associated with the TCP connection handshake. In general these </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/ale-layers">ALE layers</a></span><span class="c2"> are the ones we'll focus on when inspecting the firewall configuration, as they're the most commonly used. The three main ALE layers you're going to need to inspect are the following:</span></p> <p class="c3"><span class="c2"></span></p><a id="t.3aaa255ecc1a81c80e51f0412c1f656fa6f40adc"></a><a id="t.0"></a><table class="c10"><tbody><tr class="c12"><td class="c30" colspan="1" rowspan="1"> <p class="c1"><span class="c5">Name</span></p></td><td class="c28" colspan="1" rowspan="1"> <p class="c1"><span class="c5">Description</span></p></td></tr><tr class="c12"><td class="c30" colspan="1" rowspan="1"> <p class="c1"><span class="c5">FWPM_LAYER_ALE_AUTH_CONNECT_V4/6</span></p></td><td class="c28" colspan="1" rowspan="1"> <p class="c1"><span class="c5">Processed when TCP connect() called.</span></p></td></tr><tr class="c12"><td class="c30" colspan="1" rowspan="1"> <p class="c1"><span class="c5">FWPM_LAYER_ALE_AUTH_LISTEN_V4/6</span></p></td><td class="c28" colspan="1" rowspan="1"> <p class="c1"><span class="c5">Processed when TCP listen() called.</span></p></td></tr><tr class="c12"><td class="c30" colspan="1" rowspan="1"> <p class="c1"><span class="c5">FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4/6</span></p></td><td class="c28" colspan="1" rowspan="1"> <p class="c1"><span class="c5">Processed when a packet/connection is received.</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>What layers are used and in what order they are evaluated depend on the specific operation being performed. You can find the list of the layers for TCP packets </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/tcp-packet-flows">here</a></span><span> and UDP packets </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/fwp/udp-packet-flows">here</a></span><span class="c2">. Now, let's dig into how filter conditions are defined and what information they can check. </span></p><h2 class="c26" id="h.b12oj4ik1roh"><span class="c25">Filter Conditions</span></h2> <p class="c4"><span class="c2">Each filter contains an optional list of conditions which are used to match a packet. If no list is specified then the filter will always match any incoming packet and perform its defined action. If more than one condition is specified then the filter is only matched if all of the conditions match. If you have multiple conditions of the same type they're OR'ed together, which allows a single filter to match on multiple values.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">Each condition contains three values:</span></p><ul style="padding: 0;" class="c17 lst-kix_w3t1hchiy4w5-0 start"><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">The layer field to check.</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">The value to compare against.</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">The match type, for example the packet value and the condition value are equal.</span></li></ul> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">Each layer has a list of fields that will be populated whenever a filter's conditions are checked. The field might directly reflect a value from the packet, such as the destination IP address or the interface the packet is traversing. Or it could be a metadata value, such as the user identity of the process which created the socket. Some common fields are as follows:</span></p> <p class="c3"><span class="c2"></span></p><a id="t.cd771d5d37490ff376385a74cca3438891315752"></a><a id="t.1"></a><table class="c10"><tbody><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c22 c7">Field Type</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c7 c22">Description</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c32">FWPM_CONDITION_IP_REMOTE_ADDRESS</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The remote IP address.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_IP_LOCAL_ADDRESS</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The local IP address.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_IP_PROTOCOL</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The IP protocol type, e.g. TCP or UDP</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_IP_REMOTE_PORT</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The remote protocol port.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_IP_LOCAL_PORT</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The local protocol port.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_ALE_USER_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The user's identity.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_ALE_REMOTE_USER_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The remote user's identity.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_ALE_APP_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The path to the socket's executable.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_ALE_PACKAGE_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The user's AppContainer package SID.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_FLAGS</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">A set of additional flags.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_ORIGINAL_PROFILE_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The source network interface profile.</span></p></td></tr><tr class="c12"><td class="c6" colspan="1" rowspan="1"> <p class="c1"><span class="c9">FWPM_CONDITION_CURRENT_PROFILE_ID</span></p></td><td class="c15" colspan="1" rowspan="1"> <p class="c1"><span class="c2">The current network interface profile.</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The value to compare against the field can take different values depending on the field being checked. For example the field </span><span class="c7">FWPM_CONDITION_IP_REMOTE_ADDRESS</span><span class="c2"> can be compared to IPv4 or IPv6 addresses depending on the layer it's used in. The value can also be a range, allowing a filter to match on an IP address within a bounded set of addresses.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> and </span><span class="c7">FWPM_CONDITION_ALE_PACKAGE_ID</span><span> conditions are based on the access token captured when creating the TCP or UDP socket. The </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> stores a security descriptor which is used with an access check with the creator's token. If the token is granted access then the condition is considered to match. For </span><span class="c7">FWPM_CONDITION_ALE_PACKAGE_ID</span><span> the condition checks the package SID of the AppContainer token. If the token is not an AppContainer then the filtering engine sets the package SID to the </span><span class="c7">NULL SID (S-1-0-0)</span><span class="c2">. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The </span><span class="c7">FWPM_CONDITION_ALE_REMOTE_USER_ID</span><span> is similar to the </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> condition but compares against the remote authenticated user. </span><span>In most cases sockets are not authenticated</span><span class="c2">, however if IPsec is in use that can result in a remote user token being available to compare. It's also used in some higher-level layers such as RPC filters.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">The match type can be one of the following:</span></p><ul style="padding: 0;" class="c17 lst-kix_9fia5rt9e6y-0 start"><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_EQUAL</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_EQUAL_CASE_INSENSITIVE</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_FLAGS_ALL_SET</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_FLAGS_ANY_SET</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_FLAGS_NONE_SET</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_GREATER</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_GREATER_OR_EQUAL</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_LESS</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_LESS_OR_EQUAL</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_NOT_EQUAL</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_NOT_PREFIX</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_PREFIX</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">FWP_MATCH_RANGE</span></li></ul> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">The match types should hopefully be self explanatory based on their names. How the match is interpreted depends on the field's type and the value being used to check against. </span></p><h2 class="c26" id="h.xjr0mp71omhg"><span class="c25">Inspecting the Firewall Configuration</span></h2> <p class="c4"><span class="c2">We now have an idea of the basics of how WFP works to filter network traffic. Let's look at how to inspect the current configuration. We can't use any of the normal firewall commands or UIs such as the PowerShell NetSecurity module as I already mentioned these represent the Windows Defender view of the firewall. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Instead we need to use the RPC APIs BFE exposes to access the configuration, for example you can access a filter using the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmfiltergetbykey0">FwpmFilterGetByKey0</a></span><span class="c2"> API. Note that the BFE maintains security descriptors to restrict access to WFP objects. By default nothing can be accessed by non-administrators, therefore you'd need to call the RPC APIs while running as an administrator.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>You could implement your own tooling to call all the different APIs, but it'd be much easier if someone had already done it for us. For built-in tools the only one I know of is using </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-server/networking/technologies/netsh/netsh-contexts">netsh</a></span><span> with the </span><span class="c7">wfp </span><span class="c2">namespace. For example to dump all the currently configured filters you can use the following command as an administrator:</span></p> <p class="c3"><span class="c2"></span></p><a id="t.8d2c0a0c5175286b751a70c2aa752c5ba3d21d57"></a><a id="t.2"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c23">netsh wfp show filters file = -</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">This will print all filters in an XML format to the console. Be prepared to wait a while for the output to complete. You can also dump straight to a file. Of course you now need to interpret the XML results. It is possible to also specify certain parameters, such as local and remote addresses to reduce the output to only matching filters.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Processing an XML file doesn't sound too appealing. To make the firewall configuration easier to inspect I've added many of the BFE APIs to my </span><span class="c13"><a class="c111" href="https://www.powershellgallery.com/packages/NtObjectManager/1.1.31">NtObjectManager</a></span><span class="c2"> PowerShell module from version 1.1.32 onwards. The module exposes various commands which will return objects representing the current WFP configuration which you can easily use to inspect and group the results however you see fit.</span></p><h3 class="c33" id="h.325g1dtbp951"><span class="c16">Layer Configuration</span></h3> <p class="c4"><span>Even though the layers are predefined in the WFP implementation it's still useful to be able to query the details about them. For this you can use the </span><span class="c7">Get-FwLayer</span><span class="c2"> command.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.0b982091d7807de5c519581743d412c34929df42"></a><a id="t.3"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwLayer</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">KeyName Name </span></p> <p class="c1"><span class="c0">------- ---- </span></p> <p class="c1"><span class="c0">FWPM_LAYER_OUTBOUND_IPPACKET_V6 Outbound IP Packet v6 Layer </span></p> <p class="c1"><span class="c0">FWPM_LAYER_IPFORWARD_V4_DISCARD IP Forward v4 Discard Layer </span></p> <p class="c1"><span class="c0">FWPM_LAYER_ALE_AUTH_LISTEN_V4 ALE Listen v4 Layer</span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The output shows the SDK name for the layer, if it has one, and the name of the layer that the BFE service has configured. The layer can be queried by its SDK name, its GUID or a numeric ID, which we will come back to later. As we mostly only care about the ALE layers then there's a special </span><span class="c7">AleLayer</span><span class="c2"> parameter to query a specific layer without needing to remember the full name or ID.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.106a6412b9d80843ea8c42b8c5d55608f9a04818"></a><a id="t.4"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">(Get-FwLayer -AleLayer ConnectV4).Fields</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">KeyName Type DataType </span></p> <p class="c1"><span class="c0">------- ---- -------- </span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_APP_ID RawData ByteBlob </span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID RawData TokenAccessInformation</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_LOCAL_ADDRESS IPAddress UInt32 </span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Each layer exposes the list of fields which represent the conditions which can be checked in that layer, you can access the list through the </span><span class="c7">Fields</span><span class="c2"> property. The output shown above contains a few of the condition types we saw earlier in the table of conditions. The output also shows the type of the condition and the data type you should provide when filtering on that condition.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.4b73d7702abe7cdd68619f2dfca206c3e7e04511"></a><a id="t.5"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwSubLayer | Sort-Object Weight | Select KeyName, Weight</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">KeyName Weight</span></p> <p class="c1"><span class="c0">------- ------</span></p> <p class="c1"><span class="c0">FWPM_SUBLAYER_INSPECTION 0</span></p> <p class="c1"><span class="c0">FWPM_SUBLAYER_TEREDO 1</span></p> <p class="c1"><span class="c0">MICROSOFT_DEFENDER_SUBLAYER_FIREWALL 2</span></p> <p class="c1"><span class="c0">MICROSOFT_DEFENDER_SUBLAYER_WSH 3</span></p> <p class="c1"><span class="c0">MICROSOFT_DEFENDER_SUBLAYER_QUARANTINE 4 </span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>You can also inspect the sublayers in the same way, using the </span><span class="c7">Get-FwSubLayer</span><span class="c2"> command as shown above. The most useful information from the sublayer is the weight. As mentioned earlier this is used to determine the ordering of the associated filters. However, as we'll see you rarely need to query the weight yourself.</span></p><h3 class="c33" id="h.kx4txr7dcefa"><span class="c16">Filter Configuration</span></h3> <p class="c4"><span>Enforcing the firewall rules is up to the filters. You can enumerate all filters using the </span><span class="c7">Get-FwFilter</span><span class="c2"> command.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.9a80e20803cd73322ecbf4a3ef4396e7654974d5"></a><a id="t.6"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">FilterId ActionType Name</span></p> <p class="c1"><span class="c0">-------- ---------- ----</span></p> <p class="c1"><span class="c0">68071 Block Boot Time Filter</span></p> <p class="c1"><span class="c0">71199 Permit @FirewallAPI.dll,-80201</span></p> <p class="c1"><span class="c0">71350 Block Block inbound traffic to dmcertinst.exe</span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The default output shows the ID of a filter, the action type and the user defined name. The filter objects returned also contain the layer and sublayer identifiers as well as the list of matching conditions for the filter. As inspecting the filter is going to be the most common operation the module provides the </span><span class="c7">Format-FwFilter</span><span class="c2"> command to format a filter object in a more readable format.</span></p><a id="t.46076ba715cf35b252b036a49f94a51358ed2a50"></a><a id="t.7"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 71350 | Format-FwFilter</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : Block inbound traffic to dmcertinst.exe</span></p> <p class="c1"><span class="c0">Action Type: Block</span></p> <p class="c1"><span class="c0">Key : c391b53a-1b98-491c-9973-d86e23ea8a84</span></p> <p class="c1"><span class="c0">Id : 71350</span></p> <p class="c1"><span class="c0">Description:</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : Indexed</span></p> <p class="c1"><span class="c0">Weight : 549755813888</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_APP_ID Equal </span></p> <p class="c1"><span class="c0">\device\harddiskvolume3\windows\system32\dmcertinst.exe</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The formatted output contains the layer and sublayer information, the assigned weight of the filter and the list of conditions. The layer is </span><span class="c7">FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span><span> which handles new incoming connections. The sublayer is MICROSOFT_DEFENDER_SUBLAYER_WSH which is used to group </span><span class="c7">Windows Service Hardening</span><span class="c2"> rules which apply regardless of the normal firewall configuration.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>In this example the filter only matches on the socket creator process executable's path. The end result if the filter matches the current state is for the IPv4 TCP network connection to be blocked at the </span><span class="c7">MICROSOFT_DEFENDER_SUBLAYER_WSH</span><span class="c2"> sublayer. As already mentioned it now won't matter if a lower priority layer would permit the connection if the block is enforced.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">How can we determine the ordering of sublayers and filters? You could manually extract the weights for each sublayer and filter and try and order them, and hopefully the ordering you come up with matches what WFP uses. A much simpler approach is to specify a flag when enumerating filters for a particular layer to request the BFE APIs sort the filters using the canonical ordering. </span></p> <p class="c3"><span class="c2"></span></p><a id="t.bbb84a15b64f223a1178403511b7ef1ab83275c5"></a><a id="t.8"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -AleLayer ConnectV4 -Sorted</span></p> <p class="c1 c21"><span class="c8"></span></p> <p class="c1"><span class="c0">FilterId ActionType Name</span></p> <p class="c1"><span class="c0">-------- ---------- ----</span></p> <p class="c1"><span class="c0">65888 Permit Interface Un-quarantine filter</span></p> <p class="c1"><span class="c0">66469 Block AppContainerLoopback</span></p> <p class="c1"><span class="c0">66467 Permit AppContainerLoopback</span></p> <p class="c1"><span class="c0">66473 Block AppContainerLoopback</span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The </span><span class="c7">Sorted</span><span class="c2"> parameter specifies the flag to sort the filters. You can now go through the list of filters in order and try and work out what would be the matched filter based on some criteria you decide on. Again it'd be helpful if we could get the BFE service to do more of the hard work in figuring out what rules would apply given a particular process. For this we can specify some of the metadata that represents the connection being made and get the BFE service to only return filters which match on their conditions.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.e62ff3a5bf06c1a80c9f1aceaa36b7c60cdfdf25"></a><a id="t.9"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">$template = New-FwFilterTemplate -AleLayer ConnectV4 -Sorted</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$fs = Get-FwFilter -Template $template</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$fs.Count</span></p> <p class="c1"><span class="c0">65</span></p> <p class="c1 c21"><span class="c8"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -ProcessId $pid</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$addr = Resolve-DnsName "www.google.com" -Type A</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -IPAddress $addr.Address -Port 80</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -ProtocolType Tcp</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -ConditionFlags 0</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$template.Conditions</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value </span></p> <p class="c1"><span class="c0">------------ --------- ----- </span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_APP_ID Equal \device\harddisk...</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal FirewallTokenInformation </span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID Equal S-1-0-0</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Equal 142.250.72.196</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_PORT Equal 80</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_PROTOCOL Equal Tcp</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_FLAGS Equal None</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$fs = Get-FwFilter -Template $template</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$fs.Count</span></p> <p class="c1"><span class="c14">2</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>To specify the metadata we need to create an enumeration template using the </span><span class="c7">New-FwFilterTemplate</span><span> command. We specify the Connect IPv4 layer as well as requesting that the results are sorted. Using this template with the </span><span class="c7">Get-FwFilter</span><span class="c2"> command returns 65 results (on my machine). </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Next we add some metadata, first from the current powershell process. This populates the App ID with the executable path as well as token information such as the user ID and package ID of an AppContainer. We then add details about the target connection request, specifying a TCP connection to </span><span class="c7">www.google.com</span><span class="c2"> on port 80. Finally we add some condition flags, we'll come back to these flags later.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">Using this new template results in only 2 filters whose conditions will match the metadata. Of course depending on your current configuration the number might be different. In this case 2 filters is much easier to understand than 65. If we format those two filter we see the following:</span></p> <p class="c3"><span class="c2"></span></p><a id="t.14d3d8c28447d038e3573ae296e536607295fcde"></a><a id="t.10"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">$fs | Format-FwFilter</span></p> <p class="c1 c21"><span class="c8"></span></p> <p class="c1"><span class="c0">Name : Default Outbound</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 07ba2a96-0364-4759-966d-155007bde926</span></p> <p class="c1"><span class="c0">Id : 67989</span></p> <p class="c1"><span class="c0">Description: Default Outbound</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_FIREWALL</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 9223372036854783936</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ORIGINAL_PROFILE_ID Equal Public </span></p> <p class="c1"><span class="c0">FWPM_CONDITION_CURRENT_PROFILE_ID Equal Public</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : Default Outbound</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 36da9a47-b57d-434e-9345-0e36809e3f6a</span></p> <p class="c1"><span class="c0">Id : 67993</span></p> <p class="c1"><span class="c0">Description: Default Outbound</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_FIREWALL</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 3458764513820540928</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Both of the two filters permit the connection and based on the name they're the default backstop when no other filters match. It's possible to configure each network profile with different default backstops. In this case the default is to permit outbound traffic. We have two of them because both match all the metadata we provided, although if we'd specified a profile other than </span><span class="c7">Public </span><span class="c2">then we'd only get a single filter.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Can we prove that this is the filter which matches a TCP connection? Fortunately we can: WFP supports gathering network events related to the firewall. An event includes the filter which permitted or denied the network request, and we can then compare it to our two filters to see if one of them matched. You can use the </span><span class="c7">Get-FwNetEvent</span><span class="c2"> command to read the current circular buffer of events.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.fd84f43785a2f731e158c79bb81d95af29cb6154"></a><a id="t.11"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c23">Set-FwEngineOption -NetEventMatchAnyKeywords ClassifyAllow</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$s = [System.Net.Sockets.TcpClient]::new($addr.IPAddress, 80)</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Set-FwEngineOption -NetEventMatchAnyKeywords None</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$ev_temp = New-FwNetEventTemplate -Condition $template.Conditions</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $ev_temp -NetEventType ClassifyAllow</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwNetEvent -Template $ev_temp | Format-List</span></p> <p class="c1"><span class="c14">FilterId : </span><span class="c0 c36">67989</span></p> <p class="c1"><span class="c0">LayerId : 48</span></p> <p class="c1"><span class="c0">ReauthReason : 0</span></p> <p class="c1"><span class="c0">OriginalProfile : Public</span></p> <p class="c1"><span class="c0">CurrentProfile : Public</span></p> <p class="c1"><span class="c0">MsFwpDirection : 0</span></p> <p class="c1"><span class="c0">IsLoopback : False</span></p> <p class="c1"><span class="c0">Type : ClassifyAllow</span></p> <p class="c1"><span class="c0">Flags : IpProtocolSet, LocalAddrSet, RemoteAddrSet, ...</span></p> <p class="c1"><span class="c0">Timestamp : 8/5/2021 11:24:41 AM</span></p> <p class="c1"><span class="c0">IPProtocol : Tcp</span></p> <p class="c1"><span class="c0">LocalEndpoint : 10.0.0.101:63046</span></p> <p class="c1"><span class="c0">RemoteEndpoint : 142.250.72.196:80</span></p> <p class="c1"><span class="c0">ScopeId : 0</span></p> <p class="c1"><span class="c0">AppId : \device\harddiskvolume3\windows\system32\wind...</span></p> <p class="c1"><span class="c0">UserId : S-1-5-21-4266194842-3460360287-487498758-1103</span></p> <p class="c1"><span class="c0">AddressFamily : Inet</span></p> <p class="c1"><span class="c0">PackageSid : S-1-0-0</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>First we enable the </span><span class="c7">ClassifyAllow </span><span>event, which is generated when a firewall event is permitted. By default only firewall blocks are recorded using the </span><span class="c7">ClassifyDrop </span><span>event to avoid filling the small network event log with too much data. Next we make a connection to the Google web server we queried earlier to generate an event. We then disable the </span><span class="c7">ClassifyAllow </span><span class="c2">events again to reduce the risk we'll lose the event.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Next we can query for the current stored events using </span><span class="c7">Get-FwNetEvent</span><span>. To limit the network events returned to us we can specify a template in a similar way to when we queried for filters. In this case we create a new template using the </span><span class="c7">New-FwNetEventTemplate</span><span> command and copy the existing conditions from our filter template. We then add a condition to match on only </span><span class="c7">ClassifyAllow </span><span class="c2">events.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Formatting the results we can see the network connection event to TCP port 80. Crucially if you compare the </span><span class="c7">FilterId </span><span>value to the </span><span class="c7">Id </span><span class="c2">fields in the two enumerated filters we match the first filter. This gives us confidence that we have a basic understanding of how the filtering works. Let's move on to running some tests to determine how the AppContainer network restrictions are implemented through WFP.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Worth noting at this point that because the network event buffer can be small, of the order of 30-40 events depending on load, it's possible on a busy server that events might be lost before you query for them. You can get a real-time trace of events by using the </span><span class="c7">Start-FwNetEventListener</span><span class="c2"> command to avoid losing events.</span></p><h2 class="c26" id="h.o4gwdt98hsge"><span class="c25">Callout Drivers</span></h2> <p class="c4"><span>As mentioned a developer can implement their own custom functionality to inspect and modify network traffic. This functionality is used by various different products, ranging from AV to scan your network traffic for badness to NMAP's NPCAP </span><span class="c13"><a class="c111" href="https://github.com/nmap/npcap/blob/master/packetWin7/npf/npf/Loopback.c">capturing loopback traffic</a></span><span class="c2">. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>To set up a callout the developer needs to do two things. First they need to register its callback functions for the callout using the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/fwpsk/nf-fwpsk-fwpscalloutregister2">FwpmCalloutRegister</a></span><span> API in the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-hardware/drivers/network/roadmap-for-developing-wfp-callout-drivers">kernel driver</a></span><span>. Second they need to create a filter to use the callout by specifying the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/api/fwpmtypes/ns-fwpmtypes-fwpm_filter0">providerContextKey</a></span><span class="c2"> GUID and one of the action types which invoke a callout.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>You can query the list of registered callouts using the </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmcalloutenum0">FwpmCalloutEnum0</a></span><span> API in user-mode. I expose this API through the </span><span class="c7">Get-FwCallout</span><span class="c2"> command.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.2396b12a8028cda722ae1b6b81be45210a76e4c9"></a><a id="t.12"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwCallout | Sort CalloutId | Select CalloutId, KeyName</span></p> <p class="c1"><span class="c0">CalloutId KeyName</span></p> <p class="c1"><span class="c0">--------- -------</span></p> <p class="c1"><span class="c0"> 1 FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4</span></p> <p class="c1"><span class="c0"> 2 FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6</span></p> <p class="c1"><span class="c0"> 3 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4</span></p> <p class="c1"><span class="c0"> 4 FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6</span></p> <p class="c1"><span class="c0"> 5 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V4</span></p> <p class="c1"><span class="c0"> 6 FWPM_CALLOUT_IPSEC_INBOUND_TUNNEL_V6</span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The above output shows the callouts listed by their callout ID numbers. </span><span>The ID number is key</span><span> to finding the callback functions in the kernel. </span><span>There doesn't seem to be a way of enumerating the addresses of callout functions directly (at least from user mode). </span><span class="c13"><a class="c111" href="https://codemachine.com/articles/find_wfp_callouts.html">This article</a></span><span class="c2"> shows a basic approach to extract the callback functions using a kernel debugger, although it's a little out of date.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The NETIO driver stores all registered callbacks in a large array, the index being the callout ID. If you want to find a specific callout then find the base of the array using the description in the article then just calculate the offset based on a single callout structure and the index. For example on Windows 10 21H1 x64 the following command will dump a callout's classify callback function. Replace N with the callout ID, the magic numbers 198 and 50 are the offset into the </span><span class="c7">gWfpGlobal </span><span class="c2">global data table and the size of a callout entry which you can discover through analyzing the code.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.f1b35794a1a1f1389c2e1ccd4bbd950f2fba28f2"></a><a id="t.13"></a><table class="c10"><tbody><tr class="c12"><td class="c40" colspan="1" rowspan="1"> <p class="c1"><span class="c18">0: kd> ln poi(poi(poi(NETIO!gWfpGlobal)+198)+(50*N)+10)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>If you're in kernel mode there's an undocumented </span><span class="c7">KfdGetRefCallout</span><span> function (and a corresponding </span><span class="c7">KfdDeRefCallout</span><span class="c2"> to decrement the reference) exported by NETIO which will return a pointer to the internal callout structure based on the ID avoiding the need to extract the offsets from disassembly. </span></p><h2 class="c26" id="h.uaafke7sw96h"><span class="c25">AppContainer Network Restrictions</span></h2> <p class="c4"><span>The basics of accessing the network from an AppContainer sandbox is documented by Microsoft. Specifically the lowbox token used for the sandbox needs to have one or more </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/uwp/packaging/app-capability-declarations">capabilities</a></span><span class="c2"> enabled to grant access to the network. The three capabilities are:</span></p> <p class="c3"><span class="c2"></span></p><ul style="padding: 0;" class="c17 lst-kix_a73fbzj8zqo2-0 start"><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">internetClient - Grants client access to the Internet</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">internetClientServer - Grants client and server access to the Internet</span></li><li style="margin-left: 46pt;" class="c4 c29 li-bullet-0"><span class="c2">privateNetworkClientServer - Grants client and server access to local private networks.</span></li></ul><h3 class="c33" id="h.bfr60co2drv2"><span class="c16">Client Capabilities</span></h3> <p class="c4"><span>Pretty much all Windows Store applications are granted the </span><span class="c7">internetClient </span><span class="c2">capability as accessing the Internet is a thing these days. Even the built-in calculator has this capability, presumably so you can fill in feedback on how awesome a calculator it is.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"></p> <p class="c4"><span class="c7 c34"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXeEy36ANfMO26FR3xri1W3D4E7FWau7UbdCWfgwRSEeC7fRTNzP-LEhxN3kj1NuoVoKk4OFQAbTfryVRAI--qILEx1jsL7H6DqdjjNEn-zf7SN_x6lKH49rPKFrsWN5K-DbusILAYUjALq-h_kZbtrtmPMDlptlPP9IKvirCKzbL4yoe6dd6mz8Z6/s789/calculator.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Image showing the list of capabilities granted to Windows calculator application showing the “Your Internet Connection” capability is granted." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXeEy36ANfMO26FR3xri1W3D4E7FWau7UbdCWfgwRSEeC7fRTNzP-LEhxN3kj1NuoVoKk4OFQAbTfryVRAI--qILEx1jsL7H6DqdjjNEn-zf7SN_x6lKH49rPKFrsWN5K-DbusILAYUjALq-h_kZbtrtmPMDlptlPP9IKvirCKzbL4yoe6dd6mz8Z6/s789/calculator.png" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c34 c7"></span></p> <p class="c4"><span>However, this shouldn't grant the ability to act as a network server, for that you need the </span><span class="c7">internetClientServer</span><span> capability. Note that Windows defaults to blocking incoming connections, so just because you have the server capability still doesn't ensure you can receive network connections. The final capability is </span><span class="c7">privateNetworkClientServer</span><span class="c2"> which grants access to private networks as both a client and a server. What is the internet and what is private isn't made immediately clear, hopefully we'll find out from inspecting the firewall configuration.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.726560f1dcc0a391c92acef89ed310e6111d9ba1"></a><a id="t.14"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">$token = Get-NtToken -LowBox -PackageSid TEST</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$addr = Resolve-DnsName "www.google.com" -Type A</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$sock = Invoke-NtToken $token {</span></p> <p class="c1"><span class="c8">>> [System.Net.Sockets.TcpClient]::new($addr.IPAddress, 80)</span></p> <p class="c1"><span class="c8">>> }</span></p> <p class="c1"><span class="c20">Exception calling ".ctor" with "2" argument(s): "An attempt was made to access a socket in a way forbidden by its access permissions 216.58.194.164:80"</span></p> <p class="c1 c21"><span class="c20"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$template = New-FwNetEventTemplate</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -IPAddress $addr.IPAddress -Port 80</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -NetEventType ClassifyDrop</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwNetEvent -Template $template | Format-List</span></p> <p class="c1 c21"><span class="c8"></span></p> <p class="c1"><span class="c0">FilterId : 71079</span></p> <p class="c1"><span class="c0">LayerId : 48</span></p> <p class="c1"><span class="c0">ReauthReason : 0</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 71079 | Format-FwFilter</span></p> <p class="c1"><span class="c0">Name : Block Outbound Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Block</span></p> <p class="c1"><span class="c0">Key : fb8f5cab-1a15-4616-b63f-4a0d89e527f8</span></p> <p class="c1"><span class="c0">Id : 71079</span></p> <p class="c1"><span class="c0">Description: Block Outbound Default Rule</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 274877906944</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>In the above output we first create a lowbox token for testing the AppContainer access. In this example we don't provide any capabilities for the token so we're expecting the network connection should fail. Next we connect a </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=net-5.0">TcpClient</a></span><span class="c2"> socket while impersonating the lowbox token, and the connection is immediately blocked with an error. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>We then get the network event corresponding to the connection request to see what filter blocked the connection. Formatting the filter from the network event we find the “Block Outbound Default Rule”. This will block any AppContainer network connection, based on the </span><span class="c7">FWPM_CONDITION_ALE_PACKAGE_ID</span><span class="c2"> condition which hasn't been permitted by higher priority firewall filters. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Like with the “Default Outbound” filter we saw earlier, this is a backstop if nothing else matches. Unlike that earlier filter the default is to block rather than permit the connection. Another thing to note is the sublayer name. For “Block Outbound Default Rule” it's </span><span class="c7">MICROSOFT_DEFENDER_SUBLAYER_WSH</span><span> which is used for built-in filters which aren't directly visible from the Defender firewall configuration. Whereas </span><span class="c7">MICROSOFT_DEFENDER_SUBLAYER_FIREWALL</span><span class="c2"> is used for “Default Outbound”, which is a lower priority sublayer (based on its weight) and thus would never be evaluated due to the higher priority block.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Okay, we know how connections are blocked. Therefore there must be a higher priority filter which permits the connection within the </span><span class="c7">MICROSOFT_DEFENDER_SUBLAYER_WSH</span><span> sublayer. We could go back to manual inspection, but we might as well just see what the network event shows as the matching filter when we grant the </span><span class="c7">internetClient</span><span> capability.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.11ccdcf255abcf42d021e19361ea96cb417403f3"></a><a id="t.15"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">$cap = Get-NtSid -KnownSid CapabilityInternetClient</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$token = Get-NtToken -LowBox -PackageSid TEST -CapabilitySid $cap</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Set-FwEngineOption -NetEventMatchAnyKeywords ClassifyAllow</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$sock = Invoke-NtToken $token {</span></p> <p class="c1"><span class="c8">>> [System.Net.Sockets.TcpClient]::new($addr.IPAddress, 80)</span></p> <p class="c1"><span class="c23">>> }</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Set-FwEngineOption -NetEventMatchAnyKeywords None</span></p> <p class="c1"><span class="c14">PS> </span><span class="c23">$template = New-FwNetEventTemplate</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -IPAddress $addr.IPAddress -Port 80</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-FwCondition $template -NetEventType ClassifyAllow</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwNetEvent -Template $template | Format-List</span></p> <p class="c1 c21"><span class="c8"></span></p> <p class="c1"><span class="c0">FilterId : 71075</span></p> <p class="c1"><span class="c0">LayerId : 48</span></p> <p class="c1"><span class="c0">ReauthReason : 0</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 71075 | Format-FwFilter</span></p> <p class="c1"><span class="c0">Name : InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 406568a7-a949-410d-adbb-2642ec3e8653</span></p> <p class="c1"><span class="c0">Id : 71075</span></p> <p class="c1"><span class="c0">Description: InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 412316868544</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 0.0.0.0 High: 255.255.255.255</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ORIGINAL_PROFILE_ID Equal Public</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_CURRENT_PROFILE_ID Equal Public</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal </span></p> <p class="c1"><span class="c0">O:LSD:(A;;CC;;;S-1-15-3-1)(A;;CC;;;WD)(A;;CC;;;AN)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>In this example we create a new token using the same package SID but with </span><span class="c7">internetClient </span><span>capability. When we connect the socket we now no longer get an error and the connection is permitted. Checking for the </span><span class="c7">ClassifyAllow </span><span class="c2">event we find the “InternetClient Default Rule” filter matched the connection.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Looking at the conditions we can see that it will only match if the socket creator is in an AppContainer based on the </span><span class="c7">FWPM_CONDITION_ALE_PACKAGE_ID</span><span> condition. The </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> also ensures that it will only match if the creator has the </span><span class="c7">internetCapability </span><span>capability </span><span class="c2">which is S-1-15-3-1 in the SDDL format. This filter is what's granting access to the network.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>One odd thing is in the </span><span class="c7">FWPM_CONDITION_IP_REMOTE_ADDRESS</span><span> condition. It seems to match on all possible IPv4 addresses. Shouldn't this exclude network addresses on our local “private” network? At the very least you'd assume this would block the reserved IP address ranges from </span><span class="c13"><a class="c111" href="https://datatracker.ietf.org/doc/html/rfc1918">RFC1918</a></span><span>? The key to understanding this is the profile ID conditions, which are both set to </span><span class="c7">Public</span><span class="c2">. The computer I'm running these commands on has a single network interface configured to the public profile as shown:</span></p> <p class="c4"></p> <p class="c4"><span class="c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPvmoJTbFhG74JFB0ezY0mbIzZBczuwcx8izCO9EmbCg7WgVdJsBGTPXQGEYHkuhjBmcV26dkB86JLes5iXRML_cJl5Rbn3K0tqCGEzlvY9nIqi6admKcDERezqjrGsc6wVS7tUYbyBWdHvwCC_CR4Bmccy0StC8ZYwoyJXJJ7dpuGPnooNvgCAPRN/s724/profile.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Image showing the option of either Public or Private network profiles." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPvmoJTbFhG74JFB0ezY0mbIzZBczuwcx8izCO9EmbCg7WgVdJsBGTPXQGEYHkuhjBmcV26dkB86JLes5iXRML_cJl5Rbn3K0tqCGEzlvY9nIqi6admKcDERezqjrGsc6wVS7tUYbyBWdHvwCC_CR4Bmccy0StC8ZYwoyJXJJ7dpuGPnooNvgCAPRN/s724/profile.png" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Therefore the firewall is configured to treat all network addresses in the same context, granting the </span><span class="c7">internetClient </span><span>capability access to any address including your local “private” network. This might be unexpected. In fact if you enumerate all the filters on the machine you won't find any filter to match the </span><span class="c7">privateNetworkClientServer </span><span>capability and using the capability will not grant access to any network resource.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>If you switch the network profile to </span><span class="c7">Private</span><span class="c2">, you'll find there's now three “InternetClient Default Rule” filters (note on Windows 11 there will only be one as it uses the OR'ing feature of conditions as mentioned above to merge the three rules together).</span></p> <p class="c3"><span class="c2"></span></p><a id="t.e09797f750872be5db7da696cd4a2a5f50a66242"></a><a id="t.16"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c0">Name : InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 0.0.0.0 High: 10.0.0.0</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ORIGINAL_PROFILE_ID Equal Private</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_CURRENT_PROFILE_ID Equal Private</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 239.255.255.255 High: 255.255.255.255</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 10.255.255.255 High: 224.0.0.0</span></p> <p class="c1"><span class="c0">...</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>As you can see in the first filter, it covers addresses 0.0.0.0 to 10.0.0.0. The machine's private network is 10.0.0.0/8. The profile IDs are also now set to </span><span class="c7">Private</span><span class="c2">. The other two exclude the entire 10.0.0.0/8 network as well as the multicast group addresses from 224.0.0.0 to 240.0.0.0. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The profile ID conditions are important here if you have more than one network interface. For example if you have two, one </span><span class="c7">Public </span><span>and one </span><span class="c7">Private,</span><span> you would get a filter for the </span><span class="c7">Public </span><span>network covering the entire IP address range and the three </span><span class="c7">Private </span><span>ones excluding the private network addresses. The </span><span class="c7">Public </span><span>filter won't match if the network traffic is being sent from the </span><span class="c7">Private </span><span class="c2">network interface preventing the application without the right capability from accessing the private network.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">Speaking of which, we can also now identify the filter which will match the private network capability. There's two, to cover the private network range and the multicast range. We'll just show one of them.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.a018aad6f03aa23b66eb2eed774b31b3af479be6"></a><a id="t.17"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c0">Name : PrivateNetwork Outbound Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : e0194c63-c9e4-42a5-bbd4-06d90532d5e6</span></p> <p class="c1"><span class="c0">Id : 71640</span></p> <p class="c1"><span class="c0">Description: PrivateNetwork Outbound Default Rule</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 36029209335832512</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 10.0.0.0 High: 10.255.255.255</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ORIGINAL_PROFILE_ID Equal Private</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_CURRENT_PROFILE_ID Equal Private</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal </span></p> <p class="c1"><span class="c0">O:LSD:(A;;CC;;;S-1-15-3-3)(A;;CC;;;WD)(A;;CC;;;AN)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>We can see in the </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> condition that the connection would be permitted if the creator has the </span><span class="c7">privateNetworkClientServer </span><span class="c2">capability, which is S-1-15-3-3 in SDDL. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>It is slightly ironic that the </span><span class="c7">Public </span><span>network profile is probably recommended even if you're on your own private network (Windows 11 even makes the recommendation explicit as shown below) in that it should reduce the exposed attack surface of the device from others on the network. However if an AppContainer application with the </span><span class="c7">internetClient </span><span>capability could be compromised it opens up your private network to access where the </span><span class="c7">Private </span><span class="c2">profile wouldn't. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"></p> <p class="c4"><span class="c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6HZRf6o3-m-cyhZsOgNVROEGPDnUi8JQs6bc4ZQhwEM7kvMby2UDr9tA0buQ8jjPQw02T-NtmK6RRX3B4PgwFwOVbAkaOMF8SnE3QsZy8SQNEcU6tcZwsZPuycOSNz_6ZNPIYcXj-MLIInTA0tv94rrsChP6D7bMs1lw4IvXfJIesMARzegEAqQ11/s782/public_network.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Image showing the option of either Public or Private network profiles. This is from Windows 11 where Public is marked as recommended." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6HZRf6o3-m-cyhZsOgNVROEGPDnUi8JQs6bc4ZQhwEM7kvMby2UDr9tA0buQ8jjPQw02T-NtmK6RRX3B4PgwFwOVbAkaOMF8SnE3QsZy8SQNEcU6tcZwsZPuycOSNz_6ZNPIYcXj-MLIInTA0tv94rrsChP6D7bMs1lw4IvXfJIesMARzegEAqQ11/s782/public_network.png" style="max-height: 750; max-width: 600px;" /></a></span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c7">Aside: one thing you might wonder, if your network interface is marked as Private and the AppContainer application only has the internetClient capability, what happens if your DNS server is your local router at 10.0.0.1? Wouldn't the application be blocked from making DNS requests? Windows has a DNS client service which typically is always running. This service is what usually makes DNS requests on behalf of applications as it allows the results to be cached. The RPC server which the service exposes allows callers which have any of the three network capabilities to connect to it and make DNS requests, avoiding the problem. Of course if the service is disabled in-process DNS lookups will start to be used, which could result in weird name resolving issues depending on your network configuration.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>We can now understand how </span><span class="c13"><a class="c111" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2207">issue 2207</a></span><span> I reported to Microsoft bypasses the capability requirements. If in the </span><span class="c7">MICROSOFT_DEFENDER_SUBLAYER_WSH</span><span> sublayer for an outbound connection there are </span><span class="c7">Permit</span><span class="c2"> filters which are evaluated before the “Block Outbound Default Rule” filter then it might be possible to avoid needing capabilities. </span></p> <p class="c3"><span class="c2"></span></p><a id="t.950a47990d20f68e9c5415069eb009e1cf2ed411"></a><a id="t.18"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -AleLayer ConnectV4 -Sorted | </span></p> <p class="c1"><span class="c8">Where-Object SubLayerKeyName -eq MICROSOFT_DEFENDER_SUBLAYER_WSH |</span></p> <p class="c1"><span class="c8">Select-Object ActionType, Name</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c35">Permit Allow outbound TCP traffic from dmcertinst.exe</span></p> <p class="c1"><span class="c0">Permit Allow outbound TCP traffic from omadmclient.exe</span></p> <p class="c1"><span class="c0">Permit Allow outbound TCP traffic from deviceenroller.exe</span></p> <p class="c1"><span class="c0">Permit InternetClient Default Rule</span></p> <p class="c1"><span class="c0">Permit InternetClientServer Outbound Default Rule</span></p> <p class="c1"><span class="c0">Block Block all outbound traffic from SearchFilterHost</span></p> <p class="c1"><span class="c0">Block Block outbound traffic from dmcertinst.exe</span></p> <p class="c1"><span class="c0">Block Block outbound traffic from omadmclient.exe</span></p> <p class="c1"><span class="c0">Block Block outbound traffic from deviceenroller.exe</span></p> <p class="c1"><span class="c31">Block Block Outbound Default Rule</span></p> <p class="c1"><span class="c0">Block WSH Default Outbound Block</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 72753 | Format-FwFilter</span></p> <p class="c1"><span class="c0">Name : Allow outbound TCP traffic from dmcertinst.exe</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 5237f74f-6346-4038-a48d-4b779f862e65</span></p> <p class="c1"><span class="c0">Id : 72753</span></p> <p class="c1"><span class="c0">Description:</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : Indexed</span></p> <p class="c1"><span class="c0">Weight : 422487342972928</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_APP_ID Equal </span></p> <p class="c1"><span class="c0">\device\harddiskvolume3\windows\system32\dmcertinst.exe</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_PROTOCOL Equal Tcp</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>As we can see in the output there are quite a few </span><span class="c7">Permit </span><span>filters before the “Block Outbound Default Rule” filter, and of course I've also cropped the list to make it smaller. If we inspect the “Allow outbound TCP traffic from dmcertinst.exe” filter we find that it only matches on the App ID and the IP protocol. As it doesn't have an AppContainer specific checks, then any sockets created in the context of a </span><span class="c7">dmcertinst</span><span class="c2"> process would be permitted to make TCP connections. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">Once the “Allow outbound TCP traffic from dmcertinst.exe” filter matches the sublayer evaluation is terminated and it never reaches the “Block Outbound Default Rule” filter. This is fairly trivial to exploit, as long as the AppContainer process is allowed to spawn new processes, which is allowed by default.</span></p><h3 class="c33" id="h.mkob28po89sq"><span class="c16">Server Capabilities</span></h3> <p class="c4"><span>What about the </span><span class="c7">internetClientServer</span><span> capability, how does that function? First, there's a second set of outbound filters to cover the capability with the same network addresses as the base </span><span class="c7">internetClient </span><span>capability. The only difference is the </span><span class="c7">FWPM_CONDITION_ALE_USER_ID</span><span> condition checks for the </span><span class="c7">internetClientServer (S-1-15-3-2)</span><span> capability instead. For inbound connections the </span><span class="c7">FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span><span class="c2"> layer contains the filter.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.5c5fd583c9a660791b58c690297988f818b902ce"></a><a id="t.19"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -AleLayer RecvAcceptV4 -Sorted | </span></p> <p class="c1"><span class="c8">Where-Object Name -Match InternetClientServer | </span></p> <p class="c1"><span class="c8">Format-FwFilter</span></p> <p class="c1"><span class="c0">Name : InternetClientServer Inbound Default Rule</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 45c5f1d5-6ad2-4a2a-a605-4cab7d4fb257</span></p> <p class="c1"><span class="c0">Id : 72470</span></p> <p class="c1"><span class="c0">Description: InternetClientServer Inbound Default Rule</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 824633728960</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID NotEqual NULL SID</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_IP_REMOTE_ADDRESS Range </span></p> <p class="c1"><span class="c0">Low: 0.0.0.0 High: 255.255.255.255</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ORIGINAL_PROFILE_ID Equal Public</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_CURRENT_PROFILE_ID Equal Public</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal </span></p> <p class="c1"><span class="c0">O:LSD:(A;;CC;;;S-1-15-3-2)(A;;CC;;;WD)(A;;CC;;;AN)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The example shows the filter for a </span><span class="c7">Public </span><span>network interface granting an AppContainer application the ability to receive network connections. However, this will only be permitted if the socket creator has </span><span class="c7">internetClientServer </span><span>capability. Note, there would be similar rules for the private network if the network interface is marked as </span><span class="c7">Private</span><span> but only granting access with the </span><span class="c7">privateNetworkClientServer </span><span class="c2">capability. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>As mentioned earlier just because an application has one of these capabilities doesn't mean it can receive network connections. The default configuration will block the inbound connection. However, when an </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/uwp/">UWP</a></span><span class="c2"> application is installed and requires one of the two server capabilities, the AppX installer service registers the AppContainer profile with the Windows Defender Firewall service. This adds a filter to permit the AppContainer package to receive inbound connections. For example the following is for the Microsoft Photos application, which is typically installed by default:</span></p> <p class="c3"><span class="c2"></span></p><a id="t.258a3abd87d3e98a3935eba5711ee2ca79c86d64"></a><a id="t.20"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 68299 | </span></p> <p class="c1"><span class="c8">Format-FwFilter -FormatSecurityDescriptor -Summary</span></p> <p class="c1"><span class="c0">Name : @{Microsoft.Windows.Photos_2021...</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">Key : 7b51c091-ed5f-42c7-a2b2-ce70d777cdea</span></p> <p class="c1"><span class="c0">Id : 68299</span></p> <p class="c1"><span class="c0">Description: @{Microsoft.Windows.Photos_2021...</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_FIREWALL</span></p> <p class="c1"><span class="c0">Flags : Indexed</span></p> <p class="c1"><span class="c0">Weight : 10376294366095343616</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_PACKAGE_ID Equal </span></p> <p class="c1"><span class="c0">microsoft.windows.photos_8wekyb3d8bbwe</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal O:SYG:SYD:(A;;CCRC;;;S-1-5-21-3563698930-1433966124...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0"><Owner> (Defaulted) : NT AUTHORITY\SYSTEM</span></p> <p class="c1"><span class="c0"><Group> (Defaulted) : NT AUTHORITY\SYSTEM</span></p> <p class="c1"><span class="c0"><DACL></span></p> <p class="c1"><span class="c0">DOMAIN\alice: (Allowed)(None)(Full Access)</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your Internet connection:...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your Internet connection,...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your home or work networks:...</span></p> <p class="c1"><span class="c0">NAMED CAPABILITIES\Proximity: (Allowed)(None)(Full Access)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">The filter only checks that the package SID matches and that the socket creator is a specific user in an AppContainer. Note this rule doesn't do any checking on the executable file, remote IP address, port or profile ID. Once an installed AppContainer application is granted a server capability it can act as a server through the firewall for any traffic type or port.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>A normal application could abuse this configuration to run a network service without needing the administrator access normally required to grant the executable access. All you'd need to do is create an arbitrary AppContainer process in the permitted package and grant it the </span><span class="c7">internetClientServer </span><span>and/or the </span><span class="c7">privateNetworkClientServer </span><span>capabilities. If there isn't an application installed which has the appropriate firewall rules a non-administrator user can install any signed application with the appropriate capabilities to add the firewall rules. While this clearly circumvents the expected administrator requirements for new listening processes it's presumably by design.</span></p><h2 class="c26" id="h.fpsp45283a10"><span class="c25">Localhost Access</span></h2> <p class="c4"><span class="c2">One of the specific restrictions imposed on AppContainer applications is blocking access to localhost. The purpose of this is it makes it more difficult to exploit local network services which might not correctly handle AppContainer callers creating a sandbox escape. Let's test the behavior out and try to connect to a localhost service.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.e4db541c042a9a9e3e2091fa0b944a31717e8ea0"></a><a id="t.21"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">$token = Get-NtToken -LowBox -PackageSid "LOOPBACK"</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Invoke-NtToken $token { </span></p> <p class="c1"><span class="c8"> [System.Net.Sockets.TcpClient]::new("127.0.0.1", 445) </span></p> <p class="c1"><span class="c8">}</span></p> <p class="c1"><span class="c20">Exception calling ".ctor" with "2" argument(s): "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because</span></p> <p class="c1"><span class="c20">connected host has failed to respond 127.0.0.1:445"</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">If you compare the error to when we tried to connect to an internet address without the appropriate capability you'll notice it's different. When we connected to the internet we got an immediate error indicating that access isn't permitted. However, for localhost we instead get a timeout error, which is preceded by multi-second delay. Why the difference? Getting the network event which corresponds to the connection and displaying the blocking filter shows something interesting.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.c7ae2cade8277de8b62135c0a3f94c9c9988256b"></a><a id="t.22"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -Id 69039 | </span></p> <p class="c1"><span class="c23">Format-FwFilter -FormatSecurityDescriptor -Summary</span></p> <p class="c1"><span class="c0">Name : AppContainerLoopback</span></p> <p class="c1"><span class="c0">Action Type: Block</span></p> <p class="c1"><span class="c0">Key : a58394b7-379c-43ac-aa07-9b620559955e</span></p> <p class="c1"><span class="c0">Id : 69039</span></p> <p class="c1"><span class="c0">Description: AppContainerLoopback</span></p> <p class="c1"><span class="c14">Layer : </span><span class="c18 c37">FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 18446744073709551614</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c35">FWPM_CONDITION_FLAGS FlagsAllSet IsLoopback</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal </span></p> <p class="c1"><span class="c0">O:LSD:(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1)(A;;CC;;;S-1-15-3-2)...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0"><Owner> : NT AUTHORITY\LOCAL SERVICE</span></p> <p class="c1"><span class="c0"><DACL></span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your Internet connection...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your Internet connection, including...</span></p> <p class="c1"><span class="c0">APPLICATION PACKAGE AUTHORITY\Your home or work networks...</span></p> <p class="c1"><span class="c0">NAMED CAPABILITIES\Proximity: (Allowed)(None)(Match)</span></p> <p class="c1"><span class="c0">Everyone: (Allowed)(None)(Match)</span></p> <p class="c1"><span class="c0">NT AUTHORITY\ANONYMOUS LOGON: (Allowed)(None)(Match)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">The blocking filter is not in the connect layer as you might expect, instead it's in the receive/accept layer. This explains why we get a timeout rather than immediate failure: the “inbound” connection request is being dropped as per the default configuration. This means the TCP client waits for the response from the server, until it eventually hits the timeout limit.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The second interesting thing to note about the filter is it's not based on an IP address such as 127.0.0.1. Instead it's using a condition which checks for the </span><span class="c7">IsLoopback </span><span>condition flag (</span><span class="c7">FWP_CONDITION_FLAG_IS_LOOPBACK</span><span class="c2"> in the SDK). This flag indicates that the connection is being made through the built-in loopback network, regardless of the destination address. Even if you access the public IP addresses for the local network interfaces the packets will still be routed through the loopback network and the condition flag will be set.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The user ID check is odd, in that the security descriptor matches either AppContainer or non-AppContainer processes. This is of course the point, if it didn't match both then it wouldn't block the connection. However, it's not immediately clear what its actual purpose is if it just matches everything. In my opinion, it adds a risk that the filter will be ignored if the socket creator has disabled the </span><span class="c7">Everyone </span><span class="c2">group. This condition was modified for supporting LPAC over Windows 8, so it's presumably intentional.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">You might ask, if the filter would block any loopback connection regardless of whether it's in an AppContainer, how do loopback connections work for normal applications? Wouldn't this filter always match and block the connection? Unsurprisingly there are some additional permit filters before the blocking filter as shown below. </span></p> <p class="c3"><span class="c2"></span></p><a id="t.1840a16fe9f034fa60eca82083ef423c4fbf8a0b"></a><a id="t.23"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -AleLayer RecvAcceptV4 -Sorted | </span></p> <p class="c1"><span class="c23">Where-Object Name -Match AppContainerLoopback | Format-FwFilter</span></p> <p class="c1"><span class="c0">Name : AppContainerLoopback</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_FLAGS FlagsAllSet IsAppContainerLoopback</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : AppContainerLoopback</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_FLAGS FlagsAllSet IsReserved</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0">Name : AppContainerLoopback</span></p> <p class="c1"><span class="c0">Action Type: Permit</span></p> <p class="c1"><span class="c0">...</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_FLAGS FlagsAllSet IsNonAppContainerLoopback</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The three filters shown above only check for different condition flags, and you can find documentation for the flags on </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows-hardware/drivers/network/filtering-condition-flags">MSDN</a></span><span>. Starting at the bottom we have a check for </span><span class="c7">IsNonAppContainerLoopback</span><span class="c2">. This flag is set on a connection when the loopback connection is between non-AppContainer created sockets. This filter is what grants normal applications loopback access. It's also why an application can listen on localhost even if it's not granted access to receive connections from the network in the firewall configuration.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>In contrast the first filter checks for the </span><span class="c7">IsAppContainerLoopback </span><span class="c2">flag. Based on the documentation and the name, you might assume this would allow any AppContainer to use loopback to any other. However, based on testing this flag is only set if the two AppContainers have the same package SID. This is presumably to allow an AppContainer to communicate with itself or other processes within its package through loopback sockets. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span class="c2">This flag is also, I suspect, the reason that connecting to a loopback socket is handled in the receive layer rather than the connect layer. Perhaps WFP can't easily tell ahead of time whether both the connecting and receiving sockets will be in the same AppContainer package, so it delays resolving that until the connection has been received. This does lead to the unfortunate behavior that blocked loopback sockets timeout rather than fail immediately.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The final flag, </span><span class="c7">IsReserved</span><span> is more curious. MSDN of course says this is “Reserved for future use.”, </span><span>and the future is now</span><span class="c2">. Though checking back at the filters in Windows 8.1 also shows it being used, so if it was reserved it wasn't for very long. The obvious conclusion is this flag is really a “Microsoft Reserved” flag, by that I mean it's actually used but Microsoft is yet unwilling to publicly document it.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>What is it used for? AppContainers are supposed to be a capability based system, where you can just add new capabilities to grant additional privileges. It would make sense to have a loopback capability to grant access, which could be restricted to only being used for debugging purposes. However, it seems that loopback access was so beyond the pale for the designers that instead you can only grant access for debug purposes through an </span><span class="c13"><a class="c111" href="https://docs.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-networkisolationsetappcontainerconfig">administrator only API</a></span><span class="c2">. Perhaps it's related? </span></p> <p class="c3"><span class="c2"></span></p><a id="t.a9bbfabe6ecbaa461c5f8cded94710948469c9c2"></a><a id="t.24"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c8">Add-AppModelLoopbackException -PackageSid "LOOPBACK"</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">Get-FwFilter -AleLayer ConnectV4 | </span></p> <p class="c1"><span class="c8">Where-Object Name -Match AppContainerLoopback | </span></p> <p class="c1"><span class="c23">Format-FwFilter -FormatSecurityDescriptor -Summary</span></p> <p class="c1"><span class="c0">Name : AppContainerLoopback</span></p> <p class="c1"><span class="c24">Action Type: CalloutInspection</span></p> <p class="c1"><span class="c0">Key : dfe34c0f-84ca-4af1-9d96-8bf1e8dac8c0</span></p> <p class="c1"><span class="c0">Id : 54912247</span></p> <p class="c1"><span class="c0">Description: AppContainerLoopback</span></p> <p class="c1"><span class="c0">Layer : FWPM_LAYER_ALE_AUTH_CONNECT_V4</span></p> <p class="c1"><span class="c0">Sub Layer : MICROSOFT_DEFENDER_SUBLAYER_WSH</span></p> <p class="c1"><span class="c0">Flags : None</span></p> <p class="c1"><span class="c0">Weight : 18446744073709551615</span></p> <p class="c1"><span class="c35">Callout Key: FWPM_CALLOUT_RESERVED_AUTH_CONNECT_LAYER_V4</span></p> <p class="c1"><span class="c0">Conditions :</span></p> <p class="c1"><span class="c0">FieldKeyName MatchType Value</span></p> <p class="c1"><span class="c0">------------ --------- -----</span></p> <p class="c1"><span class="c0">FWPM_CONDITION_ALE_USER_ID Equal D:(A;NP;CC;;;WD)(A;NP;CC;;;AN)(A;NP;CC;;;S-1-15-3-1861862962-...</span></p> <p class="c1 c21"><span class="c0"></span></p> <p class="c1"><span class="c0"><DACL></span></p> <p class="c1"><span class="c0">Everyone: (Allowed)(NoPropagateInherit)(Match)</span></p> <p class="c1"><span class="c0">NT AUTHORITY\ANONYMOUS LOGON: (Allowed)(NoPropagateInherit)(Match)</span></p> <p class="c1"><span class="c39">PACKAGE CAPABILITY\LOOPBACK: (Allowed)(NoPropagateInherit)(Match)</span></p> <p class="c1"><span class="c39">LOOPBACK: (Allowed)(NoPropagateInherit)(Match)</span></p></td></tr></tbody></table> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>First we add a loopback exemption for the </span><span class="c7">LOOPBACK</span><span> package name. We then look for the </span><span class="c7">AppContainerLoopback </span><span>filters in the connect layer. The one we're interested in is shown. The first thing to note is that the action type is set to </span><span class="c7">CalloutInspection</span><span class="c2">. This might seem slightly surprising, you would expect it'd do something more than inspecting the traffic.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The name of the callout, </span><span class="c7">FWPM_CALLOUT_RESERVED_AUTH_CONNECT_LAYER_V4</span><span> gives the game away. The fact that it has </span><span class="c7">RESERVED</span><span> in the name can't be a coincidence. This callout is one implemented internally by Windows in the </span><span class="c7">TCPIP!WfpAlepDbgLowboxSetByPolicyLoopbackCalloutClassify</span><span> function. This name now loses all mystery and pretty much explains what its purpose is, which is to configure the connection so that the </span><span class="c7">IsReserved </span><span class="c2">flag is set when the receive layer processes it.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>The user ID here is equally important. When you register the loopback exemption you only specify the package SID, which is shown in the output as the last “LOOPBACK” line. Therefore you'd assume you'd need to always run your code within that package. However, the penultimate line is </span><span class="c7">“PACKAGE CAPABILITY\LOOPBACK”</span><span class="c2"> which is my module's way of telling you that this is the package SID, but converted to a capability SID. This is basically changing the first relative identifier in the SID from 2 to 3.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>We can use this behavior to simulate a generic loopback exemption capability. It allows you to create an AppContainer sandboxed process which has access to localhost which isn't restricted to a particular package. This would be useful for applications such as Chrome to implement a network facing sandboxed process and would work from Windows 8 through </span><span>11.</span><span class="c2"> . Unfortunately it's not officially documented so can't be relied upon. An example demonstrating the use of the capability is shown below.</span></p> <p class="c3"><span class="c2"></span></p><a id="t.c5c3912b5466f474ed23b7f597087336a3689bd7"></a><a id="t.25"></a><table class="c10"><tbody><tr class="c12"><td class="c19" colspan="1" rowspan="1"> <p class="c1"><span class="c14">PS> </span><span class="c23">$cap = Get-NtSid -PackageSid "LOOPBACK" -AsCapability</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$token = Get-NtToken -LowBox -PackageSid "TEST" -cap $cap</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$sock = Invoke-NtToken $token { </span></p> <p class="c1"><span class="c8"> [System.Net.Sockets.TcpClient]::new("127.0.0.1", 445) </span></p> <p class="c1"><span class="c8">}</span></p> <p class="c1"><span class="c14">PS> </span><span class="c8">$sock.Client.RemoteEndPoint</span></p> <p class="c1"><span class="c0">AddressFamily Address Port</span></p> <p class="c1"><span class="c0">------------- ------- ----</span></p> <p class="c1"><span class="c0"> InterNetwork 127.0.0.1 445</span></p></td></tr></tbody></table><h2 class="c26" id="h.83prefam1lot"><span class="c25">Conclusions</span></h2> <p class="c4"><span>That wraps up my quick overview of how AppContainer network restrictions are implemented using the Windows Firewall. I covered the basics of the Windows Firewall as well as covered some of my tooling I wrote to do analysis of the configuration. </span><span>This background information allowed me to explain why the</span><span class="c13"><a class="c111" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2207"> issue</a></span><span> I reported to Microsoft worked. </span><span>I also pointed out some of the quirks of the implementation which you might find of interest. </span></p> <p class="c3"><span class="c2"></span></p> <p class="c4"><span>Having a good understanding of how a security feature works is an important step towards finding security issues. I hope that by providing both the background and tooling other researchers can also find similar issues and try and get them fixed.</span></p> <p class="c3"><span class="c2"></span></p> <p class="c3"><span class="c2"></span></p> <p class="c3"><span class="c2"></span></p> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <span itemprop='name'>Anonymous</span> </span> </span> <span class='post-timestamp'> at <meta content='https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2021-08-19T09:37:00-07:00'>9:37 AM</abbr></a> </span> <span class='post-comment-link'> <a class='comment-link' href='https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html#comment-form' onclick=''> No comments: </a> </span> <span class='post-icons'> <span class='item-control blog-admin pid-145400864'> <a href='https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=1215474650509337553&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> <a class='goog-inline-block share-button sb-email' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=1215474650509337553&target=email' target='_blank' title='Email This'><span class='share-button-link-text'>Email This</span></a><a class='goog-inline-block share-button sb-blog' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=1215474650509337553&target=blog' onclick='window.open(this.href, "_blank", "height=270,width=475"); return false;' target='_blank' title='BlogThis!'><span class='share-button-link-text'>BlogThis!</span></a><a class='goog-inline-block share-button sb-twitter' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=1215474650509337553&target=twitter' target='_blank' title='Share to X'><span class='share-button-link-text'>Share to X</span></a><a class='goog-inline-block share-button sb-facebook' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=1215474650509337553&target=facebook' onclick='window.open(this.href, "_blank", "height=430,width=640"); return false;' target='_blank' title='Share to Facebook'><span class='share-button-link-text'>Share to Facebook</span></a><a class='goog-inline-block share-button sb-pinterest' href='https://www.blogger.com/share-post.g?blogID=4838136820032157985&postID=1215474650509337553&target=pinterest' target='_blank' title='Share to Pinterest'><span class='share-button-link-text'>Share to Pinterest</span></a> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> </span> </div> </div> </div> </div> </div></div> </div> <div class='blog-pager' id='blog-pager'> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' href='https://googleprojectzero.blogspot.com/search?updated-max=2021-10-19T09:08:00-07:00&max-results=1&reverse-paginate=true' id='Blog1_blog-pager-newer-link' title='Newer Posts'>Newer Posts</a> </span> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='https://googleprojectzero.blogspot.com/search?updated-max=2021-08-19T09:37:00-07:00&max-results=1' id='Blog1_blog-pager-older-link' title='Older Posts'>Older Posts</a> </span> <a class='home-link' href='https://googleprojectzero.blogspot.com/'>Home</a> </div> <div class='clear'></div> <div class='blog-feeds'> <div class='feed-links'> Subscribe to: <a class='feed-link' href='https://googleprojectzero.blogspot.com/feeds/posts/default' target='_blank' type='application/atom+xml'>Posts (Atom)</a> </div> </div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget BlogSearch' data-version='1' id='BlogSearch1'> <h2 class='title'>Search This Blog</h2> <div class='widget-content'> <div id='BlogSearch1_form'> <form action='https://googleprojectzero.blogspot.com/search' class='gsc-search-box' target='_top'> <table cellpadding='0' cellspacing='0' class='gsc-search-box'> <tbody> <tr> <td class='gsc-input'> <input autocomplete='off' class='gsc-input' name='q' size='10' title='search' type='text' value=''/> </td> <td class='gsc-search-button'> <input class='gsc-search-button' title='search' type='submit' value='Search'/> </td> </tr> </tbody> </table> </form> </div> </div> <div class='clear'></div> </div><div class='widget PageList' data-version='1' id='PageList1'> <h2>Pages</h2> <div class='widget-content'> <ul> <li> <a href='https://googleprojectzero.blogspot.com/p/about-project-zero.html'>About Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/working-at-project-zero.html'>Working at Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/0day.html'>0day "In the Wild"</a> </li> <li> <a href='https://googleprojectzero.github.io/0days-in-the-wild/rca.html'>0day Exploit Root Cause Analyses</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html'>Vulnerability Disclosure FAQ</a> </li> </ul> <div class='clear'></div> </div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Archives</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(9)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/'> 2022 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2022/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(24)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> <ul class='posts'> <li><a href='https://googleprojectzero.blogspot.com/2021/08/understanding-network-access-windows-app.html'>Understanding Network Access in Windows AppContainers</a></li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/01/'> January </a> <span class='post-count' dir='ltr'>(10)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/'> 2020 </a> <span class='post-count' dir='ltr'>(36)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/08/'> August </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/07/'> July </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2020/01/'> January </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/'> 2019 </a> <span class='post-count' dir='ltr'>(27)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/08/'> August </a> <span class='post-count' dir='ltr'>(11)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2019/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(22)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/12/'> December </a> <span class='post-count' dir='ltr'>(7)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/10/'> October </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2018/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(19)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/04/'> April </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2017/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/'> 2016 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/09/'> September </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2016/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(33)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/08/'> August </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/07/'> July </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/06/'> June </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2015/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2014/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> </div> </div> <script type='text/javascript'> //<![CDATA[ (function(){ let archive_list = document.getElementById('ArchiveList'); if (archive_list == null) return; let cur_year = archive_list.querySelector('.post-count-link').innerText.trim() - 0; let last_year = 2014; let elements = []; const MONTHS = ',Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','); let parent = document.getElementById('ArchiveList'); while (parent.childNodes.length) parent.removeChild(parent.childNodes[0]); function fetch_next_year() { let url = 'https://googleprojectzero.blogspot.com/?action=getTitles&widgetId=BlogArchive1&widgetType=BlogArchive&responseType=js&path=https%3A%2F%2Fgoogleprojectzero.blogspot.com%2F'+cur_year; fetch(url).then(resp => { if (!resp.ok) { console.log('http error'); return; } resp.text().then(text => { let scope = { _WidgetManager: { _HandleControllerResult: (name, method, results) => { elements.push(document.createElement('hr')); let year_header = document.createElement('div'); year_header.appendChild(document.createTextNode(cur_year)); year_header.style.fontSize = 'large'; elements.push(year_header); let list = document.createElement('ul'); elements.push(list); for (let obj of results.posts) { let link_parts = obj.url.split('/'); let year = link_parts[3]; let month = link_parts[4]; let el = document.createElement(/*'div'*/'li'); el.style.listStyleType = 'square'; el.style.listStylePosition = 'inside'; let link = document.createElement('a'); el.appendChild(link); link.appendChild(document.createTextNode(obj.title)); link.href = obj.url; let date_trailer = document.createElement('span'); el.appendChild(date_trailer); //date_trailer.appendChild(document.createTextNode(' ('+year+'-'+month+')')); date_trailer.appendChild(document.createTextNode(' ('+MONTHS[parseInt(month, 10)]+')')); //date_trailer.style.textAlign = 'right'; //elements.push(el); list.appendChild(el); } } } }; with (scope) { eval(text); } if (cur_year == last_year) { finish(); } else { cur_year--; fetch_next_year(); } }); }); } fetch_next_year(); function finish() { for (let obj of elements) { parent.appendChild(obj); } console.log(elements); } })(); //]]> </script> <div class='clear'></div> </div> </div></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-1'></div> </td> <td class='columns-cell'> <div class='sidebar no-items section' id='sidebar-right-2-2'></div> </td> </tr> </tbody> </table> <div class='sidebar no-items section' id='sidebar-right-3'></div> </aside> </div> </div> </div> <div style='clear: both'></div> <!-- columns --> </div> <!-- main --> </div> </div> <div class='main-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <footer> <div class='footer-outer'> <div class='footer-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left footer-fauxborder-left'> <div class='fauxborder-right footer-fauxborder-right'></div> <div class='region-inner footer-inner'> <div class='foot no-items section' id='footer-1'></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='foot no-items section' id='footer-2-1'></div> </td> <td class='columns-cell'> <div class='foot no-items section' id='footer-2-2'></div> </td> </tr> </tbody> </table> <!-- outside of the include in order to lock Attribution widget --> <div class='foot section' id='footer-3' name='Footer'><div class='widget Attribution' data-version='1' id='Attribution1'> <div class='widget-content' style='text-align: center;'> Powered by <a href='https://www.blogger.com' target='_blank'>Blogger</a>. </div> <div class='clear'></div> </div></div> </div> </div> <div class='footer-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </footer> <!-- content --> </div> </div> <div class='content-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <script type='text/javascript'> window.setTimeout(function() { document.body.className = document.body.className.replace('loading', ''); }, 10); </script> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/984859869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY405VsN_mVBfrW7Jrlbx8TKtCrzLQ:1732535712397';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d4838136820032157985','//googleprojectzero.blogspot.com/2021/08/','4838136820032157985'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '4838136820032157985', 'title': 'Project Zero', 'url': 'https://googleprojectzero.blogspot.com/2021/08/', 'canonicalUrl': 'https://googleprojectzero.blogspot.com/2021/08/', 'homepageUrl': 'https://googleprojectzero.blogspot.com/', 'searchUrl': 'https://googleprojectzero.blogspot.com/search', 'canonicalHomepageUrl': 'https://googleprojectzero.blogspot.com/', 'blogspotFaviconUrl': 'https://googleprojectzero.blogspot.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': false, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-240546891-1', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Project Zero - RSS\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/4838136820032157985/posts/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/da8f33dd880cc4f1', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'archive', 'pageName': 'August 2021', 'pageTitle': 'Project Zero: August 2021'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Project Zero', 'description': 'News and updates from the Project Zero team at Google', 'url': 'https://googleprojectzero.blogspot.com/2021/08/', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': true, 'isLabelSearch': false, 'archive': {'year': 2021, 'month': 8, 'rangeMessage': 'Showing posts from August, 2021'}}}]); _WidgetManager._RegisterWidget('_NavbarView', new _WidgetInfo('Navbar1', 'navbar', document.getElementById('Navbar1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2646514562-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch1', 'sidebar-right-1', document.getElementById('BlogSearch1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_PageListView', new _WidgetInfo('PageList1', 'sidebar-right-1', document.getElementById('PageList1'), {'title': 'Pages', 'links': [{'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/about-project-zero.html', 'id': '4384467920505278144', 'title': 'About Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/working-at-project-zero.html', 'id': '2459334498880008057', 'title': 'Working at Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/0day.html', 'id': '3414239791814532209', 'title': '0day \x22In the Wild\x22'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.github.io/0days-in-the-wild/rca.html', 'title': '0day Exploit Root Cause Analyses'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html', 'id': '2935252455704572784', 'title': 'Vulnerability Disclosure FAQ'}], 'mobile': false, 'showPlaceholder': true, 'hasCurrentPage': false}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>