CINXE.COM
Project Zero: 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/' 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/' 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: 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=dd12d16f-47b1-45ec-9a52-9d6602fe5782' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&zx=dd12d16f-47b1-45ec-9a52-9d6602fe5782' 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/4838136820032157985?origin\x3dhttps://googleprojectzero.blogspot.com', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </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>Wednesday, December 15, 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/AVvXsEhvP0VlEQXC6TZWOu-zPOWC-Pnyy3uqOJpwPeP3Y_rz-ZO_MvrqjiMtMwxzIz_E8NdNyrV_Fvx-RRApoMxAPrYQcHO4eiico20He9zMm3UT5-j84CCRZDJq5hjMmeIKd0aLMsflCkfrfVHp1z1PbQmYPFX6UlVtn6_gF8P6iTQaAHL3EQ6iKs4VDdEZ/s870/image2%281%29.jpg' itemprop='image_url'/> <meta content='4838136820032157985' itemprop='blogId'/> <meta content='5073082417618502919' itemprop='postId'/> <a name='5073082417618502919'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.html'>A deep dive into an NSO zero-click iMessage exploit: Remote Code Execution</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-5073082417618502919' itemprop='description articleBody'> <style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=lhDjYqiy3mZ0x6ROQEUoUw');ol{margin:0;padding:0}table td,table th{padding:0}.c13{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#b7b7b7;border-top-width:1pt;border-right-width:1pt;border-left-color:#b7b7b7;vertical-align:top;border-right-color:#b7b7b7;border-left-width:1pt;border-top-style:solid;border-left-style:solid;border-bottom-width:1pt;width:468pt;border-top-color:#b7b7b7;border-bottom-style:solid}.c0{color:#000000;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:11pt;font-family:"Arial";font-style:normal}.c20{padding-top:20pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.c19{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:20pt;font-family:"Arial";font-style:normal}.c1{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:right;height:11pt}.c4{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:center}.c14{color:#000000;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c3{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left}.c9{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial"}.c12{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:right}.c23{color:#000000;text-decoration:none;vertical-align:baseline;font-size:11pt;font-style:normal}.c22{border-spacing:0;border-collapse:collapse;margin-right:auto}.c21{padding-top:0pt;padding-bottom:0pt;line-height:1.0;text-align:left}.c16{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.c17{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c2{color:inherit;text-decoration:inherit}.c15{font-weight:400;font-family:Consolas,"Courier New"}.c6{font-weight:400;font-family:"Courier New"}.c8{font-weight:700}.c11{height:0pt}.c7{font-style:italic}.c10{height:11pt}.c18{background-color:#00ff00}.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="c17"> <p class="c3"><span class="c5">Posted by Ian Beer & Samuel Groß of Google Project Zero</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c7">We want to thank Citizen Lab for sharing a sample of the FORCEDENTRY exploit with us, and Apple’s Security Engineering and Architecture (SEAR) group for collaborating with us on the technical analysis. The editorial opinions reflected below are solely Project Zero’s and do not necessarily reflect those of the organizations we collaborated with during this research. </span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Earlier this year, Citizen Lab managed to capture an NSO iMessage-based zero-click exploit being used to target a Saudi activist.</span><span> </span><span>In this two-part blog post series we will describe </span><span>for the first time</span><span> how an in-the-wild zero-click iMessage exploit works.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Based on our research and findings, we </span><span>assess</span><span> this to be </span><span>one of the most technically sophisticated exploits we've ever seen, further</span><span> demonstrating </span><span>that the capabilities NSO provides rival those previously thought to be accessible to only a handful of nation states.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>The vulnerability discussed in this blog post was fixed on September 13, 2021 in </span><span class="c16"><a class="c21" href="https://support.apple.com/en-us/HT212807">iOS 14.8</a></span><span> as CVE-2021-30860.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">NSO</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c16"><a class="c21" href="https://en.wikipedia.org/wiki/NSO_Group">NSO</a></span><span class="c16"><a class="c21" href="https://en.wikipedia.org/wiki/NSO_Group"> Group</a></span><span> </span><span>is</span><span> one of the </span><span class="c16"><a class="c21" href="https://www.atlanticcouncil.org/in-depth-research-reports/report/countering-cyber-proliferation-zeroing-in-on-access-as-a-service/">highest-profile providers of "access-as-a-service"</a></span><span>, selling packaged hacking solutions which </span><span class="c16"><a class="c21" href="https://www.atlanticcouncil.org/in-depth-research-reports/issue-brief/surveillance-technology-at-the-fair/">enable nation state actors without a home-grown offensive cyber capability to "pay-to-play"</a></span><span class="c5">, vastly expanding the number of nations with such cyber capabilities.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>For years, groups like Citizen Lab and Amnesty International have been tracking the use of NSO's mobile spyware package "Pegasus". Despite NSO's claims that they "</span><span class="c16"><a class="c21" href="https://www.nsogroup.com/governance/human-rights-policy/">[evaluate] the potential for adverse human rights impacts arising from the misuse of NSO products</a></span><span>" Pegasus has been linked to </span><span class="c16"><a class="c21" href="https://citizenlab.ca/2020/01/stopping-the-press-new-york-times-journalist-targeted-by-saudi-linked-pegasus-spyware-operator/">the hacking of the New York Times journalist Ben Hubbard by the Saudi regime</a></span><span>,</span><span> </span><span class="c16"><a class="c21" href="https://www.amnesty.org/en/latest/research/2019/10/morocco-human-rights-defenders-targeted-with-nso-groups-spyware/">hacking of human rights defenders in Morocco</a></span><span> and </span><span class="c16"><a class="c21" href="https://citizenlab.ca/2021/08/bahrain-hacks-activists-with-nso-group-zero-click-iphone-exploits/">Bahrain</a></span><span>, </span><span class="c16"><a class="c21" href="https://www.amnesty.org/en/latest/research/2018/08/amnesty-international-among-targets-of-nso-powered-campaign/">the targeting of Amnesty International staff</a></span><span class="c5"> and dozens of other cases.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Last month the United States added NSO to the "Entity List", severely restricting the ability of US companies to do business with NSO and </span><span class="c16"><a class="c21" href="https://www.commerce.gov/news/press-releases/2021/11/commerce-adds-nso-group-and-other-foreign-companies-entity-list">stating in a press release</a></span><span> that "[NSO's tools] enabled foreign governments to conduct transnational repression, which is the practice of authoritarian governments targeting dissidents, journalists and activists outside of their sovereign borders to silence dissent."</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Citizen Lab was able to recover these Pegasus exploits from an iPhone and therefore this analysis covers NSO's capabilities against iPhone. We are aware that NSO sells similar zero-click capabilities which target Android devices; Project Zero</span><span> does not have samples of these exploits</span><span> but if you do, </span><span>please reach out.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">From One to Zero</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>In previous cases such as the </span><span class="c16"><a class="c21" href="https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/">Million Dollar Dissident from 2016</a></span><span class="c5">, targets were sent links in SMS messages:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"></p> <p class="c3"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvP0VlEQXC6TZWOu-zPOWC-Pnyy3uqOJpwPeP3Y_rz-ZO_MvrqjiMtMwxzIz_E8NdNyrV_Fvx-RRApoMxAPrYQcHO4eiico20He9zMm3UT5-j84CCRZDJq5hjMmeIKd0aLMsflCkfrfVHp1z1PbQmYPFX6UlVtn6_gF8P6iTQaAHL3EQ6iKs4VDdEZ/s870/image2%281%29.jpg" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvP0VlEQXC6TZWOu-zPOWC-Pnyy3uqOJpwPeP3Y_rz-ZO_MvrqjiMtMwxzIz_E8NdNyrV_Fvx-RRApoMxAPrYQcHO4eiico20He9zMm3UT5-j84CCRZDJq5hjMmeIKd0aLMsflCkfrfVHp1z1PbQmYPFX6UlVtn6_gF8P6iTQaAHL3EQ6iKs4VDdEZ/s870/image2%281%29.jpg" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">Screenshots of Phishing SMSs reported to Citizen Lab in 2016</span></p> <p class="c12"><span class="c7">source: </span><span class="c16 c7"><a class="c21" href="https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/">https://citizenlab.ca/2016/08/million-dollar-dissident-iphone-zero-day-nso-group-uae/</a></span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>The target was only hacked when they clicked the link, a technique known as a one-click exploit. Recently, however, it has been documented that NSO is offering their clients zero-click exploitation technology, where even very technically savvy targets who might not click a phishing link are completely unaware they are being targeted. In the zero-click scenario no user interaction is required. Meaning, t</span><span>he attacker doesn't need to send phishing messages; the exploit just works silently in the background</span><span>. Short of not using a device, t</span><span>here is no way to prevent exploitation by a zero-click exploit; it's a weapon against which there is no defense</span><span>.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c8">One weird trick</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">The initial entry point for Pegasus on iPhone is iMessage. This means that a victim can be targeted just using their phone number or AppleID username.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>iMessage has native support for GIF images, the typically small and low quality animated images popular in meme culture. You can send and receive GIFs in iMessage chats and they show up in the chat window. Apple wanted to make those GIFs loop endlessly rather than only play once, so very early on in the </span><span class="c16"><a class="c21" href="https://googleprojectzero.blogspot.com/2021/01/a-look-at-imessage-in-ios-14.html">iMessage parsing and processing pipeline</a></span><span> (after a message has been received but well before the message is shown), iMessage calls the following method in the </span><span class="c6">IMTranscoderAgent</span><span> process</span><span> (outside the "BlastDoor" sandbox), passing any image file received with the extension </span><span class="c15">.gif</span><span class="c5">:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c15 c23"> [IMGIFUtils copyGifFromPath:toDestinationPath:error]</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Looking at the selector name, the intention here was probably to just copy the GIF file before editing the loop count field, but the semantics of this method are different. Under the hood it uses the CoreGraphics APIs to </span><span class="c7">render</span><span> the source image to a </span><span class="c7">new</span><span> GIF file at the destination path. And just because the source filename has to end in </span><span class="c15">.gif</span><span class="c5">, that doesn't mean it's really a GIF file.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>The ImageIO library, </span><span class="c16"><a class="c21" href="https://googleprojectzero.blogspot.com/2020/04/fuzzing-imageio.html">as detailed in a previous Project Zero blogpost</a></span><span class="c5">, is used to guess the correct format of the source file and parse it, completely ignoring the file extension. Using this "fake gif" trick, over 20 image codecs are suddenly part of the iMessage zero-click attack surface, including some very obscure and complex formats, remotely exposing probably hundreds of thousands of lines of code. </span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">Note: Apple inform us that they have restricted the available ImageIO formats reachable from IMTranscoderAgent starting in iOS 14.8.1 (26 October 2021), and completely removed the GIF code path from IMTranscoderAgent starting in iOS 15.0 (20 September 2021), with GIF decoding taking place entirely within BlastDoor.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c8">A PDF in your GIF</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">NSO uses the "fake gif" trick to target a vulnerability in the CoreGraphics PDF parser.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>PDF was a popular target for exploitation around a decade ago, due to its ubiquity and complexity. Plus, the availability of javascript inside PDFs made development of reliable exploits far easier. The CoreGraphics PDF parser doesn't </span><span class="c7">seem</span><span class="c5"> to interpret javascript, but NSO managed to find something equally powerful inside the CoreGraphics PDF parser...</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">Extreme compression</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>In the late 1990's, bandwidth and storage were much more scarce than they are now. It was in that environment that the </span><span class="c16"><a class="c21" href="https://en.wikipedia.org/wiki/JBIG2">JBIG2</a></span><span class="c5"> standard emerged. JBIG2 is a domain specific image codec designed to compress images where pixels can only be black or white.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>It was developed to achieve extremely high compression ratios for scans of text documents and was implemented and used in high-end office scanner/printer devices like the XEROX WorkCenter device shown below. If you used the </span><span class="c7">scan to pdf</span><span> functionality of a device like this a decade ago, your PDF likely had a JBIG2 stream in it.</span><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3gos8L_dEuVS2ltgPw-T3WxC6COMIyYoq4DlSN8Z8XgNueXzXBBlQF_BusBrKSJowwIu0OouJLMwZwPZyMiORoXCShUtbb65C3ZkKU9Tzo8ANc5862ImuSa9v1pjcjxR2v4T-UdpMYlV7DEsVgr43Mj3yAjHn_EXcxVxXFhxHqq1QXoEdP3S1JnRm/s700/image10.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3gos8L_dEuVS2ltgPw-T3WxC6COMIyYoq4DlSN8Z8XgNueXzXBBlQF_BusBrKSJowwIu0OouJLMwZwPZyMiORoXCShUtbb65C3ZkKU9Tzo8ANc5862ImuSa9v1pjcjxR2v4T-UdpMYlV7DEsVgr43Mj3yAjHn_EXcxVxXFhxHqq1QXoEdP3S1JnRm/s700/image10.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">A Xerox WorkCentre 7500 series multifunction printer, which used JBIG2</span></p> <p class="c12"><span class="c7">for its scan-to-pdf functionality</span></p> <p class="c12"><span class="c7">source: </span><span class="c16 c7"><a class="c21" href="https://www.office.xerox.com/en-us/multifunction-printers/workcentre-7545-7556/specifications">https://www.office.xerox.com/en-us/multifunction-printers/workcentre-7545-7556/specifications</a></span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">The PDFs files produced by those scanners were exceptionally small, perhaps only a few kilobytes. There are two novel techniques which JBIG2 uses to achieve these extreme compression ratios which are relevant to this exploit:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c8">Technique</span><span class="c8"> 1: </span><span class="c14 c8">Segmentation and substitution</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Effectively every text document, especially those written in languages with small alphabets like English or German, consists of many repeated letters (also known as </span><span class="c7">glyphs</span><span class="c5">) on each page. JBIG2 tries to segment each page into glyphs then uses simple pattern matching to match up glyphs which look the same:</span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2YXgDQeGK1E3GixB5S11rI1e7Xqi3cQJKuL4ZklPLYw8U1hbbEDXGOyCfcqqhoQT2evw5kHYC3Ba9RWx21XHknWvjxFKg5te5-K19ZYaoTR2wD4AmBw_c-9RXNUuonuD2TT21aTlvihuC_i_t3GgYFjw2pzL7YshGF7BZa4bq-i44V63NN6Pv7yzy/s352/image4%284%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2YXgDQeGK1E3GixB5S11rI1e7Xqi3cQJKuL4ZklPLYw8U1hbbEDXGOyCfcqqhoQT2evw5kHYC3Ba9RWx21XHknWvjxFKg5te5-K19ZYaoTR2wD4AmBw_c-9RXNUuonuD2TT21aTlvihuC_i_t3GgYFjw2pzL7YshGF7BZa4bq-i44V63NN6Pv7yzy/s352/image4%284%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">Simple pattern matching can find all the shapes which look similar on a page,</span></p> <p class="c12"><span class="c9 c7">in this case all the 'e's</span></p> <p class="c1"><span class="c9 c7"></span></p> <p class="c3"><span>JBIG2 doesn't actually know anything about glyphs and it isn't doing OCR (optical character recognition.) A JBIG encoder is just looking for connected regions of pixels and grouping similar looking regions together. The compression algorithm is to simply substitute all </span><span class="c7">sufficiently-similar</span><span class="c5"> looking regions with a copy of just one of them:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2k3SPN7fikk3DnL-5YdWTK6_n1JTjvlb6qC-4tVnHeqU16cM6VWYjmMzNL9ZwMK0MXOhWITS_P0kgsx3YFHDaI2Rd5R8f1CM55ccmCBROIlyymNW2jSSCRWCpddWLuIzhGG6uB8PDKcDg5IpWW7NjdvVPZRFme3Hk4EHHmXZwEJYoohHgaVa31w0u/s327/image1%285%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2k3SPN7fikk3DnL-5YdWTK6_n1JTjvlb6qC-4tVnHeqU16cM6VWYjmMzNL9ZwMK0MXOhWITS_P0kgsx3YFHDaI2Rd5R8f1CM55ccmCBROIlyymNW2jSSCRWCpddWLuIzhGG6uB8PDKcDg5IpWW7NjdvVPZRFme3Hk4EHHmXZwEJYoohHgaVa31w0u/s327/image1%285%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">Replacing all occurrences of similar glyphs with a copy of just one often yields a document which is still quite legible and enables very high compression ratios</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">In this case the output is perfectly readable but the amount of information to be stored is significantly reduced. Rather than needing to store all the original pixel information for the whole page you only need a compressed version of the "reference glyph" for each character and the relative coordinates of all the places where copies should be made. The decompression algorithm then treats the output page like a canvas and "draws" the exact same glyph at all the stored locations.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>There's a significant issue with such a scheme: it's far too easy for a poor encoder to accidentally swap similar looking characters, and this can happen with interesting consequences. </span><span class="c16"><a class="c21" href="http://www.dkriesel.com/en/blog/2013/0802_xerox-workcentres_are_switching_written_numbers_when_scanning">D. Kriesel's blog has some motivating examples</a></span><span> where PDFs of scanned invoices have different figures or PDFs of scanned construction drawings end up with incorrect measurements. These aren't the issues we're looking at, but they are one significant reason why JBIG2 is not a common compression format anymore.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c8">Technique </span><span class="c8">2: </span><span class="c8">Refinement coding</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">As mentioned above, the substitution based compression output is lossy. After a round of compression and decompression the rendered output doesn't look exactly like the input. But JBIG2 also supports lossless compression as well as an intermediate "less lossy" compression mode.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>It does this by also storing (and compressing) the </span><span class="c7">difference</span><span class="c5"> between the substituted glyph and each original glyph. Here's an example showing a difference mask between a substituted character on the left and the original lossless character in the middle:</span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-7RiEuIp4vanQlf6nxJf1tfbuA7B61DISNjLlNxXrvSqFnqxcvSLPN6_60h2ypZdjDKHtNmCN3Nr5W66JaLw_j5iSxxntOZ0eXFB2wEQHfUjs_9LIwmckCXdTzurtKgpwyaWkGfInvM35YC3kp_K4qyJYxV4HDEf0E9W_Zqt3OelULvnfSlWAc2Kw/s267/image3%285%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-7RiEuIp4vanQlf6nxJf1tfbuA7B61DISNjLlNxXrvSqFnqxcvSLPN6_60h2ypZdjDKHtNmCN3Nr5W66JaLw_j5iSxxntOZ0eXFB2wEQHfUjs_9LIwmckCXdTzurtKgpwyaWkGfInvM35YC3kp_K4qyJYxV4HDEf0E9W_Zqt3OelULvnfSlWAc2Kw/s267/image3%285%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">Using the XOR operator on bitmaps to compute a difference image</span></p> <p class="c1"><span class="c5"></span></p> <p class="c3"><span class="c5">In this simple example the encoder can store the difference mask shown on the right, then during decompression the difference mask can be XORed with the substituted character to recover the exact pixels making up the original character. There are some more tricks outside of the scope of this blog post to further compress that difference mask using the intermediate forms of the substituted character as a "context" for the compression.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Rather than completely encoding the entire difference in one go, it can be done in steps, with each iteration using a logical operator (one of AND, OR, XOR or XNOR) to set, clear or flip bits. Each successive refinement step brings the rendered output closer to the original and this allows a level of control over the "</span><span>lossiness</span><span class="c5">" of the compression. The implementation of these refinement coding steps is very flexible and they are also able to "read" values already present on the output canvas.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">A JBIG2 stream</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Most of the CoreGraphics PDF decoder appears to be Apple proprietary code, but the JBIG2 implementation is from Xpdf, </span><span class="c16"><a class="c21" href="https://www.xpdfreader.com/download.html">the source code for which is freely available</a></span><span class="c5">.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">The JBIG2 format is a series of segments, which can be thought of as a series of drawing commands which are executed sequentially in a single pass. The CoreGraphics JBIG2 parser supports 19 different segment types which include operations like defining a new page, decoding a huffman table or rendering a bitmap to given coordinates on the page.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Segments are represented by the class </span><span class="c6">JBIG2Segment</span><span> and its subclasses </span><span class="c6">JBIG2Bitmap</span><span> and </span><span class="c6">JBIG2SymbolDict</span><span class="c5">.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>A </span><span class="c6">JBIG2Bitmap</span><span> represents a rectangular array of pixels. Its </span><span class="c6">data</span><span class="c5"> field points to a backing-buffer containing the rendering canvas.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>A </span><span class="c6">JBIG2SymbolDict</span><span> groups </span><span class="c6">JBIG2Bitmaps</span><span> together. The destination page is represented as a </span><span class="c6">JBIG2Bitmap</span><span class="c5">, as are individual glyphs.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c6">JBIG2Segments</span><span> can be referred to by a segment number and the </span><span class="c6">GList</span><span> vector type stores pointers to all the </span><span class="c6">JBIG2Segments</span><span>. To look up a segment by segment number the </span><span class="c6">GList</span><span> is scanned sequentially.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">The vulnerability</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">The vulnerability is a classic integer overflow when collating referenced segments:</span></p> <p class="c3 c10"><span class="c5"></span></p><a id="t.7ed94ce0bf920398965583c15daf9916cc6dcfb3"></a><a id="t.0"></a><table class="c22"><tbody><tr class="c11"><td class="c13" colspan="1" rowspan="1"> <p class="c3"><span class="c6"> </span><span class="c0 c18">Guint numSyms; // (1)</span></p> <p class="c3 c10"><span class="c0"></span></p> <p class="c3"><span class="c0"> numSyms = 0;</span></p> <p class="c3"><span class="c0"> for (i = 0; i < nRefSegs; ++i) {</span></p> <p class="c3"><span class="c0"> if ((seg = findSegment(refSegs[i]))) {</span></p> <p class="c3"><span class="c0"> if (seg->getType() == jbig2SegSymbolDict) {</span></p> <p class="c3"><span class="c6"> </span><span class="c0 c18">numSyms += ((JBIG2SymbolDict *)seg)->getSize(); // (2)</span></p> <p class="c3"><span class="c0"> } else if (seg->getType() == jbig2SegCodeTable) {</span></p> <p class="c3"><span class="c0"> codeTables->append(seg);</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c3"><span class="c0"> } else {</span></p> <p class="c3"><span class="c0"> error(errSyntaxError, getPos(),</span></p> <p class="c3"><span class="c0"> "Invalid segment reference in JBIG2 text region");</span></p> <p class="c3"><span class="c0"> delete codeTables;</span></p> <p class="c3"><span class="c0"> return;</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c3"><span class="c0">...</span></p> <p class="c3"><span class="c0"> // get the symbol bitmaps</span></p> <p class="c3"><span class="c6"> </span><span class="c6 c18">syms = (</span><span class="c6 c18">JBIG2Bitmap</span><span class="c6 c18"> **)gmallocn(numSyms, sizeof(</span><span class="c6 c18">JBIG2Bitmap</span><span class="c0 c18"> *)); // (3)</span></p> <p class="c3 c10"><span class="c0"></span></p> <p class="c3"><span class="c0"> kk = 0;</span></p> <p class="c3"><span class="c0"> for (i = 0; i < nRefSegs; ++i) {</span></p> <p class="c3"><span class="c0"> if ((seg = findSegment(refSegs[i]))) {</span></p> <p class="c3"><span class="c0"> if (seg->getType() == jbig2SegSymbolDict) {</span></p> <p class="c3"><span class="c0"> symbolDict = (JBIG2SymbolDict *)seg;</span></p> <p class="c3"><span class="c0"> for (k = 0; k < symbolDict->getSize(); ++k) {</span></p> <p class="c3"><span class="c6"> </span><span class="c0 c18">syms[kk++] = symbolDict->getBitmap(k); // (4)</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c3"><span class="c0"> }</span></p> <p class="c21"><span class="c6"> }</span></p></td></tr></tbody></table> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c6">numSyms</span><span> is a 32-bit integer declared at </span><span class="c6">(1)</span><span>. By supplying carefully crafted </span><span>reference segments</span><span> it's possible for the repeated addition at </span><span class="c6">(2)</span><span> to cause </span><span class="c6">numSyms</span><span class="c5"> to overflow to a controlled, small value.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>That smaller value is used for the heap allocation size at </span><span class="c6">(3)</span><span> meaning </span><span class="c6">syms</span><span class="c5"> points to an undersized buffer.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Inside the inner-most loop at </span><span class="c6">(4)</span><span> </span><span class="c6">JBIG2Bitmap</span><span> pointer values are written into the undersized </span><span class="c6">syms</span><span class="c5"> buffer.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Without another trick this loop would write over 32GB of data into the undersized </span><span class="c6">syms</span><span> buffer, certainly causing a crash. To avoid that crash the heap is groomed such that the first few writes off of the end of the </span><span class="c6">syms</span><span> buffer corrupt the </span><span class="c6">GList</span><span> backing buffer. This </span><span class="c6">GList</span><span> stores all known segments and is used by the </span><span class="c6">findSegments</span><span> routine to map from the segment numbers passed in </span><span class="c6">refSegs</span><span> to </span><span class="c6">JBIG2Segment</span><span> pointers. The overflow causes the JBIG2Segment pointers in the GList to be overwritten with </span><span class="c6">JBIG2Bitmap</span><span> pointers at </span><span class="c6">(4)</span><span class="c5">.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Conveniently since </span><span class="c6">JBIG2Bitmap</span><span> inherits from </span><span class="c6">JBIG2Segment</span><span> the </span><span class="c6">seg->getType()</span><span> virtual call succeed even on devices where Pointer Authentication is enabled (which is used to perform a weak type check on virtual calls) but the returned type will now </span><span class="c8">not</span><span> be equal to </span><span class="c6">jbig2SegSymbolDict</span><span> thus causing further writes at </span><span class="c6">(4)</span><span> to not be reached and bounding the extent of the memory corruption</span><span>.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9fFsg7COjTu5Aq2rV9CTyka-eczC_BvD3wrGUP3XL5W9RHoGOKElzDMbDWLrgp1BJfRvEjcw36ZdMgbA2iwnjj0jqDKGBS94UL2tdcH0evSr66V5uGUrhra3PlGyKvCznPT2rSUL3ZegDR-FXRbKw6SJFzNrgubTbAzKXKVrga6h-B3VcHFqOU9zx/s506/image6%282%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9fFsg7COjTu5Aq2rV9CTyka-eczC_BvD3wrGUP3XL5W9RHoGOKElzDMbDWLrgp1BJfRvEjcw36ZdMgbA2iwnjj0jqDKGBS94UL2tdcH0evSr66V5uGUrhra3PlGyKvCznPT2rSUL3ZegDR-FXRbKw6SJFzNrgubTbAzKXKVrga6h-B3VcHFqOU9zx/s506/image6%282%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">A simplified view of the memory layout when the heap overflow occurs showing the undersized-buffer below the GList backing buffer and the JBIG2Bitmap</span></p> <p class="c1"><span class="c9 c7"></span></p> <p class="c3"><span class="c8 c14">Boundless unbounding</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>Directly after the corrupted segments </span><span class="c6">GList</span><span>, the attacker grooms the </span><span class="c6">JBIG2Bitmap</span><span class="c5"> object which represents the current page (the place to where current drawing commands render). </span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c6">JBIG2Bitmaps</span><span class="c5"> are simple wrappers around a backing buffer, storing the buffer’s width and height (in bits) as well as a line value which defines how many bytes are stored for each line.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0hdaagzsqy4Esf9vjf9KS6euQUhDYTUMu9xDO8q4nxUCbVjpLmxBCJkixLWpg8YETgQNC-2DYLlPss0Eo46knC-qDRI0dLEcOHPvjbwGfaF_E_7EimexamWDy68_id27V2y9k0J2moeJg94okQoOtrMyejAg7bYepIgtdcgNH7NRz7Ne-mWeWs1QA/s1070/image7%282%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0hdaagzsqy4Esf9vjf9KS6euQUhDYTUMu9xDO8q4nxUCbVjpLmxBCJkixLWpg8YETgQNC-2DYLlPss0Eo46knC-qDRI0dLEcOHPvjbwGfaF_E_7EimexamWDy68_id27V2y9k0J2moeJg94okQoOtrMyejAg7bYepIgtdcgNH7NRz7Ne-mWeWs1QA/s1070/image7%282%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">The memory layout of the JBIG2Bitmap object showing the segnum, w, h and line fields which are corrupted during the overflow</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>By carefully structuring </span><span class="c6">refSegs</span><span> they can stop the overflow after writing exactly three more </span><span class="c6">JBIG2Bitmap</span><span> pointers after the end of the </span><span class="c6">segments</span><span> </span><span class="c6">GList</span><span> buffer. This overwrites the vtable pointer and the first four fields of the </span><span class="c6">JBIG2Bitmap</span><span> representing the current page.</span><span> Due to the nature of the iOS address space layout these pointers are very likely to be in the second 4GB of virtual memory, with addresses between </span><span class="c6">0x100000000</span><span> and </span><span class="c6">0x1ffffffff</span><span>. Since all iOS hardware is little endian (meaning that the </span><span class="c6">w</span><span> and </span><span class="c6">line</span><span> fields are likely to be overwritten with </span><span class="c6">0x1</span><span> — </span><span>the most-significant half of a </span><span class="c6">JBIG2Bitmap</span><span> pointer) and the </span><span class="c6">segNum</span><span> and </span><span class="c6">h</span><span> fields are likely to be overwritten with the least-significant half of such a pointer, a fairly random value depending on heap layout and ASLR somewhere between </span><span class="c6">0x100000</span><span> and </span><span class="c6">0xffffffff</span><span class="c5">.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>This gives the current destination page </span><span class="c6">JBIG2Bitmap</span><span> an unknown, but very large, value for </span><span class="c6">h</span><span>. Since that </span><span class="c6">h</span><span> value is used for bounds checking and is supposed to reflect the allocated size of the page backing buffer, this has the effect of "unbounding" the drawing canvas. This means that subsequent </span><span class="c6">JBIG2</span><span class="c5"> segment commands can read and write memory outside of the original bounds of the page backing buffer.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>The heap groom also places the current page's backing buffer just below the undersized </span><span class="c6">syms</span><span> buffer, such that when the page </span><span class="c6">JBIG2Bitmap</span><span> is unbounded, it's able to </span><span>read and write</span><span class="c5"> its own fields:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"><hr style="page-break-before:always;display:none;"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidgMVssjmRf7_LhtPKH-MCSJdXOZk5t6EAm-FLazQh5ssP2ksV4kIlzxSIOGz5Elrm8ROuBz92K4-Jthwu4WYa8vN61EgdpB5dbtuCULDRFhqK1TkPOE8xl63p9MAIgf1dNwYKgkYMwlgoNEFcvdDmXy6GdlcRQ5ESrN8d3bAYIEse7dGPQc3cbo8h/s550/image5%282%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidgMVssjmRf7_LhtPKH-MCSJdXOZk5t6EAm-FLazQh5ssP2ksV4kIlzxSIOGz5Elrm8ROuBz92K4-Jthwu4WYa8vN61EgdpB5dbtuCULDRFhqK1TkPOE8xl63p9MAIgf1dNwYKgkYMwlgoNEFcvdDmXy6GdlcRQ5ESrN8d3bAYIEse7dGPQc3cbo8h/s550/image5%282%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">The memory layout showing how the unbounded bitmap backing buffer is able to reference the JBIG2Bitmap object and modify fields in it as it is located after the backing buffer in memory</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>By rendering 4-byte bitmaps at the correct canvas coordinates they can write to all the fields of the page </span><span class="c6">JBIG2Bitmap</span><span> and b</span><span>y carefully choosing new values for </span><span class="c6">w</span><span>, </span><span class="c6">h</span><span> and </span><span class="c6">line</span><span>, </span><span class="c5">they can write to arbitrary offsets from the page backing buffer.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">At this point it would also be possible to write to arbitrary absolute memory addresses if you knew their offsets from the page backing buffer. But how to compute those offsets? Thus far, this exploit has proceeded in a manner very similar to a "canonical" scripting language exploit which in Javascript might end up with an unbounded ArrayBuffer object with access to memory. But in those cases the attacker has the ability to run arbitrary Javascript which can obviously be used to compute offsets and perform arbitrary computations. How do you do that in a single-pass image parser?</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">My other compression format is turing-complete!</span></p> <p class="c3 c10"><span class="c14 c8"></span></p> <p class="c3"><span class="c5">As mentioned earlier, the sequence of steps which implement JBIG2 refinement are very flexible. Refinement steps can reference both the output bitmap and any previously created segments, as well as render output to either the current page or a segment. By carefully crafting the context-dependent part of the refinement decompression, it's possible to craft sequences of segments where only the refinement combination operators have any effect.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>In practice this means it is possible to apply the AND, OR, XOR and XNOR logical operators between memory regions at arbitrary offsets from the current page's </span><span class="c6">JBIG2Bitmap</span><span class="c5"> backing buffer. And since that has been unbounded… it's possible to perform those logical operations on memory at arbitrary out-of-bounds offsets:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFkcS4JjzhV7pzJ5kVEmFj7WLW5Zzdc0JkYDaQBhVYIDayiYOksOID0LFOFGr9qQA44qwe3LRN9KNqOelKIflmcTDnR3k6qrtfvJ4wsoTDyuM59nmE6DGM_n_wOUMNY8HoLybtywIMq2-VdeFVwHHc9aDe0tkExa4hfvXKJ6o_m2QYT2LXp4NGDNJI/s551/image9%281%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFkcS4JjzhV7pzJ5kVEmFj7WLW5Zzdc0JkYDaQBhVYIDayiYOksOID0LFOFGr9qQA44qwe3LRN9KNqOelKIflmcTDnR3k6qrtfvJ4wsoTDyuM59nmE6DGM_n_wOUMNY8HoLybtywIMq2-VdeFVwHHc9aDe0tkExa4hfvXKJ6o_m2QYT2LXp4NGDNJI/s551/image9%281%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">The memory layout showing how logical operators can be applied out-of-bounds</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">It's when you take this to its most extreme form that things start to get really interesting. What if rather than operating on glyph-sized sub-rectangles you instead operated on single bits?</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c8">You can now provide as input a sequence of JBIG2 segment commands which implement a sequence of logical bit operations to apply to the page. And since the page buffer has been unbounded those bit operations can operate on arbitrary memory.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>With a bit of back-of-the-envelope scribbling you can convince yourself that with just the available AND, OR, XOR and XNOR logical</span><span> operators</span><span> you can in fact compute </span><span class="c7">any</span><span class="c5"> computable function - the simplest proof being that you can create a logical NOT operator by XORing with 1 and then putting an AND gate in front of that to form a NAND gate:</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c4"></p> <p class="c4"><span class="c5"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBpKF2iKJhndHIFtGm9xUBVjpXOM4GYcwSdNyfuUvwI-883zmbnhi_Ch6CR4XEaA6D2uaGkU3g8rNocZS_ZlWXD8rTSRGTgYact6ar43k8ywMZG6hnjDz8Yr3pC3Fh4W3dggIA_XriPw1Vc6myG-18TPe8Ffj_NGuywLqz4tpdlrbMAso-CBZcM_4X/s265/image8%282%29.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBpKF2iKJhndHIFtGm9xUBVjpXOM4GYcwSdNyfuUvwI-883zmbnhi_Ch6CR4XEaA6D2uaGkU3g8rNocZS_ZlWXD8rTSRGTgYact6ar43k8ywMZG6hnjDz8Yr3pC3Fh4W3dggIA_XriPw1Vc6myG-18TPe8Ffj_NGuywLqz4tpdlrbMAso-CBZcM_4X/s265/image8%282%29.png" style="max-height: 750px; max-width: 600px;" /></a></span></p> <p class="c12"><span class="c9 c7">An AND gate connected to one input of an XOR gate. The other XOR gate input is connected to the constant value 1 creating an NAND.</span></p> <p class="c1"><span class="c9 c7"></span></p> <p class="c3"><span>A NAND gate is an example of a universal logic gate; one from which all other gates can be built and from which a circuit can be </span><span class="c16"><a class="c21" href="https://www.nand2tetris.org/">built to compute any computable function</a></span><span class="c5">.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c14 c8">Practical circuits</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>JBIG2 doesn't have scripting capabilities, but when combined with a vulnerability, it does have the ability to emulate circuits of arbitrary logic gates operating on arbitrary memory. So why not just use that to build your </span><span class="c7">own</span><span> computer architecture and script that!? That's exactly what this exploit does. Using over 70,000 segment commands defining logical bit operations, they define a small computer architecture with features such as registers and a full 64-bit adder and comparator which they use to search memory and perform arithmetic operations. It's not as fast as Javascript, but it's fundamentally computationally equivalent</span><span>.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span class="c5">The bootstrapping operations for the sandbox escape exploit are written to run on this logic circuit and the whole thing runs in this weird, emulated environment created out of a single decompression pass through a JBIG2 stream. It's pretty incredible, and at the same time, pretty terrifying.</span></p> <p class="c3 c10"><span class="c5"></span></p> <p class="c3"><span>In a future post (currently being finished),</span><span> we'll take a look at exactly how they escape the </span><span class="c6">IMTranscoderAgent</span><span> sandbox.</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/12/a-deep-dive-into-nso-zero-click.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2021-12-15T09:00:00-08:00'>9:00 AM</abbr></a> </span> <span class='post-comment-link'> <a class='comment-link' href='https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.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=5073082417618502919&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=5073082417618502919&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=5073082417618502919&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=5073082417618502919&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=5073082417618502919&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=5073082417618502919&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 class="date-outer"> <h2 class='date-header'><span>Wednesday, December 1, 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='4838136820032157985' itemprop='blogId'/> <meta content='7868790017880077590' itemprop='postId'/> <a name='7868790017880077590'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html'>This shouldn't have happened: A vulnerability postmortem</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-7868790017880077590' itemprop='description articleBody'> <style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=t66UaDGGO9uRFa9A_n0Ge4kPz49mG1-u2NJpkhDAP5E');.lst-kix_q4elz23jmthh-5>li{counter-increment:lst-ctn-kix_q4elz23jmthh-5}ol.lst-kix_q4elz23jmthh-6.start{counter-reset:lst-ctn-kix_q4elz23jmthh-6 0}ul.lst-kix_wy7koemfbmv1-3{list-style-type:none}ul.lst-kix_wy7koemfbmv1-2{list-style-type:none}ul.lst-kix_wy7koemfbmv1-5{list-style-type:none}.lst-kix_2hgb22nra90i-8>li:before{content:"\0025a0 "}ul.lst-kix_wy7koemfbmv1-4{list-style-type:none}ul.lst-kix_wy7koemfbmv1-1{list-style-type:none}.lst-kix_2hgb22nra90i-7>li:before{content:"\0025cb "}ul.lst-kix_wy7koemfbmv1-0{list-style-type:none}ol.lst-kix_q4elz23jmthh-0.start{counter-reset:lst-ctn-kix_q4elz23jmthh-0 0}.lst-kix_2hgb22nra90i-4>li:before{content:"\0025cb "}.lst-kix_2hgb22nra90i-6>li:before{content:"\0025cf "}.lst-kix_2hgb22nra90i-5>li:before{content:"\0025a0 "}ul.lst-kix_z8xtsan2wa5s-5{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-6{list-style-type:none}.lst-kix_q4elz23jmthh-7>li{counter-increment:lst-ctn-kix_q4elz23jmthh-7}ul.lst-kix_z8xtsan2wa5s-7{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-8{list-style-type:none}.lst-kix_2hgb22nra90i-0>li:before{content:"\0025cf "}.lst-kix_2hgb22nra90i-2>li:before{content:"\0025a0 "}.lst-kix_2hgb22nra90i-3>li:before{content:"\0025cf "}ul.lst-kix_z8xtsan2wa5s-0{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-1{list-style-type:none}ul.lst-kix_wy7koemfbmv1-7{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-2{list-style-type:none}ul.lst-kix_wy7koemfbmv1-6{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-3{list-style-type:none}ul.lst-kix_z8xtsan2wa5s-4{list-style-type:none}.lst-kix_2hgb22nra90i-1>li:before{content:"\0025cb "}ul.lst-kix_wy7koemfbmv1-8{list-style-type:none}.lst-kix_q4elz23jmthh-5>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-5,lower-roman) ". "}.lst-kix_q4elz23jmthh-6>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-6,decimal) ". "}.lst-kix_q4elz23jmthh-4>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-4,lower-latin) ". "}.lst-kix_q4elz23jmthh-8>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-8,lower-roman) ". "}.lst-kix_oetzaqvyxxoa-0>li:before{content:"\0025cf "}.lst-kix_oetzaqvyxxoa-1>li:before{content:"\0025cb "}.lst-kix_oetzaqvyxxoa-2>li:before{content:"\0025a0 "}.lst-kix_oetzaqvyxxoa-3>li:before{content:"\0025cf "}.lst-kix_q4elz23jmthh-7>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-7,lower-latin) ". "}.lst-kix_q4elz23jmthh-0>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-0,decimal) ". "}.lst-kix_q4elz23jmthh-1>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-1,lower-latin) ". "}.lst-kix_q4elz23jmthh-2>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-2,lower-roman) ". "}.lst-kix_q4elz23jmthh-3>li{counter-increment:lst-ctn-kix_q4elz23jmthh-3}.lst-kix_q4elz23jmthh-3>li:before{content:"" counter(lst-ctn-kix_q4elz23jmthh-3,decimal) ". "}ul.lst-kix_ohd9cwgi2ci1-5{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-4{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-7{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-6{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-8{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-2{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-3{list-style-type:none}.lst-kix_oetzaqvyxxoa-6>li:before{content:"\0025cf "}.lst-kix_oetzaqvyxxoa-7>li:before{content:"\0025cb "}ul.lst-kix_ugjdqlaeq7aq-0{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-1{list-style-type:none}ol.lst-kix_q4elz23jmthh-5.start{counter-reset:lst-ctn-kix_q4elz23jmthh-5 0}ul.lst-kix_ugjdqlaeq7aq-6{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-7{list-style-type:none}.lst-kix_oetzaqvyxxoa-4>li:before{content:"\0025cb "}.lst-kix_oetzaqvyxxoa-5>li:before{content:"\0025a0 "}.lst-kix_oetzaqvyxxoa-8>li:before{content:"\0025a0 "}ul.lst-kix_ugjdqlaeq7aq-4{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-5{list-style-type:none}ul.lst-kix_ugjdqlaeq7aq-8{list-style-type:none}.lst-kix_x7i5vtjupm2f-2>li:before{content:"\0025a0 "}.lst-kix_x7i5vtjupm2f-6>li:before{content:"\0025cf "}ul.lst-kix_2fd9br43vo52-4{list-style-type:none}ul.lst-kix_2fd9br43vo52-3{list-style-type:none}.lst-kix_ohd9cwgi2ci1-3>li:before{content:"\0025cf "}ul.lst-kix_2fd9br43vo52-6{list-style-type:none}ul.lst-kix_2fd9br43vo52-5{list-style-type:none}ul.lst-kix_2fd9br43vo52-8{list-style-type:none}.lst-kix_fvn7czyjg1wz-2>li:before{content:"\0025a0 "}ul.lst-kix_2fd9br43vo52-7{list-style-type:none}.lst-kix_x7i5vtjupm2f-4>li:before{content:"\0025cb "}.lst-kix_2fd9br43vo52-3>li:before{content:"\0025cf "}.lst-kix_fvn7czyjg1wz-4>li:before{content:"\0025cb "}ul.lst-kix_2fd9br43vo52-0{list-style-type:none}.lst-kix_2fd9br43vo52-5>li:before{content:"\0025a0 "}ul.lst-kix_2fd9br43vo52-2{list-style-type:none}ul.lst-kix_2fd9br43vo52-1{list-style-type:none}.lst-kix_4idwybng3hbx-2>li:before{content:"\0025a0 "}.lst-kix_2fd9br43vo52-7>li:before{content:"\0025cb "}.lst-kix_fvn7czyjg1wz-8>li:before{content:"\0025a0 "}.lst-kix_ohd9cwgi2ci1-1>li:before{content:"\0025cb "}.lst-kix_4idwybng3hbx-0>li:before{content:"\0025cf "}.lst-kix_fvn7czyjg1wz-6>li:before{content:"\0025cf "}.lst-kix_x7i5vtjupm2f-0>li:before{content:"\0025cf "}.lst-kix_ugjdqlaeq7aq-8>li:before{content:"\0025a0 "}ul.lst-kix_fvn7czyjg1wz-2{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-1{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-0{list-style-type:none}.lst-kix_wy7koemfbmv1-4>li:before{content:"\0025cb "}.lst-kix_ugjdqlaeq7aq-6>li:before{content:"\0025cf "}ol.lst-kix_q4elz23jmthh-7.start{counter-reset:lst-ctn-kix_q4elz23jmthh-7 0}.lst-kix_wy7koemfbmv1-0>li:before{content:"\0025cf "}.lst-kix_wy7koemfbmv1-2>li:before{content:"\0025a0 "}.lst-kix_ugjdqlaeq7aq-0>li:before{content:"\0025cf "}.lst-kix_ohd9cwgi2ci1-5>li:before{content:"\0025cf "}.lst-kix_ohd9cwgi2ci1-7>li:before{content:"\0025cf "}.lst-kix_2fd9br43vo52-1>li:before{content:"\0025cb "}.lst-kix_ugjdqlaeq7aq-2>li:before{content:"\0025a0 "}ul.lst-kix_2hgb22nra90i-0{list-style-type:none}ul.lst-kix_2hgb22nra90i-1{list-style-type:none}ul.lst-kix_2hgb22nra90i-2{list-style-type:none}ul.lst-kix_2hgb22nra90i-3{list-style-type:none}.lst-kix_fvn7czyjg1wz-0>li:before{content:"\0025cf "}.lst-kix_ugjdqlaeq7aq-4>li:before{content:"\0025cb "}ul.lst-kix_2hgb22nra90i-4{list-style-type:none}ul.lst-kix_2hgb22nra90i-5{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-8{list-style-type:none}ul.lst-kix_2hgb22nra90i-6{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-7{list-style-type:none}ul.lst-kix_2hgb22nra90i-7{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-6{list-style-type:none}ul.lst-kix_2hgb22nra90i-8{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-5{list-style-type:none}ul.lst-kix_fvn7czyjg1wz-4{list-style-type:none}.lst-kix_x7i5vtjupm2f-8>li:before{content:"\0025a0 "}ul.lst-kix_fvn7czyjg1wz-3{list-style-type:none}ul.lst-kix_81cegzboe9ky-8{list-style-type:none}ul.lst-kix_81cegzboe9ky-5{list-style-type:none}ul.lst-kix_81cegzboe9ky-4{list-style-type:none}ul.lst-kix_81cegzboe9ky-7{list-style-type:none}ul.lst-kix_81cegzboe9ky-6{list-style-type:none}ol.lst-kix_q4elz23jmthh-8.start{counter-reset:lst-ctn-kix_q4elz23jmthh-8 0}ul.lst-kix_oetzaqvyxxoa-2{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-3{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-0{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-1{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-6{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-7{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-4{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-5{list-style-type:none}ul.lst-kix_oetzaqvyxxoa-8{list-style-type:none}ul.lst-kix_4idwybng3hbx-5{list-style-type:none}ul.lst-kix_4idwybng3hbx-4{list-style-type:none}.lst-kix_81cegzboe9ky-2>li:before{content:"\0025a0 "}.lst-kix_81cegzboe9ky-4>li:before{content:"\0025cb "}ul.lst-kix_4idwybng3hbx-7{list-style-type:none}ul.lst-kix_4idwybng3hbx-6{list-style-type:none}.lst-kix_wy7koemfbmv1-6>li:before{content:"\0025cf "}ul.lst-kix_4idwybng3hbx-8{list-style-type:none}.lst-kix_2gk0zovl15u8-4>li:before{content:"\0025cb "}.lst-kix_81cegzboe9ky-0>li:before{content:"\0025cf "}.lst-kix_81cegzboe9ky-6>li:before{content:"\0025cf "}.lst-kix_81cegzboe9ky-8>li:before{content:"\0025a0 "}.lst-kix_wy7koemfbmv1-8>li:before{content:"\0025a0 "}ul.lst-kix_4idwybng3hbx-1{list-style-type:none}ul.lst-kix_4idwybng3hbx-0{list-style-type:none}ul.lst-kix_4idwybng3hbx-3{list-style-type:none}.lst-kix_2gk0zovl15u8-6>li:before{content:"\0025cf "}ul.lst-kix_4idwybng3hbx-2{list-style-type:none}.lst-kix_prwgb47nvxz6-8>li:before{content:"\0025a0 "}.lst-kix_2gk0zovl15u8-8>li:before{content:"\0025a0 "}ul.lst-kix_81cegzboe9ky-1{list-style-type:none}ul.lst-kix_81cegzboe9ky-0{list-style-type:none}ul.lst-kix_81cegzboe9ky-3{list-style-type:none}ul.lst-kix_81cegzboe9ky-2{list-style-type:none}.lst-kix_z8xtsan2wa5s-3>li:before{content:"\0025cf "}.lst-kix_z8xtsan2wa5s-2>li:before{content:"\0025a0 "}.lst-kix_z8xtsan2wa5s-4>li:before{content:"\0025cb "}.lst-kix_prwgb47nvxz6-0>li:before{content:"\0025cf "}.lst-kix_z8xtsan2wa5s-1>li:before{content:"\0025cb "}.lst-kix_z8xtsan2wa5s-5>li:before{content:"\0025a0 "}.lst-kix_prwgb47nvxz6-1>li:before{content:"\0025cb "}ol.lst-kix_q4elz23jmthh-4{list-style-type:none}ol.lst-kix_q4elz23jmthh-3{list-style-type:none}ol.lst-kix_q4elz23jmthh-3.start{counter-reset:lst-ctn-kix_q4elz23jmthh-3 0}ol.lst-kix_q4elz23jmthh-6{list-style-type:none}ol.lst-kix_q4elz23jmthh-5{list-style-type:none}ol.lst-kix_q4elz23jmthh-0{list-style-type:none}ol.lst-kix_q4elz23jmthh-2{list-style-type:none}ol.lst-kix_q4elz23jmthh-1{list-style-type:none}.lst-kix_prwgb47nvxz6-6>li:before{content:"\0025cf "}.lst-kix_prwgb47nvxz6-5>li:before{content:"\0025a0 "}.lst-kix_z8xtsan2wa5s-7>li:before{content:"\0025cb "}.lst-kix_prwgb47nvxz6-3>li:before{content:"\0025cf "}.lst-kix_z8xtsan2wa5s-6>li:before{content:"\0025cf "}.lst-kix_z8xtsan2wa5s-8>li:before{content:"\0025a0 "}.lst-kix_prwgb47nvxz6-2>li:before{content:"\0025a0 "}.lst-kix_prwgb47nvxz6-4>li:before{content:"\0025cb "}.lst-kix_2gk0zovl15u8-3>li:before{content:"\0025cf "}.lst-kix_q4elz23jmthh-6>li{counter-increment:lst-ctn-kix_q4elz23jmthh-6}.lst-kix_2gk0zovl15u8-2>li:before{content:"\0025a0 "}.lst-kix_2gk0zovl15u8-1>li:before{content:"\0025cb "}.lst-kix_cw1yal3teuur-8>li:before{content:"\0025a0 "}.lst-kix_2gk0zovl15u8-0>li:before{content:"\0025cf "}.lst-kix_cw1yal3teuur-6>li:before{content:"\0025cf "}.lst-kix_cw1yal3teuur-7>li:before{content:"\0025cb "}.lst-kix_q4elz23jmthh-4>li{counter-increment:lst-ctn-kix_q4elz23jmthh-4}ol.lst-kix_q4elz23jmthh-8{list-style-type:none}ol.lst-kix_q4elz23jmthh-7{list-style-type:none}.lst-kix_z8xtsan2wa5s-0>li:before{content:"\0025cf "}.lst-kix_cw1yal3teuur-0>li:before{content:"\0025cf "}.lst-kix_cw1yal3teuur-2>li:before{content:"\0025a0 "}.lst-kix_cw1yal3teuur-3>li:before{content:"\0025cf "}ol.lst-kix_q4elz23jmthh-4.start{counter-reset:lst-ctn-kix_q4elz23jmthh-4 0}.lst-kix_cw1yal3teuur-1>li:before{content:"\0025cb "}.lst-kix_cw1yal3teuur-4>li:before{content:"\0025cb "}.lst-kix_cw1yal3teuur-5>li:before{content:"\0025a0 "}.lst-kix_4idwybng3hbx-6>li:before{content:"\0025cf "}.lst-kix_4idwybng3hbx-5>li:before{content:"\0025a0 "}.lst-kix_4idwybng3hbx-7>li:before{content:"\0025cb "}.lst-kix_q4elz23jmthh-0>li{counter-increment:lst-ctn-kix_q4elz23jmthh-0}.lst-kix_4idwybng3hbx-3>li:before{content:"\0025cf "}.lst-kix_4idwybng3hbx-4>li:before{content:"\0025cb "}.lst-kix_4idwybng3hbx-8>li:before{content:"\0025a0 "}.lst-kix_x7i5vtjupm2f-5>li:before{content:"\0025a0 "}.lst-kix_x7i5vtjupm2f-7>li:before{content:"\0025cb "}.lst-kix_x7i5vtjupm2f-3>li:before{content:"\0025cf "}.lst-kix_ohd9cwgi2ci1-4>li:before{content:"\0025cf "}.lst-kix_2fd9br43vo52-2>li:before{content:"\0025a0 "}.lst-kix_fvn7czyjg1wz-3>li:before{content:"\0025cf "}ul.lst-kix_x7i5vtjupm2f-8{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-1{list-style-type:none}ul.lst-kix_x7i5vtjupm2f-4{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-0{list-style-type:none}ul.lst-kix_x7i5vtjupm2f-5{list-style-type:none}ul.lst-kix_ohd9cwgi2ci1-3{list-style-type:none}.lst-kix_2fd9br43vo52-4>li:before{content:"\0025cb "}ul.lst-kix_x7i5vtjupm2f-6{list-style-type:none}.lst-kix_fvn7czyjg1wz-5>li:before{content:"\0025a0 "}ul.lst-kix_ohd9cwgi2ci1-2{list-style-type:none}ul.lst-kix_x7i5vtjupm2f-7{list-style-type:none}.lst-kix_ohd9cwgi2ci1-2>li:before{content:"\0025a0 "}ul.lst-kix_x7i5vtjupm2f-0{list-style-type:none}ul.lst-kix_x7i5vtjupm2f-1{list-style-type:none}ul.lst-kix_prwgb47nvxz6-8{list-style-type:none}.lst-kix_4idwybng3hbx-1>li:before{content:"\0025cb "}ul.lst-kix_x7i5vtjupm2f-2{list-style-type:none}.lst-kix_x7i5vtjupm2f-1>li:before{content:"\0025cb "}ul.lst-kix_x7i5vtjupm2f-3{list-style-type:none}.lst-kix_ohd9cwgi2ci1-0>li:before{content:"\0025cf "}.lst-kix_2fd9br43vo52-6>li:before{content:"\0025cf "}.lst-kix_fvn7czyjg1wz-7>li:before{content:"\0025cb "}.lst-kix_wy7koemfbmv1-5>li:before{content:"\0025a0 "}ul.lst-kix_prwgb47nvxz6-2{list-style-type:none}ul.lst-kix_prwgb47nvxz6-3{list-style-type:none}ul.lst-kix_prwgb47nvxz6-0{list-style-type:none}ul.lst-kix_prwgb47nvxz6-1{list-style-type:none}ul.lst-kix_prwgb47nvxz6-6{list-style-type:none}ul.lst-kix_prwgb47nvxz6-7{list-style-type:none}ul.lst-kix_prwgb47nvxz6-4{list-style-type:none}ul.lst-kix_prwgb47nvxz6-5{list-style-type:none}.lst-kix_wy7koemfbmv1-3>li:before{content:"\0025cf "}.lst-kix_ugjdqlaeq7aq-7>li:before{content:"\0025cb "}.lst-kix_wy7koemfbmv1-1>li:before{content:"\0025cb "}.lst-kix_ohd9cwgi2ci1-6>li:before{content:"\0025cf "}.lst-kix_ugjdqlaeq7aq-1>li:before{content:"\0025cb "}.lst-kix_2fd9br43vo52-0>li:before{content:"\0025cf "}.lst-kix_fvn7czyjg1wz-1>li:before{content:"\0025cb "}.lst-kix_ugjdqlaeq7aq-5>li:before{content:"\0025a0 "}.lst-kix_ugjdqlaeq7aq-3>li:before{content:"\0025cf "}.lst-kix_q4elz23jmthh-1>li{counter-increment:lst-ctn-kix_q4elz23jmthh-1}.lst-kix_ohd9cwgi2ci1-8>li:before{content:"\0025cf "}ol.lst-kix_q4elz23jmthh-1.start{counter-reset:lst-ctn-kix_q4elz23jmthh-1 0}.lst-kix_q4elz23jmthh-8>li{counter-increment:lst-ctn-kix_q4elz23jmthh-8}.lst-kix_q4elz23jmthh-2>li{counter-increment:lst-ctn-kix_q4elz23jmthh-2}.lst-kix_81cegzboe9ky-3>li:before{content:"\0025cf "}.lst-kix_81cegzboe9ky-1>li:before{content:"\0025cb "}.lst-kix_81cegzboe9ky-5>li:before{content:"\0025a0 "}.lst-kix_2fd9br43vo52-8>li:before{content:"\0025a0 "}.lst-kix_wy7koemfbmv1-7>li:before{content:"\0025cb "}.lst-kix_81cegzboe9ky-7>li:before{content:"\0025cb "}.lst-kix_2gk0zovl15u8-5>li:before{content:"\0025a0 "}ul.lst-kix_2gk0zovl15u8-0{list-style-type:none}ul.lst-kix_2gk0zovl15u8-2{list-style-type:none}.lst-kix_2gk0zovl15u8-7>li:before{content:"\0025cb "}ul.lst-kix_2gk0zovl15u8-1{list-style-type:none}li.li-bullet-0:before{margin-left:-18pt;white-space:nowrap;display:inline-block;min-width:18pt}ul.lst-kix_2gk0zovl15u8-4{list-style-type:none}ul.lst-kix_2gk0zovl15u8-3{list-style-type:none}ul.lst-kix_cw1yal3teuur-5{list-style-type:none}.lst-kix_prwgb47nvxz6-7>li:before{content:"\0025cb "}ul.lst-kix_2gk0zovl15u8-6{list-style-type:none}ul.lst-kix_cw1yal3teuur-4{list-style-type:none}ul.lst-kix_2gk0zovl15u8-5{list-style-type:none}ul.lst-kix_cw1yal3teuur-7{list-style-type:none}ul.lst-kix_2gk0zovl15u8-8{list-style-type:none}ul.lst-kix_cw1yal3teuur-6{list-style-type:none}ul.lst-kix_2gk0zovl15u8-7{list-style-type:none}ul.lst-kix_cw1yal3teuur-8{list-style-type:none}ul.lst-kix_cw1yal3teuur-1{list-style-type:none}ol.lst-kix_q4elz23jmthh-2.start{counter-reset:lst-ctn-kix_q4elz23jmthh-2 0}ul.lst-kix_cw1yal3teuur-0{list-style-type:none}ul.lst-kix_cw1yal3teuur-3{list-style-type:none}ul.lst-kix_cw1yal3teuur-2{list-style-type:none}ol{margin:0;padding:0}table td,table th{padding:0}.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:468pt;border-top-color:#000000;border-bottom-style:solid}.c6{color:#000000;font-weight:700;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:italic}.c2{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left;height:11pt}.c31{background-color:#ffffff;padding-top:0pt;padding-bottom:0pt;line-height:1.3;text-align:left;height:11pt}.c13{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left}.c19{background-color:#ffffff;font-size:8pt;font-family:Consolas,"Courier New";color:#009900;font-weight:400}.c34{background-color:#ffffff;padding-top:0pt;padding-bottom:0pt;line-height:1.3;text-align:left}.c12{background-color:#ffffff;font-size:8pt;font-family:Consolas,"Courier New";color:#993333;font-weight:400}.c1{background-color:#ffffff;font-size:8pt;font-family:Consolas,"Courier New";color:#212529;font-weight:400}.c45{color:#333333;text-decoration:none;vertical-align:baseline;font-size:30pt;font-family:"Amatic SC"}.c7{font-size:8pt;font-family:Consolas,"Courier New";color:#333333;font-weight:400}.c4{margin-left:36pt;border-spacing:0;border-collapse:collapse;margin-right:auto}.c14{border-spacing:0;border-collapse:collapse;margin-right:auto}.c26{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.c11{color:#000000;font-weight:400;font-size:11pt;font-family:"Arial"}.c3{padding-top:0pt;padding-bottom:0pt;line-height:1.0;text-align:left}.c10{font-size:8pt;font-family:Consolas,"Courier New";color:#0000ff;font-weight:400}.c40{padding-top:0pt;padding-bottom:0pt;line-height:1.5;text-align:center}.c24{color:#000000;font-weight:700;font-size:11pt;font-family:"Arial"}.c0{font-size:9pt;font-family:Consolas,"Courier New";color:#333333;font-weight:400}.c18{font-size:8pt;font-family:Consolas,"Courier New";color:#008080;font-weight:400}.c5{font-size:9pt;font-family:Consolas,"Courier New";color:#0000ff;font-weight:400}.c30{color:#000000;font-weight:400;font-size:10pt;font-family:"Arial"}.c42{background-color:#ffffff;font-size:8pt;color:#b1b100}.c47{font-weight:400;font-size:8pt;font-family:"Arial"}.c28{background-color:#ffffff;font-size:8pt;color:#000066}.c38{background-color:#ffffff;font-size:8pt;color:#ff0000}.c23{background-color:#ffffff;font-size:8pt;color:#339933}.c8{text-decoration:none;vertical-align:baseline;font-style:normal}.c50{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.c46{text-decoration:none;vertical-align:baseline;font-style:italic}.c51{background-color:#ffffff;font-size:8pt;color:#0000dd}.c9{color:inherit;text-decoration:inherit}.c27{margin-left:36pt;padding-left:0pt}.c17{padding:0;margin:0}.c22{font-size:9pt;color:#008000}.c21{font-weight:400;font-family:Consolas,"Courier New"}.c33{orphans:2;widows:2}.c44{width:33%;height:1px}.c35{font-weight:400;font-style:italic}.c48{font-size:8pt;color:#8b0000}.c29{height:0pt}.c39{font-size:11pt}.c32{font-weight:700}.c41{font-size:10pt}.c20{vertical-align:super}.c49{height:11pt}.c36{color:#000000}.c16{page-break-after:avoid}.c37{font-size:5pt}.c43{margin-left:36pt}.c25{font-size:9pt}.title{padding-top:0pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:0pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:0pt;color:#000000;font-size:10pt;padding-bottom:0pt;font-family:"Arial";line-height:1.0;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:0pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:0pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:0pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:0pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:0pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:0pt;font-family:"Arial";line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:0pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:0pt;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="c50"><div> <p class="c3 c33 c16 subtitle" id="h.hkm9sn4jh6p1"><span class="c30 c8">Posted by Tavis Ormandy, Project Zero</span></p> <p class="c2"><span class="c11 c8"></span></p><h1 class="c13 c16" id="h.ce7bhufj5ofg"><span class="c24 c8">Introduction</span></h1> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">This is an unusual blog post. I normally write posts to highlight some hidden attack surface or interesting complex vulnerability class. This time, I want to talk about a vulnerability that is neither of those things. The striking thing about this vulnerability is just how simple it is. This should have been caught earlier, and I want to explore why that didn’t happen.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">In 2021, all good bugs need a catchy name, so I’m calling this one “BigSig”.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">First, let’s take a look at the bug, I’ll explain how I found it and then try to understand why we missed it for so long.</span></p> <p class="c2"><span class="c11 c8"></span></p><h1 class="c13 c16" id="h.7ttrpxxbs2f9"><span class="c24 c8">Analysis</span></h1> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c26"><a class="c91" href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Overview">Network Security Services</a></span><span> (NSS) is Mozilla's widely used, cross-platform cryptography library. </span><span>When you verify an ASN.1 encoded digital signature</span><span>, NSS will create a </span><span class="c26 c21"><a class="c91" href="https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/security/nss/lib/cryptohi/secvfy.c#120">VFYContext</a></span><span class="c11 c8"> structure to store the necessary data. This includes things like the public key, the hash algorithm, and the signature itself.</span></p> <p class="c2"><span class="c11 c8"></span></p><a id="t.9a4d5980e1b13f527ae3eec9673b5d4e7a7010ba"></a><a id="t.0"></a><table class="c14"><tbody><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3"><span class="c10">struct</span><span class="c7"> </span><span class="c18">VFYContextStr</span><span class="c7 c8"> {</span></p> <p class="c3"><span class="c7"> </span><span class="c18">SECOidTag</span><span class="c7"> hashAlg; </span><span class="c21 c48">/* the hash algorithm */</span></p> <p class="c3"><span class="c7"> </span><span class="c18">SECKEYPublicKey</span><span class="c7 c8"> *key;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">union</span><span class="c7 c8"> {</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">char</span><span class="c7 c8"> buffer[1];</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">char</span><span class="c7 c8"> dsasig[DSA_MAX_SIGNATURE_LEN];</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">char</span><span class="c7 c8"> ecdsasig[2 * MAX_ECKEY_LEN];</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">char</span><span class="c7 c8"> rsasig[(RSA_MAX_MODULUS_BITS + 7) / 8];</span></p> <p class="c3"><span class="c7 c8"> } u;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">int</span><span class="c7 c8"> pkcs1RSADigestInfoLen;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">unsigned</span><span class="c7"> </span><span class="c10">char</span><span class="c7 c8"> *pkcs1RSADigestInfo;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">void</span><span class="c7 c8"> *wincx;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">void</span><span class="c7 c8"> *hashcx;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">const</span><span class="c7"> </span><span class="c18">SECHashObject</span><span class="c7"> *</span><span class="c7">hashobj</span><span class="c7 c8">;</span></p> <p class="c3"><span class="c7"> </span><span class="c18">SECOidTag</span><span class="c7"> encAlg; </span><span class="c21 c48">/* enc alg */</span></p> <p class="c3"><span class="c7"> </span><span class="c18">PRBool</span><span class="c7 c8"> hasSignature;</span></p> <p class="c3"><span class="c7"> </span><span class="c18">SECItem</span><span class="c7 c8"> *params;</span></p> <p class="c34"><span class="c7">};</span></p></td></tr><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3 c16 c33 subtitle" id="h.scw51sqnutu4"><span class="c32">Fig 1</span><span>. The </span><span class="c21">VFYContext</span><span class="c30 c8"> structure from NSS.</span></p></td></tr></tbody></table> <p class="c13"><span><br>The maximum size signature that this structure can handle is whatever the largest union member is, in this case that’s RSA at </span><span class="c26"><a class="c91" href="https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/security/nss/lib/freebl/blapit.h#139">2048 bytes</a></span><span>.</span><span class="c11 c8"> That’s 16384 bits, large enough to accommodate signatures from even the most ridiculously oversized keys.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">Okay, but what happens if you just....make a signature that’s bigger than that?</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">Well, it turns out the answer is memory corruption. Yes, really.</span></p> <p class="c13"><span class="c11 c8"><br>The untrusted signature is simply copied into this fixed-sized buffer, overwriting adjacent members with arbitrary attacker-controlled data.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">The bug is simple to reproduce and affects multiple algorithms. The easiest to demonstrate is RSA-PSS. In fact, just these three commands work:</span></p> <p class="c2"><span class="c11 c8"></span></p><a id="t.488dd1b55b75f3df66e36d7bc8b67538aa8c4fd3"></a><a id="t.1"></a><table class="c14"><tbody><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3"><span class="c5"># We need 16384 bits to fill the buffer, then 32 + 64 + 64 + 64 bits to overflow to </span><span class="c5">hashobj</span><span class="c5 c8">, </span></p> <p class="c3"><span class="c5 c8"># which contains function pointers (bigger would work too, but takes longer to generate).</span></p> <p class="c3"><span class="c21 c25">$ openssl </span><span class="c21 c25">genpkey</span><span class="c21 c25"> -algorithm rsa-pss -pkeyopt rsa_keygen_bits:$((16384 + 32 + 64 + 64 + 64)) -pkeyopt rsa_keygen_primes:5 -out </span><span class="c21 c25">bigsig.key</span></p> <p class="c3"><span class="c5 c8"># Generate a self-signed certificate from that key</span></p> <p class="c3"><span class="c8 c21 c36 c25">$ openssl req -x509 -new -key bigsig.key -subj "/CN=BigSig" -sha256 -out bigsig.cer</span></p> <p class="c3"><span class="c5 c8"># Verify it with NSS...</span></p> <p class="c3"><span class="c21 c25">$ </span><span class="c21 c25">vfychain</span><span class="c8 c21 c36 c25"> -a bigsig.cer </span></p> <p class="c3"><span class="c8 c21 c25 c36">Segmentation fault</span></p></td></tr><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3 c33 c16 subtitle" id="h.48dytsqrxb61"><span class="c32">Fig 2</span><span class="c30 c8">. Reproducing the BigSig vulnerability in three easy commands.</span></p></td></tr></tbody></table> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>The actual code that does the corruption varies based on the algorithm; </span><span class="c26"><a class="c91" href="https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/security/nss/lib/cryptohi/secvfy.c#477">here is the code</a></span><span> for RSA-PSS. The bug is that there is simply no bounds checking at all; </span><span class="c21">sig</span><span> and </span><span class="c21">key</span><span> are arbitrary-length, attacker-controlled blobs, and </span><span class="c21">cx->u</span><span class="c11 c8"> is a fixed-size buffer.</span></p> <p class="c2"><span class="c11 c8"></span></p><a id="t.5f5007590f34efd7a404c6d3f020f980f0b97070"></a><a id="t.2"></a><table class="c14"><tbody><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3"><span class="c7"> </span><span class="c10">case</span><span class="c7"> </span><span class="c7">rsaPssKey</span><span class="c7 c8">:</span></p> <p class="c3"><span class="c7 c8"> sigLen = SECKEY_SignatureLen(key);</span></p> <p class="c3"><span class="c7"> </span><span class="c10">if</span><span class="c7 c8"> (sigLen == 0) {</span></p> <p class="c3"><span class="c7"> </span><span class="c21 c48">/* error set by SECKEY_SignatureLen */</span></p> <p class="c3"><span class="c7 c8"> rv = SECFailure;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">break</span><span class="c7 c8">;</span></p> <p class="c3"><span class="c7 c8"> }</span></p> <p class="c31"><span class="c7 c8"></span></p> <p class="c3"><span class="c7"> </span><span class="c10">if</span><span class="c7 c8"> (sig->len != sigLen) {</span></p> <p class="c3"><span class="c7 c8"> PORT_SetError(SEC_ERROR_BAD_SIGNATURE);</span></p> <p class="c3"><span class="c7 c8"> rv = SECFailure;</span></p> <p class="c3"><span class="c7"> </span><span class="c10">break</span><span class="c7 c8">;</span></p> <p class="c3"><span class="c7 c8"> }</span></p> <p class="c31"><span class="c7 c8"></span></p> <p class="c3"><span class="c7 c8"> PORT_Memcpy(cx->u.buffer, sig->data, sigLen);</span></p> <p class="c34"><span class="c7"> </span><span class="c10">break</span><span class="c7">;</span></p></td></tr><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3 c33 c16 subtitle" id="h.b383i7m17o2u"><span class="c32">Fig 3</span><span>. The signature size must match the size of the key, but there are no other limitations. </span><span class="c21">cx->u</span><span> is a fixed-size buffer, and </span><span class="c21">sig</span><span class="c30 c8"> is an arbitrary-length, attacker-controlled blob.</span></p></td></tr></tbody></table> <p class="c2"><span class="c11 c8"></span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">I think this vulnerability raises a few immediate questions:</span></p> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_2fd9br43vo52-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Was this a recent code change or regression that hadn’t been around long enough to be discovered? </span><span class="c32">No</span><span>, the original code was </span><span class="c26"><a class="c91" href="https://hg.mozilla.org/projects/nss/annotate/41f5eb9e5df23951883ba3243f3ae51550663d77/security/nss/lib/cryptohi/secvfy.c#l158">checked in</a></span><span> with ECC support on the 17th October 2003, but wasn't exploitable until some </span><span class="c26"><a class="c91" href="https://hg.mozilla.org/projects/nss/diff/10393/security/nss/lib/cryptohi/seckey.c#l1.63">refactoring</a></span><span> in June 2012. In 2017, RSA-PSS support was </span><span class="c26"><a class="c91" href="https://hg.mozilla.org/projects/nss/rev/84e886ea090e36c69df58a71665a97bd25c62d02">added</a></span><span class="c11 c8"> and made the same error.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_81cegzboe9ky-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Does this bug require a long time to generate a key that triggers the bug? </span><span class="c32">No</span><span>, the example above generates a real key and signature, but it can just be garbage as the overflow happens before the signature check. A few kilobytes of </span><span class="c21">A</span><span>’s works just fine.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_2gk0zovl15u8-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Does reaching the vulnerable code require some complicated state that fuzzers and static analyzers would have difficulty synthesizing, like hashes or checksums? </span><span class="c32">No</span><span>, it has to </span><span>be well-formed</span><span class="c11 c8"> DER, that’s about it.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_z8xtsan2wa5s-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Is this an uncommon code path? </span><span class="c32">No</span><span>, Firefox does not use this code path for RSA-PSS signatures, but the default entrypoint for certificate verification in NSS, </span><span class="c21">CERT_VerifyCertificate(),</span><span class="c11 c8"> is vulnerable.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_oetzaqvyxxoa-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Is it specific to the RSA-PSS algorithm? </span><span class="c32">No</span><span class="c11 c8">, it also affects DSA signatures.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_prwgb47nvxz6-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Is it unexploitable, or otherwise limited impact? </span><span class="c32">No</span><span>, the </span><span class="c21">hashobj</span><span class="c21"> </span><span>member can be clobbered. That object contains </span><span class="c26"><a class="c91" href="https://searchfox.org/mozilla-central/rev/41a8c58186206985c0d70d3d460c04ac844d11d0/security/nss/lib/util/hasht.h#45">function pointers</a></span><span>,</span><span> which are used immediately.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c32">This wasn’t a process failure, the vendor did everything right</span><span>. Mozilla has a mature, world-class security team. They pioneered </span><span class="c26"><a class="c91" href="https://www.mozilla.org/en-US/security/bug-bounty/">bug bounties</a></span><span>,</span><span> </span><span>invest</span><span> in </span><span class="c26"><a class="c91" href="https://research.mozilla.org/rust/">memory safety</a></span><span>, fuzzing and </span><span class="c26"><a class="c91" href="https://coverage.moz.tools/">test coverage</a></span><span class="c11 c8">.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>NSS was one of the very first projects included with </span><span class="c26"><a class="c91" href="https://google.github.io/oss-fuzz/">oss-fuzz</a></span><span>, it was officially supported since at least </span><span class="c26"><a class="c91" href="https://github.com/google/oss-fuzz/commit/3d325bf20f0b09961b6c7de34aa4da0d16cfa67d">October 2014</a></span><span>. Mozilla also fuzz NSS themselves with </span><span class="c26"><a class="c91" href="https://llvm.org/docs/LibFuzzer.html">libFuzzer</a></span><span>, and have contributed their own </span><span class="c26"><a class="c91" href="https://searchfox.org/mozilla-central/source/security/nss/fuzz/asn1_mutators.cc">mutator</a></span><span> collection and distilled </span><span class="c26"><a class="c91" href="https://github.com/mozilla/nss-fuzzing-corpus">coverage corpus</a></span><span>. There is an extensive testsuite, and nightly </span><span class="c26"><a class="c91" href="https://firefox-source-docs.mozilla.org/tools/sanitizer/asan.html">ASAN</a></span><span> builds.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>I'm generally skeptical of static analysis, but this seems like a simple missing bounds check that should be easy to find. Coverity has been monitoring NSS since at least </span><span class="c26"><a class="c91" href="https://scan.coverity.com/projects/nss">December 2008</a></span><span>, and also </span><span>appears</span><span class="c11 c8"> to have failed to discover this.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>Until 2015, Google Chrome </span><span class="c26"><a class="c91" href="https://chromium.googlesource.com/chromium/third_party/nss/+/refs/heads/master/README.chromium">used</a></span><span> NSS, and maintained their own testsuite and fuzzing infrastructure independent of Mozilla. Today, Chrome platforms use </span><span class="c26"><a class="c91" href="https://boringssl.googlesource.com/boringssl/">BoringSSL</a></span><span class="c11 c8">, but the NSS port is still maintained.</span></p> <p class="c2"><span class="c11 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_ohd9cwgi2ci1-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Did Mozilla have good test coverage for the vulnerable areas? </span><span class="c26"><a class="c91" href="https://coverage.moz.tools/#revision=latest&path=security%2Fnss%2Flib%2Fcryptohi%2Fsecvfy.c&suite=gtest&view=file&line=201">YES</a></span><span class="c11 c8">.</span></li><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Did Mozilla/chrome/oss-fuzz have relevant inputs in their fuzz corpus? </span><span class="c26"><a class="c91" href="https://storage.googleapis.com/oss-fuzz-coverage/nss/reports/20211027/linux/src/nss/lib/cryptohi/secvfy.c.html#L201">YES</a></span><span class="c11 c8">.</span></li><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Is there a mutator capable of extending </span><span>ASN1_ITEM</span><span>s? </span><span class="c26"><a class="c91" href="https://codereview.chromium.org/1677803002/patch/180001/190008">YES</a></span><span>.</span></li><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Is this an </span><span class="c26"><a class="c91" href="https://github.com/google/sanitizers/wiki/AddressSanitizerIntraObjectOverflow">intra-object overflow</a></span><span>, or other form of corruption that ASAN would have difficulty detecting? NO, it's a textbook buffer overflow that ASAN can easily detect.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><h1 class="c13 c16" id="h.rdx8pm6yen5"><span class="c24 c8">How did I find the bug?</span></h1> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">I've been experimenting with alternative methods for measuring code coverage, to see if any have any practical use in fuzzing. The fuzzer that discovered this vulnerability used a combination of two approaches, stack coverage and object isolation.</span></p> <p class="c2"><span class="c11 c8"></span></p><h2 class="c13 c16" id="h.j6f1q7cn6h69"><span class="c24 c8">Stack Coverage</span></h2> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>The most common method of measuring code coverage is block coverage, or </span><span class="c26"><a class="c91" href="https://clang.llvm.org/docs/SanitizerCoverage.html#edge-coverage">edge coverage</a></span><span> when source code is available. I’ve been curious if that is always sufficient. For example, consider a simple dispatch table with a combination of trusted and untrusted parameters, as in </span><span class="c32">Fig 4</span><span class="c11 c8">.</span></p> <p class="c2"><span class="c11 c8"></span></p><a id="t.bd548b01b21e1cf0cdd7eaa859aff19f6d0661e5"></a><a id="t.3"></a><table class="c14"><tbody><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3"><span class="c23 c21">#include <stdio.h></span></p> <p class="c3"><span class="c23 c21">#include <string.h></span></p> <p class="c3"><span class="c23 c21">#include <limits.h></span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c12">static</span><span class="c1"> </span><span class="c12">char</span><span class="c1"> buf</span><span class="c19">[</span><span class="c21 c51">128</span><span class="c19">]</span><span class="c23 c21">;</span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c12">void</span><span class="c1"> cmd_handler_foo</span><span class="c19">(</span><span class="c12">int</span><span class="c1"> a</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">size_t</span><span class="c1"> b</span><span class="c19">)</span><span class="c1"> </span><span class="c19">{</span><span class="c1"> </span><span class="c28 c21">memset</span><span class="c19">(</span><span class="c1">buf</span><span class="c23 c21">,</span><span class="c1"> a</span><span class="c23 c21">,</span><span class="c1"> b</span><span class="c19">)</span><span class="c23 c21">;</span><span class="c1"> </span><span class="c19">}</span></p> <p class="c3"><span class="c12">void</span><span class="c1"> cmd_handler_bar</span><span class="c19">(</span><span class="c12">int</span><span class="c1"> a</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">size_t</span><span class="c1"> b</span><span class="c19">)</span><span class="c1"> </span><span class="c19">{</span><span class="c1"> cmd_handler_foo</span><span class="c19">(</span><span class="c21 c38">'A'</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">sizeof</span><span class="c1"> buf</span><span class="c19">)</span><span class="c23 c21">;</span><span class="c1"> </span><span class="c19">}</span></p> <p class="c3"><span class="c12">void</span><span class="c1"> cmd_handler_baz</span><span class="c19">(</span><span class="c12">int</span><span class="c1"> a</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">size_t</span><span class="c1"> b</span><span class="c19">)</span><span class="c1"> </span><span class="c19">{</span><span class="c1"> cmd_handler_bar</span><span class="c19">(</span><span class="c1">a</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">sizeof</span><span class="c1"> buf</span><span class="c19">)</span><span class="c23 c21">;</span><span class="c1"> </span><span class="c19">}</span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c12">typedef</span><span class="c1"> </span><span class="c12">void</span><span class="c1"> </span><span class="c19">(</span><span class="c23 c21">*</span><span class="c1"> dispatch_t</span><span class="c19">)(</span><span class="c12">int</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c12">size_t</span><span class="c19">)</span><span class="c23 c21">;</span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c1">dispatch_t handlers</span><span class="c19">[</span><span class="c1">UCHAR_MAX</span><span class="c19">]</span><span class="c1"> </span><span class="c23 c21">=</span><span class="c1"> </span><span class="c19">{</span></p> <p class="c3"><span class="c1"> cmd_handler_foo</span><span class="c23 c21">,</span></p> <p class="c3"><span class="c1"> cmd_handler_bar</span><span class="c23 c21">,</span></p> <p class="c3"><span class="c1"> cmd_handler_baz</span><span class="c23 c21">,</span></p> <p class="c3"><span class="c19">}</span><span class="c23 c21">;</span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c12">int</span><span class="c1"> main</span><span class="c19">(</span><span class="c12">int</span><span class="c1"> argc</span><span class="c21 c23">,</span><span class="c1"> </span><span class="c12">char</span><span class="c1"> </span><span class="c23 c21">**</span><span class="c1">argv</span><span class="c19">)</span></p> <p class="c3"><span class="c19">{</span></p> <p class="c3"><span class="c1"> </span><span class="c12">int</span><span class="c1"> cmd</span><span class="c23 c21">;</span></p> <p class="c3"><span class="c1 c8"> </span></p> <p class="c3"><span class="c1"> </span><span class="c21 c42">while</span><span class="c1"> </span><span class="c19">((</span><span class="c1">cmd </span><span class="c23 c21">=</span><span class="c1"> </span><span class="c28 c21">getchar</span><span class="c19">())</span><span class="c1"> </span><span class="c23 c21">!=</span><span class="c1"> EOF</span><span class="c19">)</span><span class="c1"> </span><span class="c19">{</span></p> <p class="c3"><span class="c1"> </span><span class="c42 c21">if</span><span class="c1"> </span><span class="c19">(</span><span class="c1">handlers</span><span class="c19">[</span><span class="c1">cmd</span><span class="c19">])</span><span class="c1"> </span><span class="c19">{</span></p> <p class="c3"><span class="c1"> handlers</span><span class="c19">[</span><span class="c1">cmd</span><span class="c19">](</span><span class="c21 c28">getchar</span><span class="c19">()</span><span class="c23 c21">,</span><span class="c1"> </span><span class="c28 c21">getchar</span><span class="c19">())</span><span class="c23 c21">;</span></p> <p class="c3"><span class="c1"> </span><span class="c19">}</span></p> <p class="c3"><span class="c1"> </span><span class="c19">}</span></p> <p class="c3"><span class="c19">}</span></p></td></tr><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3 c33 c16 subtitle" id="h.9a31y6u3z63v"><span class="c32">Fig 4.</span><span> The coverage </span><span>of command</span><span> </span><span class="c32">bar</span><span> is a superset of command </span><span class="c32">foo</span><span>, so an input containing the latter would be discarded during corpus minimization. There is a vulnerability unreachable via command </span><span class="c32">bar </span><span>that might never be discovered. Stack coverage would correctly keep both inputs.</span><sup><a href="#ftnt1" id="ftnt_ref1" name="ftnt_ref1">[1]</a></sup></p></td></tr></tbody></table> <p class="c2"><span class="c11 c8"></span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">To solve this problem, I’ve been experimenting with monitoring the call stack during execution.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">The naive implementation is too slow to be practical, but after a lot of optimization I had come up with a library that was fast enough to be integrated into coverage-guided fuzzing, and was testing how it performed with NSS and other libraries.</span></p> <p class="c2"><span class="c11 c8"></span></p><h2 class="c13 c16" id="h.hx3w7e9emdvh"><span class="c24 c8">Object Isolation</span></h2> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">Many data types are constructed from smaller records. PNG files are made of chunks, PDF files are made of streams, ELF files are made of sections, and X.509 certificates are made of ASN.1 TLV items. If a fuzzer has some understanding of the underlying format, it can isolate these records and extract the one(s) causing some new stack trace to be found.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">The fuzzer I was using is able to isolate and extract interesting new ASN.1 OIDs, SEQUENCEs, INTEGERs, and so on. Once extracted, it can then randomly combine or insert them into template data. This isn’t really a new idea, but is a new implementation. I'm planning to open source this code in the future.</span></p> <p class="c2"><span class="c11 c8"></span></p><h2 class="c13 c16" id="h.eyoyd5te8xd4"><span class="c24 c8">Do these approaches work?</span></h2> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">I wish that I could say that discovering this bug validates my ideas, but I’m not sure it does. I was doing some moderately novel fuzzing, but I see no reason this bug couldn’t have been found earlier with even rudimentary fuzzing techniques.</span></p> <p class="c2"><span class="c11 c8"></span></p><h2 class="c13 c16" id="h.gjjikaq7urz2"><span class="c24 c8">Lessons Learned</span></h2> <p class="c2"><span class="c24 c8"></span></p> <p class="c13"><span class="c11 c8">How did extensive, customized fuzzing with impressive coverage metrics fail to discover this bug?</span></p> <p class="c2"><span class="c11 c8"></span></p><h3 class="c13 c16" id="h.jjxdnfbzkeky"><span class="c24 c8">What went wrong</span></h3> <p class="c2"><span class="c11 c8"></span></p><h4 class="c13 c16" id="h.tq5bofk5duby"><span>Issue #1 </span><span class="c35">Missing end-to-end testing</span><span class="c6">.</span></h4> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>NSS is a </span><span class="c26"><a class="c91" href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines">modular</a></span><span> library. This layered design is reflected in the </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/source/fuzz/">fuzzing</a></span><span> approach, as each component is fuzzed independently. For example, the </span><span class="c26"><a class="c91" href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_Tech_Notes/nss_tech_note1#how_to_use_the_nss_asn.1_and_quickder_decoders">QuickDER</a></span><span> decoder is tested </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/source/fuzz/quickder_target.cc">extensively</a></span><span>, but the fuzzer simply </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/rev/5f2fa238b58c9158a52c0681ca2a67958a353082/fuzz/quickder_target.cc#72">creates and discards</a></span><span class="c11 c8"> objects and never uses them.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c2"><span class="c11 c8"></span></p><a id="t.f4e0f36d5a94fbada814aadd6ad3730de37e434f"></a><a id="t.4"></a><table class="c4"><tbody><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3"><span class="c5">extern</span><span class="c0"> </span><span class="c21 c22">"C"</span><span class="c0"> </span><span class="c5">int</span><span class="c0"> LLVMFuzzerTestOneInput(</span><span class="c5">const</span><span class="c0 c8"> uint8_t *Data, size_t Size) {</span></p> <p class="c3"><span class="c0"> </span><span class="c5">char</span><span class="c0 c8"> *dest[2048];</span></p> <p class="c31"><span class="c0 c8"></span></p> <p class="c3"><span class="c0"> </span><span class="c5">for</span><span class="c0"> (</span><span class="c5">auto</span><span class="c0 c8"> tpl : templates) {</span></p> <p class="c3"><span class="c0 c8"> PORTCheapArenaPool pool;</span></p> <p class="c3"><span class="c0"> SECItem buf = {siBuffer, </span><span class="c5">const_cast</span><span class="c0"><</span><span class="c5">unsigned</span><span class="c0"> </span><span class="c5">char</span><span class="c0 c8"> *>(Data),</span></p> <p class="c3"><span class="c0"> </span><span class="c5">static_cast</span><span class="c0"><</span><span class="c5">unsigned</span><span class="c0"> </span><span class="c5">int</span><span class="c0 c8">>(Size)};</span></p> <p class="c31"><span class="c0 c8"></span></p> <p class="c3"><span class="c0 c8"> PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);</span></p> <p class="c3"><span class="c0"> (</span><span class="c5">void</span><span class="c0 c8">)SEC_QuickDERDecodeItem(&pool.arena, dest, tpl, &buf);</span></p> <p class="c3"><span class="c0 c8"> PORT_DestroyCheapArena(&pool);</span></p> <p class="c34"><span class="c0"> }</span></p></td></tr><tr class="c29"><td class="c15" colspan="1" rowspan="1"> <p class="c3 c16 subtitle" id="h.9rchnmg47ypy"><span class="c32">Fig 5.</span><span class="c8 c30"> The QuickDER fuzzer simply creates and discards objects. This verifies the ASN.1 parsing, but not whether other components handle the resulting objects correctly.</span></p></td></tr></tbody></table> <p class="c2 c43"><span class="c11 c8"></span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>This fuzzer might have produced a </span><span class="c21">SECKEYPublicKey</span><span class="c11 c8"> that could have reached the vulnerable code, but as the result was never used to verify a signature, the bug could never be discovered.</span></p> <p class="c2"><span class="c11 c8"></span></p><h4 class="c13 c16" id="h.y3p8hf8j0qqo"><span>Issue #2 </span><span class="c35">Arbitrary size limits</span><span class="c6">.</span></h4> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>There is an arbitrary limit of </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/source/fuzz/options/quickder.options">10000 bytes</a></span><span class="c11 c8"> placed on fuzzed input. There is no such limit within NSS; many structures can exceed this size. This vulnerability demonstrates that errors happen at extremes, so this limit should be chosen thoughtfully.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>A reasonable choice might be 2</span><span class="c20">24</span><span>-1 bytes, the </span><span class="c26"><a class="c91" href="https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2">largest possible</a></span><span class="c11 c8"> certificate that can be presented by a server during a TLS handshake negotiation.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>While NSS might handle objects even larger than this, TLS cannot possibly be involved, reducing the overall severity of any vulnerabilities missed.</span></p> <p class="c2"><span class="c11 c8"></span></p><h4 class="c13 c16" id="h.fny13amxfoss"><span>Issue #3 </span><span class="c35">Misleading metrics</span><span class="c6">.</span></h4> <p class="c2"><span class="c11 c46"></span></p> <p class="c13"><span class="c11 c8">All of the NSS fuzzers are represented in combined coverage metrics by oss-fuzz, rather than their individual coverage. This data proved misleading, as the vulnerable code is fuzzed extensively but by fuzzers that could not possibly generate a relevant input.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>This is because fuzzers like the </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/source/fuzz/tls_server_target.cc">tls_server_target</a></span><span> use fixed, </span><span class="c26"><a class="c91" href="https://searchfox.org/nss/source/fuzz/tls_server_certs.cc">hardcoded</a></span><span class="c11 c8"> certificates. This exercises code relevant to certificate verification, but only fuzzes TLS messages and protocol state changes.</span></p> <p class="c2"><span class="c11 c8"></span></p><h3 class="c13 c16" id="h.5iljytmv85wn"><span class="c24 c8">What Worked</span></h3> <p class="c2"><span class="c24 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_4idwybng3hbx-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>The design of the mozilla::pkix validation library prevented this bug from being worse than it could have been. </span><span class="c11 c8">Unfortunately it is unused outside of Firefox and Thunderbird.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">It’s debatable whether this was just good fortune or not. It seems likely RSA-PSS would eventually be permitted by mozilla::pkix, even though it was not today.</span></p> <p class="c2"><span class="c8 c11"></span></p><h2 class="c13 c16" id="h.t0ijbutqkb5h"><span class="c8 c24">Recommendations</span></h2> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span class="c11 c8">This issue demonstrates that even extremely well-maintained C/C++ can have fatal, trivial mistakes.</span></p> <p class="c2"><span class="c11 c8"></span></p><h3 class="c13 c16" id="h.yesvwttsgrsf"><span class="c24 c8">Short Term</span></h3> <p class="c2"><span class="c24 c8"></span></p><ul style="padding: 0;" class="c17 lst-kix_x7i5vtjupm2f-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>Raise the maximum size of ASN.1 objects produced by libFuzzer from 10,000 to 2</span><span class="c20">24</span><span class="c11 c8">-1 = 16,777,215 bytes.</span></li></ul><ul style="padding: 0;" class="c17 lst-kix_wy7koemfbmv1-0 start"><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span class="c11 c8">The QuickDER fuzzer should call some relevant APIs with any objects successfully created before destroying them.</span></li><li style="margin-left: 46pt;" class="c13 c27 li-bullet-0"><span>The oss-fuzz code coverage metrics should be divided by fuzzer, not by project.</span></li></ul> <p class="c2"><span class="c11 c8"></span></p><h1 class="c13 c16" id="h.i49j9otzq1oi"><span class="c24 c8">Solution</span></h1> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>This vulnerability is </span><span>CVE-2021-43527,</span><span> and is resolved in </span><span class="c26"><a class="c91" href="https://www.mozilla.org/en-US/security/advisories/">NSS 3.73.0</a></span><span class="c11 c8">. If you are a vendor that distributes NSS in your products, you will most likely need to update or backport the patch.</span></p> <p class="c2"><span class="c11 c8"></span></p><h1 class="c13 c16" id="h.9canxlbf0rtp"><span class="c24 c8">Credits</span></h1> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>I would not have been able to find this bug without assistance from my colleagues from Ch</span><span>rome, Ryan S</span><span class="c11 c8">leevi and David Benjamin, who helped answer my ASN.1 encoding questions and engaged in thoughtful discussion on the topic.</span></p> <p class="c2"><span class="c11 c8"></span></p> <p class="c13"><span>Thanks to the NSS team, who helped triage and analyze the vulnerability.</span></p><hr class="c44"><div> <p class="c3 c33"><a href="#ftnt_ref1" id="ftnt1" name="ftnt1">[1]</a><span class="c8 c36 c47"> In this minimal example, a workaround if source was available would be to use a combination of sancov's data-flow instrumentation options, but that also fails on more complex variants.</span></p></div></div> <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/12/this-shouldnt-have-happened.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2021-12-01T10:38:00-08:00'>10:38 AM</abbr></a> </span> <span class='post-comment-link'> <a class='comment-link' href='https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.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=7868790017880077590&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=7868790017880077590&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=7868790017880077590&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=7868790017880077590&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=7868790017880077590&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=7868790017880077590&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=2022-02-10T08:58:00-08: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-12-01T10:38:00-08: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/2025/'> 2025 </a> <span class='post-count' dir='ltr'>(2)</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/2025/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/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(12)</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/12/'> December </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/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 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/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='posts'> <li><a href='https://googleprojectzero.blogspot.com/2021/12/a-deep-dive-into-nso-zero-click.html'>A deep dive into an NSO zero-click iMessage exploi...</a></li> <li><a href='https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html'>This shouldn't have happened: A vulnerability post...</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/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 collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/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/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/688949419-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY5eZmOfBlhbMriTb96VPIv5qdBrYw:1739857327226';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d4838136820032157985','//googleprojectzero.blogspot.com/2021/','4838136820032157985'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '4838136820032157985', 'title': 'Project Zero', 'url': 'https://googleprojectzero.blogspot.com/2021/', 'canonicalUrl': 'https://googleprojectzero.blogspot.com/2021/', '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/f6e0cc369f0f1a05', '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': '2021', 'pageTitle': 'Project Zero: 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/', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': true, 'isLabelSearch': false, 'archive': {'year': 2021, 'rangeMessage': 'Showing posts from 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/1360229384-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>