CINXE.COM
Project Zero: November 2022
<!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/2022/11/' 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/2022/11/' 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: November 2022</title> <style type='text/css'>@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;font-stretch:normal;font-display:swap;src:url(//fonts.gstatic.com/s/opensans/v40/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0B4gaVY.eot);}</style> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Simple Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#66bbdd"/> <Group description="Page Text" selector="body"> <Variable name="body.font" description="Font" type="font" default="normal normal 12px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="body.text.color" description="Text Color" type="color" default="#222222"/> </Group> <Group description="Backgrounds" selector=".body-fauxcolumns-outer"> <Variable name="body.background.color" description="Outer Background" type="color" default="#66bbdd"/> <Variable name="content.background.color" description="Main Background" type="color" default="#ffffff"/> <Variable name="header.background.color" description="Header Background" type="color" default="transparent"/> </Group> <Group description="Links" selector=".main-outer"> <Variable name="link.color" description="Link Color" type="color" default="#2288bb"/> <Variable name="link.visited.color" description="Visited Color" type="color" default="#888888"/> <Variable name="link.hover.color" description="Hover Color" type="color" default="#33aaff"/> </Group> <Group description="Blog Title" selector=".header h1"> <Variable name="header.font" description="Font" type="font" default="normal normal 60px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="header.text.color" description="Title Color" type="color" default="#3399bb" /> </Group> <Group description="Blog Description" selector=".header .description"> <Variable name="description.text.color" description="Description Color" type="color" default="#777777" /> </Group> <Group description="Tabs Text" selector=".tabs-inner .widget li a"> <Variable name="tabs.font" description="Font" type="font" default="normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="tabs.text.color" description="Text Color" type="color" default="#999999"/> <Variable name="tabs.selected.text.color" description="Selected Color" type="color" default="#000000"/> </Group> <Group description="Tabs Background" selector=".tabs-outer .PageList"> <Variable name="tabs.background.color" description="Background Color" type="color" default="#f5f5f5"/> <Variable name="tabs.selected.background.color" description="Selected Color" type="color" default="#eeeeee"/> </Group> <Group description="Post Title" selector="h3.post-title, .comments h4"> <Variable name="post.title.font" description="Font" type="font" default="normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> </Group> <Group description="Date Header" selector=".date-header"> <Variable name="date.header.color" description="Text Color" type="color" default="#000000"/> <Variable name="date.header.background.color" description="Background Color" type="color" default="transparent"/> <Variable name="date.header.font" description="Text Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="date.header.padding" description="Date Header Padding" type="string" default="inherit"/> <Variable name="date.header.letterspacing" description="Date Header Letter Spacing" type="string" default="inherit"/> <Variable name="date.header.margin" description="Date Header Margin" type="string" default="inherit"/> </Group> <Group description="Post Footer" selector=".post-footer"> <Variable name="post.footer.text.color" description="Text Color" type="color" default="#666666"/> <Variable name="post.footer.background.color" description="Background Color" type="color" default="#f9f9f9"/> <Variable name="post.footer.border.color" description="Shadow Color" type="color" default="#eeeeee"/> </Group> <Group description="Gadgets" selector="h2"> <Variable name="widget.title.font" description="Title Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="widget.title.text.color" description="Title Color" type="color" default="#000000"/> <Variable name="widget.alternate.text.color" description="Alternate Color" type="color" default="#999999"/> </Group> <Group description="Images" selector=".main-inner"> <Variable name="image.background.color" description="Background Color" type="color" default="#ffffff"/> <Variable name="image.border.color" description="Border Color" type="color" default="#eeeeee"/> <Variable name="image.text.color" description="Caption Text Color" type="color" default="#000000"/> </Group> <Group description="Accents" selector=".content-inner"> <Variable name="body.rule.color" description="Separator Line Color" type="color" default="#eeeeee"/> <Variable name="tabs.border.color" description="Tabs Border Color" type="color" default="transparent"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#eeeeee" default="$(color) none repeat scroll top left"/> <Variable name="body.background.override" description="Body Background Override" type="string" default=""/> <Variable name="body.background.gradient.cap" description="Body Gradient Cap" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="body.background.gradient.tile" description="Body Gradient Tile" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/body_gradient_tile_light.png)"/> <Variable name="content.background.color.selector" description="Content Background Color Selector" type="string" default=".content-inner"/> <Variable name="content.padding" description="Content Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.padding.horizontal" description="Content Horizontal Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.shadow.spread" description="Content Shadow Spread" type="length" default="40px" min="0" max="100px"/> <Variable name="content.shadow.spread.webkit" description="Content Shadow Spread (WebKit)" type="length" default="5px" min="0" max="100px"/> <Variable name="content.shadow.spread.ie" description="Content Shadow Spread (IE)" type="length" default="10px" min="0" max="100px"/> <Variable name="main.border.width" description="Main Border Width" type="length" default="0" min="0" max="10px"/> <Variable name="header.background.gradient" description="Header Gradient" type="url" default="none"/> <Variable name="header.shadow.offset.left" description="Header Shadow Offset Left" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.offset.top" description="Header Shadow Offset Top" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.spread" description="Header Shadow Spread" type="length" default="1px" min="0" max="100px"/> <Variable name="header.padding" description="Header Padding" type="length" default="30px" min="0" max="100px"/> <Variable name="header.border.size" description="Header Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.bottom.border.size" description="Header Bottom Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.border.horizontalsize" description="Header Horizontal Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="description.text.size" description="Description Text Size" type="string" default="140%"/> <Variable name="tabs.margin.top" description="Tabs Margin Top" type="length" default="0" min="0" max="100px"/> <Variable name="tabs.margin.side" description="Tabs Side Margin" type="length" default="30px" min="0" max="100px"/> <Variable name="tabs.background.gradient" description="Tabs Background Gradient" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="tabs.border.width" description="Tabs Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="tabs.bevel.border.width" description="Tabs Bevel Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="post.margin.bottom" description="Post Bottom Margin" type="length" default="25px" min="0" max="100px"/> <Variable name="image.border.small.size" description="Image Border Small Size" type="length" default="2px" min="0" max="10px"/> <Variable name="image.border.large.size" description="Image Border Large Size" type="length" default="5px" min="0" max="10px"/> <Variable name="page.width.selector" description="Page Width Selector" type="string" default=".region-inner"/> <Variable name="page.width" description="Page Width" type="string" default="auto"/> <Variable name="main.section.margin" description="Main Section Margin" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding" description="Main Padding" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding.top" description="Main Padding Top" type="length" default="30px" min="0" max="100px"/> <Variable name="main.padding.bottom" description="Main Padding Bottom" type="length" default="30px" min="0" max="100px"/> <Variable name="paging.background" color="#ffffff" description="Background of blog paging area" type="background" default="transparent none no-repeat scroll top center"/> <Variable name="footer.bevel" description="Bevel border length of footer" type="length" default="0" min="0" max="10px"/> <Variable name="mobile.background.overlay" description="Mobile Background Overlay" type="string" default="transparent none repeat scroll top left"/> <Variable name="mobile.background.size" description="Mobile Background Size" type="string" default="auto"/> <Variable name="mobile.button.color" description="Mobile Button Color" type="color" default="#ffffff" /> <Variable name="startSide" description="Side where text starts in blog language" type="automatic" default="left"/> <Variable name="endSide" description="Side where text ends in blog language" type="automatic" default="right"/> */ /* Content ----------------------------------------------- */ body { font: normal normal 12px Open Sans; color: #000000; background: #eeeeee none repeat scroll top left; padding: 0 0 0 0; } html body .region-inner { min-width: 0; max-width: 100%; width: auto; } h2 { font-size: 22px; } a:link { text-decoration:none; color: #2288bb; } a:visited { text-decoration:none; color: #888888; } a:hover { text-decoration:underline; color: #33aaff; } .body-fauxcolumn-outer .fauxcolumn-inner { background: transparent none repeat scroll top left; _background-image: none; } .body-fauxcolumn-outer .cap-top { position: absolute; z-index: 1; height: 400px; width: 100%; } .body-fauxcolumn-outer .cap-top .cap-left { width: 100%; background: transparent none repeat-x scroll top left; _background-image: none; } .content-outer { -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .15); -goog-ms-box-shadow: 0 0 0 #333333; box-shadow: 0 0 0 rgba(0, 0, 0, .15); margin-bottom: 1px; } .content-inner { padding: 10px 40px; } .content-inner { background-color: #ffffff; } /* Header ----------------------------------------------- */ .header-outer { background: transparent none repeat-x scroll 0 -400px; _background-image: none; } .Header h1 { font: normal normal 40px Open Sans; color: #000000; text-shadow: 0 0 0 rgba(0, 0, 0, .2); } .Header h1 a { color: #000000; } .Header .description { font-size: 18px; color: #000000; } .header-inner .Header .titlewrapper { padding: 22px 0; } .header-inner .Header .descriptionwrapper { padding: 0 0; } /* Tabs ----------------------------------------------- */ .tabs-inner .section:first-child { border-top: 0 solid #dddddd; } .tabs-inner .section:first-child ul { margin-top: -1px; border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; } .tabs-inner .widget ul { background: transparent none repeat-x scroll 0 -800px; _background-image: none; border-bottom: 1px solid #dddddd; margin-top: 0; margin-left: -30px; margin-right: -30px; } .tabs-inner .widget li a { display: inline-block; padding: .6em 1em; font: normal normal 12px Open Sans; color: #000000; border-left: 1px solid #ffffff; border-right: 1px solid #dddddd; } .tabs-inner .widget li:first-child a { border-left: none; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; background-color: #eeeeee; text-decoration: none; } /* Columns ----------------------------------------------- */ .main-outer { border-top: 0 solid transparent; } .fauxcolumn-left-outer .fauxcolumn-inner { border-right: 1px solid transparent; } .fauxcolumn-right-outer .fauxcolumn-inner { border-left: 1px solid transparent; } /* Headings ----------------------------------------------- */ div.widget > h2, div.widget h2.title { margin: 0 0 1em 0; font: normal bold 11px 'Trebuchet MS',Trebuchet,Verdana,sans-serif; color: #000000; } /* Widgets ----------------------------------------------- */ .widget .zippy { color: #999999; text-shadow: 2px 2px 1px rgba(0, 0, 0, .1); } .widget .popular-posts ul { list-style: none; } /* Posts ----------------------------------------------- */ h2.date-header { font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } .date-header span { background-color: #bbbbbb; color: #ffffff; padding: 0.4em; letter-spacing: 3px; margin: inherit; } .main-inner { padding-top: 35px; padding-bottom: 65px; } .main-inner .column-center-inner { padding: 0 0; } .main-inner .column-center-inner .section { margin: 0 1em; } .post { margin: 0 0 45px 0; } h3.post-title, .comments h4 { font: normal normal 22px Open Sans; margin: .75em 0 0; } .post-body { font-size: 110%; line-height: 1.4; position: relative; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 2px; background: #ffffff; border: 1px solid #eeeeee; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body .tr-caption-container { color: #666666; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } .post-header { margin: 0 0 1.5em; line-height: 1.6; font-size: 90%; } .post-footer { margin: 20px -2px 0; padding: 5px 10px; color: #666666; background-color: #eeeeee; border-bottom: 1px solid #eeeeee; line-height: 1.6; font-size: 90%; } #comments .comment-author { padding-top: 1.5em; border-top: 1px solid transparent; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } #comments .avatar-image-container img { border: 1px solid #eeeeee; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #999999; border-bottom: 1px solid #999999; } .comments .comment-thread.inline-thread { background-color: #eeeeee; } .comments .continue { border-top: 2px solid #999999; } /* Accents ---------------------------------------------- */ .section-columns td.columns-cell { border-left: 1px solid transparent; } .blog-pager { background: transparent url(//www.blogblog.com/1kt/simple/paging_dot.png) repeat-x scroll top center; } .blog-pager-older-link, .home-link, .blog-pager-newer-link { background-color: #ffffff; padding: 5px; } .footer-outer { border-top: 1px dashed #bbbbbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } .mobile .body-fauxcolumn-outer .cap-top { background-size: 100% auto; } .mobile .content-outer { -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); box-shadow: 0 0 3px rgba(0, 0, 0, .15); } .mobile .tabs-inner .widget ul { margin-left: 0; margin-right: 0; } .mobile .post { margin: 0; } .mobile .main-inner .column-center-inner .section { margin: 0; } .mobile .date-header span { padding: 0.1em 10px; margin: 0 -10px; } .mobile h3.post-title { margin: 0; } .mobile .blog-pager { background: transparent none no-repeat scroll top center; } .mobile .footer-outer { border-top: none; } .mobile .main-inner, .mobile .footer-inner { background-color: #ffffff; } .mobile-index-contents { color: #000000; } .mobile-link-button { background-color: #2288bb; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #ffffff; } .mobile .tabs-inner .section:first-child { border-top: none; } .mobile .tabs-inner .PageList .widget-content { background-color: #eeeeee; color: #000000; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #dddddd; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 1120px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 1120px; max-width: 1120px; _width: 1120px; } .main-inner .columns { padding-left: 0; padding-right: 310px; } .main-inner .fauxcolumn-center-outer { left: 0; right: 310px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0") - parseInt("310px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0; } .main-inner .fauxcolumn-right-outer { width: 310px; } .main-inner .column-left-outer { width: 0; right: 100%; margin-left: -0; } .main-inner .column-right-outer { width: 310px; margin-right: -310px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } body#layout div.add_widget { padding: 8px; } body#layout div.add_widget a { margin-left: 32px; } --></style> <script type='text/javascript'> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-240546891-1', 'auto', 'blogger'); ga('blogger.send', 'pageview'); </script> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&zx=89189630-7e30-43b5-91d3-8fdab32d43bc' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=4838136820032157985&zx=89189630-7e30-43b5-91d3-8fdab32d43bc' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body class='loading'> <div class='navbar section' id='navbar' name='Navbar'><div class='widget Navbar' data-version='1' id='Navbar1'><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d4838136820032157985\x26blogName\x3dProject+Zero\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dLIGHT\x26layoutType\x3dLAYOUTS\x26searchRoot\x3dhttps://googleprojectzero.blogspot.com/search\x26blogLocale\x3den\x26v\x3d2\x26homepageUrl\x3dhttps://googleprojectzero.blogspot.com/\x26vt\x3d7568236161501195533', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe", messageHandlersFilter: gapi.iframes.CROSS_ORIGIN_IFRAMES_FILTER, messageHandlers: { 'blogger-ping': function() {} } }); } }); </script><script type="text/javascript"> (function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '//pagead2.googlesyndication.com/pagead/js/google_top_exp.js'; var head = document.getElementsByTagName('head')[0]; if (head) { head.appendChild(script); }})(); </script> </div></div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header' name='Header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner'> <div class='titlewrapper'> <h1 class='title'> <a href='https://googleprojectzero.blogspot.com/'> Project Zero </a> </h1> </div> <div class='descriptionwrapper'> <p class='description'><span>News and updates from the Project Zero team at Google</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol' name='Cross-Column'></div> <div class='tabs no-items section' id='crosscol-overflow' name='Cross-Column 2'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main' name='Main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class="date-outer"> <h2 class='date-header'><span>Tuesday, November 22, 2022</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/AVvXsEhHlABpgRGtGMlLmoEY3wDYvg13cwbxPVGScpjHBa0wa8vaohGjhB9YkYuIyfxxnm2iWh4czqp1YUdMCrSgy-dtdlZ8FkLV5IDrQZ1SSCNUoYjsJlHdPoOjtUar_uHQda_aAUu75_4sUUAFjM7Jvr-d6JOMHD7AexIZMXDsdrZIdKX7aA4wrhRC6PCD/s1200/tweet.png' itemprop='image_url'/> <meta content='4838136820032157985' itemprop='blogId'/> <meta content='7519748497805371118' itemprop='postId'/> <a name='7519748497805371118'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html'>Mind the Gap</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-7519748497805371118' itemprop='description articleBody'> <style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=OPeqXG-QxW3ZD8BtmPikfA');.lst-kix_9apzkelodq30-0>li:before{content:"\0025cf "}ul.lst-kix_9apzkelodq30-6{list-style-type:none}ul.lst-kix_9apzkelodq30-7{list-style-type:none}ul.lst-kix_9apzkelodq30-4{list-style-type:none}ul.lst-kix_9apzkelodq30-5{list-style-type:none}ul.lst-kix_9apzkelodq30-2{list-style-type:none}ul.lst-kix_9apzkelodq30-3{list-style-type:none}ul.lst-kix_9apzkelodq30-0{list-style-type:none}.lst-kix_9apzkelodq30-6>li:before{content:"\0025cf "}ul.lst-kix_9apzkelodq30-1{list-style-type:none}.lst-kix_9apzkelodq30-5>li:before{content:"\0025a0 "}.lst-kix_9apzkelodq30-3>li:before{content:"\0025cf "}.lst-kix_9apzkelodq30-7>li:before{content:"\0025cb "}.lst-kix_9apzkelodq30-4>li:before{content:"\0025cb "}.lst-kix_9apzkelodq30-8>li:before{content:"\0025a0 "}ul.lst-kix_9apzkelodq30-8{list-style-type:none}.lst-kix_9apzkelodq30-1>li:before{content:"\0025cb "}.lst-kix_9apzkelodq30-2>li:before{content:"\0025a0 "}ol{margin:0;padding:0}table td,table th{padding:0}.HwIiAvYVxJ-c0{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.HwIiAvYVxJ-c6{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:16pt;font-family:"Arial";font-style:normal}.HwIiAvYVxJ-c7{padding-top:18pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;orphans:2;widows:2;text-align:left}.HwIiAvYVxJ-c9{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial"}.HwIiAvYVxJ-c4{padding-top:0pt;padding-bottom:0pt;line-height:1.5;orphans:2;widows:2;text-align:left}.HwIiAvYVxJ-c3{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.HwIiAvYVxJ-c10{background-color:#ffffff;font-size:10.5pt;font-family:"Roboto";font-weight:400}.HwIiAvYVxJ-c8{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.HwIiAvYVxJ-c1{color:inherit;text-decoration:inherit}.HwIiAvYVxJ-c11{background-color:#ffffff}.HwIiAvYVxJ-c2{font-style:italic}.HwIiAvYVxJ-c5{height:11pt}.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><body class="c8 doc-content"> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0">By Ian Beer, Project Zero </span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c2">Note: The vulnerabilities discussed in this blog post (CVE-2022-33917) are fixed by the upstream vendor, but at the time of publication, t</span><span class="HwIiAvYVxJ-c2 HwIiAvYVxJ-c11">hese fixes have not yet made it downstream to affected Android devices (including Pixel, Samsung, Xiaomi, Oppo and others). Devices with a Mali GPU are currently vulnerable.</span><span class="HwIiAvYVxJ-c9 HwIiAvYVxJ-c2"> </span></p><h2 class="HwIiAvYVxJ-c7" id="h.mvgq8h546lfi"><span class="HwIiAvYVxJ-c6">Introduction</span></h2> <p class="HwIiAvYVxJ-c4"><span>In June 2022, Project Zero researcher Maddie Stone gave a talk at </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://www.first.org/conference/2022/">FirstCon22</a></span><span> titled </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://googleprojectzero.blogspot.com/2022/06/2022-0-day-in-wild-exploitationso-far.html">0-day In-the-Wild Exploitation in 2022…so far</a></span><span>. A key takeaway was that approximately 50% of the observed 0-days in the first half of 2022 were variants of previously patched vulnerabilities. This finding is consistent with our understanding of attacker behavior: </span><span class="HwIiAvYVxJ-c10">attackers will take the path of least resistance, and as long as vendors don't consistently perform thorough root-cause analysis when fixing security vulnerabilities, it will continue to be worth investing time in trying to revive known vulnerabilities before looking for novel ones</span><span class="HwIiAvYVxJ-c0">.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0">The presentation discussed an in the wild exploit targeting the Pixel 6 and leveraging CVE-2021-39793, a vulnerability in the ARM Mali GPU driver used by a large number of other Android devices. ARM's advisory described the vulnerability as: </span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c2">Title</span><span class="HwIiAvYVxJ-c0"> Mali GPU Kernel Driver may elevate CPU RO pages to writable</span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c2">CVE</span><span class="HwIiAvYVxJ-c0"> CVE-2022-22706 (also reported in CVE-2021-39793)</span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c2">Date of issue</span><span class="HwIiAvYVxJ-c0"> 6th January 2022 </span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c2">Impact</span><span> A non-privileged user can get a write access to read-only memory pages [</span><span class="HwIiAvYVxJ-c2">sic</span><span class="HwIiAvYVxJ-c0">].</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span>The week before FirstCon22, Maddie gave an internal preview of her talk. Inspired by the description of an in-the-wild vulnerability in low-level memory management code, fellow Project Zero researcher Jann Horn started auditing the ARM Mali GPU driver. Over the next three weeks, Jann found five more exploitable vulnerabilities (</span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2325">2325</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2327">2327</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2331">2331</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2333">2333</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2334">2334</a></span><span class="HwIiAvYVxJ-c0">).</span></p><h2 class="HwIiAvYVxJ-c7" id="h.9l9fwyug2cxl"><span class="HwIiAvYVxJ-c6">Taking a closer look</span></h2> <p class="HwIiAvYVxJ-c4"><span>One of these issues (</span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2334">2334</a></span><span>) lead to kernel memory corruption, one (</span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2331">2331</a></span><span>) lead to physical memory addresses being disclosed to userspace and the remaining three (</span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2325">2325</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2327">2327</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2333">2333</a></span><span class="HwIiAvYVxJ-c0">) lead to a physical page use-after-free condition. These would enable an attacker to continue to read and write physical pages after they had been returned to the system.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span>For example, by forcing the kernel to </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://googleprojectzero.blogspot.com/2021/10/how-simple-linux-kernel-memory.html">reuse these pages as page tables</a></span><span class="HwIiAvYVxJ-c0">, an attacker with native code execution in an app context could gain full access to the system, bypassing Android's permissions model and allowing broad access to user data.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span>Anecdotally, we heard from multiple sources that the Mali issues we had reported collided with vulnerabilities available in the 0-day market, and we even saw one </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://twitter.com/jgrusko/status/1571921203723440135">public reference</a></span><span class="HwIiAvYVxJ-c0">:</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHlABpgRGtGMlLmoEY3wDYvg13cwbxPVGScpjHBa0wa8vaohGjhB9YkYuIyfxxnm2iWh4czqp1YUdMCrSgy-dtdlZ8FkLV5IDrQZ1SSCNUoYjsJlHdPoOjtUar_uHQda_aAUu75_4sUUAFjM7Jvr-d6JOMHD7AexIZMXDsdrZIdKX7aA4wrhRC6PCD/s1420/tweet.png" style="display: block; padding: 1em 0; text-align: center;"><img alt="@ProjectZeroBugs\nArm Mali: driver exposes physical addresses to unprivileged userspace\n\n @jgrusko Replying to @ProjectZeroBugs\nRIP the feature that was there forever and nobody wanted to report :)" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHlABpgRGtGMlLmoEY3wDYvg13cwbxPVGScpjHBa0wa8vaohGjhB9YkYuIyfxxnm2iWh4czqp1YUdMCrSgy-dtdlZ8FkLV5IDrQZ1SSCNUoYjsJlHdPoOjtUar_uHQda_aAUu75_4sUUAFjM7Jvr-d6JOMHD7AexIZMXDsdrZIdKX7aA4wrhRC6PCD/s1200/tweet.png" title="@ProjectZeroBugs\nArm Mali: driver exposes physical addresses to unprivileged userspace\n\n @jgrusko Replying to @ProjectZeroBugs\nRIP the feature that was there forever and nobody wanted to report :)" width="640/" /></a></span></p><h2 class="HwIiAvYVxJ-c7" id="h.r9ws488aqcv6"><span class="HwIiAvYVxJ-c6">The "Patch gap" is for vendors, too</span></h2> <p class="HwIiAvYVxJ-c4"><span>We reported these five issues to ARM when they were discovered between June and July 2022. ARM fixed the issues promptly in July and August 2022, disclosing them as security issues on their </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://developer.arm.com/Arm%20Security%20Center/Mali%20GPU%20Driver%20Vulnerabilities">Arm Mali Driver Vulnerabilities</a></span><span> page (assigning </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://nvd.nist.gov/vuln/detail/CVE-2022-36449">CVE-2022-36449</a></span><span>) and publishing the </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://developer.arm.com/downloads/-/mali-drivers/valhall-kernel">patched driver source on their public developer website</a></span><span class="HwIiAvYVxJ-c0">.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span>In line with our </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://googleprojectzero.blogspot.com/2021/04/policy-and-disclosure-2021-edition.html">2021 disclosure policy update</a></span><span> we then waited an additional 30 days before derestricting our Project Zero tracker entries. Between late August and mid-September 2022 we derestricted these issues in the public Project Zero tracker: </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2325">2325</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2327">2327</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2331">2331</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2333">2333</a></span><span>, </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://bugs.chromium.org/p/project-zero/issues/detail?id=2334">2334</a></span><span class="HwIiAvYVxJ-c0">.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span>When time permits and as an additional check, we test the effectiveness of the patches that the vendor has provided. This sometimes leads to follow-up bug reports where a patch is incomplete or a variant is discovered (for a recently compiled list of examples, see </span><span class="HwIiAvYVxJ-c3"><a class="HwIiAvYVxJ-c11" href="https://googleprojectzero.blogspot.com/2022/06/2022-0-day-in-wild-exploitationso-far.html">the first table in this blogpost</a></span><span class="HwIiAvYVxJ-c0">), and sometimes we discover the fix isn't there at all. </span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0">In this case we discovered that all of our test devices which used Mali are still vulnerable to these issues. CVE-2022-36449 is not mentioned in any downstream security bulletins.</span></p><h2 class="HwIiAvYVxJ-c7" id="h.yanf95yf2fn7"><span class="HwIiAvYVxJ-c6">Conclusion</span></h2> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0">Just as users are recommended to patch as quickly as they can once a release containing security updates is available, so the same applies to vendors and companies. Minimizing the "patch gap" as a vendor in these scenarios is arguably more important, as end users (or other vendors downstream) are blocking on this action before they can receive the security benefits of the patch.</span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p> <p class="HwIiAvYVxJ-c4"><span class="HwIiAvYVxJ-c0">Companies need to remain vigilant, follow upstream sources closely, and do their best to provide complete patches to users as soon as possible. </span></p> <p class="HwIiAvYVxJ-c4 HwIiAvYVxJ-c5"><span class="HwIiAvYVxJ-c0"></span></p></body> <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'> <meta content='https://www.blogger.com/profile/08975904405228580347' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/08975904405228580347' rel='author' title='author profile'> <span itemprop='name'>Google Project Zero</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2022-11-22T13:05:00-08:00'>1:05 PM</abbr></a> </span> <span class='post-comment-link'> <a class='comment-link' href='https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html#comment-form' onclick=''> No comments: </a> </span> <span class='post-icons'> <span class='item-control blog-admin pid-1053444070'> <a href='https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=7519748497805371118&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=7519748497805371118&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=7519748497805371118&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=7519748497805371118&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=7519748497805371118&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=7519748497805371118&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>Friday, November 4, 2022</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/AVvXsEgnrMtDmG4HptsA4AWfg404rrysHRNHsnGwDE6LY1iWCH2ywFNiQy4qn6yuV9ONlcJ2_YilTV8pd1um42sMKqVhKQliJWco-ZF9Vq0z24fCavXMMcM6jsFLP-JDuw726K7zXOtvC5Cb4K_bWcNUkl3Y2hlWwiIGS0FOjkJNG1oWDSQ7bc9RGm6ZUQXN/s1104/image2.png' itemprop='image_url'/> <meta content='4838136820032157985' itemprop='blogId'/> <meta content='5910791167813767823' itemprop='postId'/> <a name='5910791167813767823'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html'>A Very Powerful Clipboard: Analysis of a Samsung in-the-wild exploit chain</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-5910791167813767823' itemprop='description articleBody'> <style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=lhDjYqiy3mZ0x6ROQEUoUw');ol.lst-kix_gtv3l2c5wia7-3.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-3 0}ol.lst-kix_lsx8wn19rrkf-0.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-0 0}.lst-kix_hbruetvjkelf-8>li:before{content:"\0025a0 "}.lst-kix_hbruetvjkelf-7>li:before{content:"\0025cb "}.lst-kix_gtv3l2c5wia7-3>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-3}.lst-kix_olk2lnn0afau-5>li{counter-increment:lst-ctn-kix_olk2lnn0afau-5}ol.lst-kix_gtv3l2c5wia7-6.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-6 0}.lst-kix_lsx8wn19rrkf-4>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-4}.lst-kix_gtv3l2c5wia7-5>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-5,lower-roman) ". "}.lst-kix_gtv3l2c5wia7-2>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-2}.lst-kix_gtv3l2c5wia7-4>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-4,lower-latin) ". "}.lst-kix_hbruetvjkelf-0>li:before{content:"\0025cf "}.lst-kix_gtv3l2c5wia7-6>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-6,decimal) ". "}.lst-kix_hbruetvjkelf-1>li:before{content:"\0025cb "}.lst-kix_gtv3l2c5wia7-7>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-7,lower-latin) ". "}ol.lst-kix_lsx8wn19rrkf-3.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-3 0}.lst-kix_lsx8wn19rrkf-5>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-5}.lst-kix_gtv3l2c5wia7-8>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-8,lower-roman) ". "}.lst-kix_lsx8wn19rrkf-2>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-2}.lst-kix_hbruetvjkelf-4>li:before{content:"\0025cb "}.lst-kix_hbruetvjkelf-3>li:before{content:"\0025cf "}.lst-kix_hbruetvjkelf-5>li:before{content:"\0025a0 "}.lst-kix_hbruetvjkelf-2>li:before{content:"\0025a0 "}.lst-kix_hbruetvjkelf-6>li:before{content:"\0025cf "}ol.lst-kix_olk2lnn0afau-0.start{counter-reset:lst-ctn-kix_olk2lnn0afau-0 0}ol.lst-kix_olk2lnn0afau-6.start{counter-reset:lst-ctn-kix_olk2lnn0afau-6 0}.lst-kix_olk2lnn0afau-7>li{counter-increment:lst-ctn-kix_olk2lnn0afau-7}ol.lst-kix_lsx8wn19rrkf-6.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-6 0}ol.lst-kix_olk2lnn0afau-3.start{counter-reset:lst-ctn-kix_olk2lnn0afau-3 0}.lst-kix_olk2lnn0afau-4>li{counter-increment:lst-ctn-kix_olk2lnn0afau-4}ol.lst-kix_gtv3l2c5wia7-5{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-6{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-7{list-style-type:none}.lst-kix_olk2lnn0afau-0>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-0,decimal) ". "}ol.lst-kix_gtv3l2c5wia7-8{list-style-type:none}.lst-kix_olk2lnn0afau-1>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-1,lower-latin) ". "}.lst-kix_gtv3l2c5wia7-0>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-0}.lst-kix_olk2lnn0afau-2>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-2,lower-roman) ". "}.lst-kix_olk2lnn0afau-4>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-4,lower-latin) ". "}ol.lst-kix_gtv3l2c5wia7-0{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-1{list-style-type:none}.lst-kix_gtv3l2c5wia7-6>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-6}.lst-kix_olk2lnn0afau-8>li{counter-increment:lst-ctn-kix_olk2lnn0afau-8}ol.lst-kix_gtv3l2c5wia7-2{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-3{list-style-type:none}.lst-kix_olk2lnn0afau-3>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-3,decimal) ". "}ol.lst-kix_gtv3l2c5wia7-4{list-style-type:none}.lst-kix_lsx8wn19rrkf-1>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-1}.lst-kix_lsx8wn19rrkf-7>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-7}ol.lst-kix_lsx8wn19rrkf-5.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-5 0}.lst-kix_olk2lnn0afau-2>li{counter-increment:lst-ctn-kix_olk2lnn0afau-2}ol.lst-kix_olk2lnn0afau-8{list-style-type:none}ol.lst-kix_olk2lnn0afau-7{list-style-type:none}.lst-kix_gtv3l2c5wia7-5>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-5}ol.lst-kix_olk2lnn0afau-6{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-4.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-4 0}.lst-kix_gtv3l2c5wia7-2>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-2,lower-roman) ". "}.lst-kix_gtv3l2c5wia7-1>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-1,lower-latin) ". "}.lst-kix_gtv3l2c5wia7-3>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-3,decimal) ". "}ol.lst-kix_olk2lnn0afau-5.start{counter-reset:lst-ctn-kix_olk2lnn0afau-5 0}.lst-kix_gtv3l2c5wia7-0>li:before{content:"" counter(lst-ctn-kix_gtv3l2c5wia7-0,decimal) ". "}.lst-kix_olk2lnn0afau-6>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-6,decimal) ". "}.lst-kix_olk2lnn0afau-8>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-8,lower-roman) ". "}ol.lst-kix_lsx8wn19rrkf-4.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-4 0}.lst-kix_olk2lnn0afau-5>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-5,lower-roman) ". "}ol.lst-kix_lsx8wn19rrkf-1{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-0{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-3{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-2{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-5{list-style-type:none}.lst-kix_olk2lnn0afau-7>li:before{content:"" counter(lst-ctn-kix_olk2lnn0afau-7,lower-latin) ". "}ol.lst-kix_lsx8wn19rrkf-4{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-7{list-style-type:none}ol.lst-kix_olk2lnn0afau-4.start{counter-reset:lst-ctn-kix_olk2lnn0afau-4 0}ol.lst-kix_lsx8wn19rrkf-6{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-7.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-7 0}.lst-kix_lsx8wn19rrkf-8>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-8,lower-roman) ". "}ol.lst-kix_lsx8wn19rrkf-8{list-style-type:none}.lst-kix_lsx8wn19rrkf-6>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-6,decimal) ". "}.lst-kix_x9fpm1r5kf6m-8>li:before{content:"\0025a0 "}.lst-kix_lsx8wn19rrkf-7>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-7,lower-latin) ". "}.lst-kix_lsx8wn19rrkf-0>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-0,decimal) ". "}.lst-kix_lsx8wn19rrkf-1>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-1,lower-latin) ". "}ol.lst-kix_olk2lnn0afau-7.start{counter-reset:lst-ctn-kix_olk2lnn0afau-7 0}.lst-kix_x9fpm1r5kf6m-6>li:before{content:"\0025cf "}.lst-kix_lsx8wn19rrkf-2>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-2,lower-roman) ". "}.lst-kix_x9fpm1r5kf6m-7>li:before{content:"\0025cb "}.lst-kix_x9fpm1r5kf6m-3>li:before{content:"\0025cf "}.lst-kix_lsx8wn19rrkf-4>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-4,lower-latin) ". "}.lst-kix_lsx8wn19rrkf-5>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-5,lower-roman) ". "}.lst-kix_x9fpm1r5kf6m-4>li:before{content:"\0025cb "}.lst-kix_lsx8wn19rrkf-3>li:before{content:"" counter(lst-ctn-kix_lsx8wn19rrkf-3,decimal) ". "}.lst-kix_x9fpm1r5kf6m-5>li:before{content:"\0025a0 "}ol.lst-kix_olk2lnn0afau-1{list-style-type:none}ol.lst-kix_olk2lnn0afau-0{list-style-type:none}.lst-kix_x9fpm1r5kf6m-0>li:before{content:"\0025cf "}ol.lst-kix_olk2lnn0afau-5{list-style-type:none}ol.lst-kix_olk2lnn0afau-4{list-style-type:none}ol.lst-kix_olk2lnn0afau-3{list-style-type:none}ol.lst-kix_olk2lnn0afau-2{list-style-type:none}.lst-kix_lsx8wn19rrkf-8>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-8}.lst-kix_gtv3l2c5wia7-8>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-8}.lst-kix_x9fpm1r5kf6m-2>li:before{content:"\0025a0 "}ol.lst-kix_gtv3l2c5wia7-2.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-2 0}.lst-kix_x9fpm1r5kf6m-1>li:before{content:"\0025cb "}ul.lst-kix_x9fpm1r5kf6m-3{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-4{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-1{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-2{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-0{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-5.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-5 0}.lst-kix_olk2lnn0afau-1>li{counter-increment:lst-ctn-kix_olk2lnn0afau-1}ul.lst-kix_x9fpm1r5kf6m-7{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-8{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-5{list-style-type:none}ul.lst-kix_x9fpm1r5kf6m-6{list-style-type:none}ul.lst-kix_hbruetvjkelf-4{list-style-type:none}ul.lst-kix_hbruetvjkelf-3{list-style-type:none}ul.lst-kix_hbruetvjkelf-2{list-style-type:none}ul.lst-kix_hbruetvjkelf-1{list-style-type:none}ul.lst-kix_hbruetvjkelf-0{list-style-type:none}ol.lst-kix_lsx8wn19rrkf-2.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-2 0}ol.lst-kix_gtv3l2c5wia7-8.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-8 0}.lst-kix_lsx8wn19rrkf-0>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-0}ul.lst-kix_hbruetvjkelf-8{list-style-type:none}ul.lst-kix_hbruetvjkelf-7{list-style-type:none}ul.lst-kix_hbruetvjkelf-6{list-style-type:none}ul.lst-kix_hbruetvjkelf-5{list-style-type:none}ol.lst-kix_gtv3l2c5wia7-1.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-1 0}ol.lst-kix_olk2lnn0afau-2.start{counter-reset:lst-ctn-kix_olk2lnn0afau-2 0}.lst-kix_olk2lnn0afau-3>li{counter-increment:lst-ctn-kix_olk2lnn0afau-3}ol.lst-kix_lsx8wn19rrkf-1.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-1 0}.lst-kix_olk2lnn0afau-6>li{counter-increment:lst-ctn-kix_olk2lnn0afau-6}ol.lst-kix_lsx8wn19rrkf-8.start{counter-reset:lst-ctn-kix_lsx8wn19rrkf-8 0}ol.lst-kix_gtv3l2c5wia7-7.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-7 0}li.li-bullet-0:before{margin-left:-18pt;white-space:nowrap;display:inline-block;min-width:18pt}.lst-kix_olk2lnn0afau-0>li{counter-increment:lst-ctn-kix_olk2lnn0afau-0}ol.lst-kix_olk2lnn0afau-1.start{counter-reset:lst-ctn-kix_olk2lnn0afau-1 0}ol.lst-kix_gtv3l2c5wia7-0.start{counter-reset:lst-ctn-kix_gtv3l2c5wia7-0 0}.lst-kix_gtv3l2c5wia7-4>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-4}.lst-kix_gtv3l2c5wia7-1>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-1}.lst-kix_gtv3l2c5wia7-7>li{counter-increment:lst-ctn-kix_gtv3l2c5wia7-7}ol.lst-kix_olk2lnn0afau-8.start{counter-reset:lst-ctn-kix_olk2lnn0afau-8 0}.lst-kix_lsx8wn19rrkf-3>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-3}.lst-kix_lsx8wn19rrkf-6>li{counter-increment:lst-ctn-kix_lsx8wn19rrkf-6}ol{margin:0;padding:0}table td,table th{padding:0}.vdnyYxSRCi-c48{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#e0e0e0;border-top-width:1pt;border-right-width:1pt;border-left-color:#e0e0e0;vertical-align:top;border-right-color:#e0e0e0;border-left-width:1pt;border-top-style:solid;background-color:#fafafa;border-left-style:solid;border-bottom-width:1pt;width:507.8pt;border-top-color:#e0e0e0;border-bottom-style:solid}.vdnyYxSRCi-c41{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#e0e0e0;border-top-width:1pt;border-right-width:1pt;border-left-color:#e0e0e0;vertical-align:top;border-right-color:#e0e0e0;border-left-width:1pt;border-top-style:solid;background-color:#fafafa;border-left-style:solid;border-bottom-width:1pt;width:444.8pt;border-top-color:#e0e0e0;border-bottom-style:solid}.vdnyYxSRCi-c10{border-right-style:solid;padding:5pt 5pt 5pt 5pt;border-bottom-color:#e0e0e0;border-top-width:1pt;border-right-width:1pt;border-left-color:#e0e0e0;vertical-align:top;border-right-color:#e0e0e0;border-left-width:1pt;border-top-style:solid;background-color:#fafafa;border-left-style:solid;border-bottom-width:1pt;width:468pt;border-top-color:#e0e0e0;border-bottom-style:solid}.vdnyYxSRCi-c49{-webkit-text-decoration-skip:none;color:#000000;font-weight:400;text-decoration:line-through;vertical-align:baseline;text-decoration-skip-ink:none;font-family:"Arial";font-style:normal}.vdnyYxSRCi-c16{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:16pt;font-family:"Arial";font-style:normal}.vdnyYxSRCi-c32{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10pt;font-family:"Arial";font-style:normal}.vdnyYxSRCi-c3{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10pt;font-family:Consolas,"Courier New";font-style:normal}.vdnyYxSRCi-c31{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:20pt;font-family:"Arial";font-style:normal}.vdnyYxSRCi-c7{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.vdnyYxSRCi-c35{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial"}.vdnyYxSRCi-c17{padding-top:18pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;text-align:left}.vdnyYxSRCi-c52{padding-top:16pt;padding-bottom:4pt;line-height:1.5;page-break-after:avoid;text-align:left}.vdnyYxSRCi-c28{padding-top:20pt;padding-bottom:6pt;line-height:1.5;page-break-after:avoid;text-align:left}.vdnyYxSRCi-c0{font-size:10pt;font-family:Consolas,"Courier New";color:#616161;font-weight:400}.vdnyYxSRCi-c30{padding-top:0pt;padding-bottom:0pt;line-height:1.38;text-align:left}.vdnyYxSRCi-c5{font-size:10pt;font-family:Consolas,"Courier New";color:#9c27b0;font-weight:400}.vdnyYxSRCi-c47{margin-left:0.8pt;border-spacing:0;border-collapse:collapse;margin-right:auto}.vdnyYxSRCi-c13{font-size:10pt;font-family:Consolas,"Courier New";color:#c53929;font-weight:400}.vdnyYxSRCi-c20{font-size:10pt;font-family:Consolas,"Courier New";color:#455a64;font-weight:400}.vdnyYxSRCi-c24{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;color:#1155cc;text-decoration:underline}.vdnyYxSRCi-c4{font-size:10pt;font-family:Consolas,"Courier New";color:#000000;font-weight:400}.vdnyYxSRCi-c1{padding-top:0pt;padding-bottom:0pt;line-height:1.5;text-align:left}.vdnyYxSRCi-c12{font-size:10pt;font-family:Consolas,"Courier New";color:#3367d6;font-weight:400}.vdnyYxSRCi-c43{color:#222222;font-weight:400;font-size:11pt;font-family:"Arial"}.vdnyYxSRCi-c23{border-spacing:0;border-collapse:collapse;margin-right:auto}.vdnyYxSRCi-c34{color:#000000;font-weight:400;font-size:11pt;font-family:Consolas,"Courier New"}.vdnyYxSRCi-c18{font-size:10pt;font-family:Consolas,"Courier New";color:#0f9d58;font-weight:400}.vdnyYxSRCi-c25{font-size:10pt;font-family:Consolas,"Courier New";color:#00796b;font-weight:400}.vdnyYxSRCi-c42{color:#434343;font-weight:400;font-size:14pt;font-family:"Arial"}.vdnyYxSRCi-c2{font-family:Consolas,"Courier New";color:#0d904f;font-weight:400}.vdnyYxSRCi-c27{-webkit-text-decoration-skip:none;text-decoration:underline;text-decoration-skip-ink:none}.vdnyYxSRCi-c33{text-decoration:none;vertical-align:baseline;font-style:normal}.vdnyYxSRCi-c37{background-color:#ffffff;max-width:468pt;padding:72pt 72pt 72pt 72pt}.vdnyYxSRCi-c51{text-decoration-skip-ink:none;-webkit-text-decoration-skip:none;text-decoration:line-through}.vdnyYxSRCi-c21{margin-left:36pt;padding-left:0pt}.vdnyYxSRCi-c6{color:inherit;text-decoration:inherit}.vdnyYxSRCi-c46{font-weight:400;font-family:Consolas,"Courier New"}.vdnyYxSRCi-c36{padding:0;margin:0}.vdnyYxSRCi-c8{orphans:2;widows:2}.vdnyYxSRCi-c26{margin-left:36pt;text-indent:36pt}.vdnyYxSRCi-c45{text-decoration:none;vertical-align:baseline}.vdnyYxSRCi-c9{background-color:#ffff00}.vdnyYxSRCi-c15{background-color:#b6d7a8}.vdnyYxSRCi-c29{background-color:#f4cccc}.vdnyYxSRCi-c11{font-style:italic}.vdnyYxSRCi-c19{height:0pt}.vdnyYxSRCi-c14{height:11pt}.vdnyYxSRCi-c44{font-size:11pt}.vdnyYxSRCi-c22{height:52.5pt}.vdnyYxSRCi-c40{font-weight:700}.vdnyYxSRCi-c39{margin-left:36pt}.vdnyYxSRCi-c38{background-color:#ffffff}.vdnyYxSRCi-c50{color:#222222}.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="c37 doc-content"> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c35 vdnyYxSRCi-c11">Posted by Maddie Stone, Project Zero</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c35 vdnyYxSRCi-c11"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c11 vdnyYxSRCi-c40">Note</span><span class="vdnyYxSRCi-c35 vdnyYxSRCi-c11">: The three vulnerabilities discussed in this blog were all fixed in Samsung’s March 2021 release. They were fixed as CVE-2021-25337, CVE-2021-25369, CVE-2021-25370. To ensure your Samsung device is up-to-date under settings you can check that your device is running SMR Mar-2021 or later.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>As defenders, in-the-wild exploit samples give us important insight into what attackers are really doing. We get the “ground truth” data about the vulnerabilities and exploit techniques they’re using, which then informs our further research and guidance to security teams on what could have the biggest impact or return on investment. To do this, we need to know that the vulnerabilities and exploit samples were found in-the-wild. </span><span>Over the past few years there’s been tremendous progress in vendor’s transparently disclosing when a vulnerability is known to be exploited in-the-wild: </span><span>Adobe, Android, Apple, ARM, Chrome, </span><span>Microsoft, </span><span>Mozilla, and others are sharing this information via their security release note</span><span class="vdnyYxSRCi-c7">s.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>While</span><span class="vdnyYxSRCi-c7"> we understand that Samsung has yet to annotate any vulnerabilities as in-the-wild, going forward, Samsung has committed to publicly sharing when vulnerabilities may be under limited, targeted exploitation, as part of their release notes. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>We hope that, like Samsung, others will join their industry peers in disclosing when there is evidence to suggest that a vulnerability is being exploited in-the-wild in one of their products. </span></p><h1 class="vdnyYxSRCi-c28 vdnyYxSRCi-c8" id="h.a4kixp1dzrny"><span>The exploit sample</span></h1> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The Google Threat Analysis Group (TAG) obtained a partial exploit chain for Samsung devices that TAG believes belonged to a commercial surveillance vendor. These exploits were likely discovered in the testing phase. </span><span class="vdnyYxSRCi-c7">The sample is from late 2020. The chain merited further analysis because it is a 3 vulnerability chain where all 3 vulnerabilities are within Samsung custom components, including a vulnerability in a Java component. This exploit analysis was completed in collaboration with Clement Lecigne from TAG.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The sample used three vulnerabilities, all patched in March 2021 by Samsung: </span></p><ol class="c36 lst-kix_lsx8wn19rrkf-0 start" start="1"><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span class="vdnyYxSRCi-c7">Arbitrary file read/write via the clipboard provider - CVE-2021-25337</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Kernel information leak via </span><span class="vdnyYxSRCi-c2">sec_log</span><span class="vdnyYxSRCi-c7"> - CVE-2021-25369</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span class="vdnyYxSRCi-c7">Use-after-free in the Display Processing Unit (DPU) driver - CVE-2021-25370</span></li></ol> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">The exploit sample targets Samsung phones running kernel 4.14.113 with the Exynos SOC. Samsung phones run one of two types of SOCs depending on where they’re sold. For example the Samsung phones sold in the United States, China, and a few other countries use a Qualcomm SOC and phones sold most other places (ex. Europe and Africa) run an Exynos SOC. The exploit sample relies on both the Mali GPU driver and the DPU driver which are specific to the Exynos Samsung phones.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">Examples of Samsung phones that were running kernel 4.14.113 in late 2020 (when this sample was found) include the S10, A50, and A51.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The in-the-wild sample that was obtained is a JNI native library file that would have been loaded as a part of an app. Unfortunately TAG did not obtain the app that would have been used with this library. Getting initial code execution via an application is a path that we’ve seen in other campaigns this year. </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://blog.google/threat-analysis-group/italian-spyware-vendor-targets-users-in-italy-and-kazakhstan/">TAG</a></span><span> and </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://googleprojectzero.blogspot.com/2022/06/curious-case-carrier-app.html">Project Zero</a></span><span class="vdnyYxSRCi-c7"> published detailed analyses of one of these campaigns in June. </span></p><h1 class="vdnyYxSRCi-c28 vdnyYxSRCi-c8" id="h.e0me6pgei4ix"><span class="vdnyYxSRCi-c31">Vulnerability #1 - Arbitrary filesystem read and write</span></h1> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit chain used CVE-2021-25337 for an initial arbitrary file read and write. The exploit is running as the </span><span class="vdnyYxSRCi-c2">untrusted_app</span><span> SELinux context, but uses the </span><span class="vdnyYxSRCi-c2">system_server</span><span class="vdnyYxSRCi-c7"> SELinux context to open files that it usually wouldn’t be able to access. This bug was due to a lack of access control in a custom Samsung clipboard provider that runs as the system user. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnrMtDmG4HptsA4AWfg404rrysHRNHsnGwDE6LY1iWCH2ywFNiQy4qn6yuV9ONlcJ2_YilTV8pd1um42sMKqVhKQliJWco-ZF9Vq0z24fCavXMMcM6jsFLP-JDuw726K7zXOtvC5Cb4K_bWcNUkl3Y2hlWwiIGS0FOjkJNG1oWDSQ7bc9RGm6ZUQXN/s1104/image2.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Screenshot of the CVE-2021-25337 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19527 (CVE-2021-25337): Arbitrary file read/write vulnerability via unprotected clipboard content provider  Severity: Moderate Affected versions: P(9.0), Q(10.0), R(11.0) devices except ONEUI 3.1 in R(11.0) Reported on: November 3, 2020 Disclosure status: Privately disclosed. An improper access control in clipboard service prior to SMR MAR-2021 Release 1 allows untrusted applications to read or write arbitrary files in the device. The patch adds the proper caller check to prevent improper access to clipboard service." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnrMtDmG4HptsA4AWfg404rrysHRNHsnGwDE6LY1iWCH2ywFNiQy4qn6yuV9ONlcJ2_YilTV8pd1um42sMKqVhKQliJWco-ZF9Vq0z24fCavXMMcM6jsFLP-JDuw726K7zXOtvC5Cb4K_bWcNUkl3Y2hlWwiIGS0FOjkJNG1oWDSQ7bc9RGm6ZUQXN/s1104/image2.png" style="max-height: 750px; max-width: 600px;" title="Screenshot of the CVE-2021-25337 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19527 (CVE-2021-25337): Arbitrary file read/write vulnerability via unprotected clipboard content provider  Severity: Moderate Affected versions: P(9.0), Q(10.0), R(11.0) devices except ONEUI 3.1 in R(11.0) Reported on: November 3, 2020 Disclosure status: Privately disclosed. An improper access control in clipboard service prior to SMR MAR-2021 Release 1 allows untrusted applications to read or write arbitrary files in the device. The patch adds the proper caller check to prevent improper access to clipboard service." /></a></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.6kvrbnoz6uxg"><span class="vdnyYxSRCi-c16">About Android content providers</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>In Android, </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/guide/topics/providers/content-providers">Content Providers</a></span><span> manage the storage and system-wide access of different data. Content providers organize their data as tables with columns representing the type of data collected and the rows representing each piece of data. Content providers are </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/guide/topics/providers/content-provider-creating#ContentProvider">required to implement six abstract methods</a></span><span>: </span><span class="vdnyYxSRCi-c2">query</span><span>, </span><span class="vdnyYxSRCi-c2">insert</span><span>,</span><span class="vdnyYxSRCi-c2"> update</span><span>, </span><span class="vdnyYxSRCi-c2">delete</span><span>, </span><span class="vdnyYxSRCi-c2">getType</span><span>, and </span><span class="vdnyYxSRCi-c2">onCreate</span><span>. All of these methods besides </span><span class="vdnyYxSRCi-c2">onCreate</span><span class="vdnyYxSRCi-c7"> are called by a client application.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>According to the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/guide/topics/providers/content-provider-creating#Permissions">Android documentation</a></span><span class="vdnyYxSRCi-c7">:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c26"><span><br></span><span class="vdnyYxSRCi-c35 vdnyYxSRCi-c11">All applications can read from or write to your provider, even if the underlying data is private, because by default your provider does not have permissions set. To change this, set permissions for your provider in your manifest file, using attributes or child elements of the <provider> element. You can set permissions that apply to the entire provider, or to certain tables, or even to certain records, or all three.</span></p><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.q6l9tyie3537"><span class="vdnyYxSRCi-c16">The vulnerability</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Samsung created a custom clipboard content provider that runs within the system server. The </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/java/com/android/server/SystemServer.java">system server</a></span><span> is a very privileged process on Android that manages many of the services critical to the functioning of the device, such as the WifiService and TimeZoneDetectorService. The system server runs as the privileged </span><span class="vdnyYxSRCi-c2">system</span><span> user (UID 1000, </span><span class="vdnyYxSRCi-c2">AID_system</span><span>) and under the </span><span class="vdnyYxSRCi-c2">system_server</span><span class="vdnyYxSRCi-c7"> SELinux context.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Samsung added a custom clipboard content provider to the system server. This custom clipboard provider is specifically for images. In the </span><span class="vdnyYxSRCi-c2">com.android.server.semclipboard.SemClipboardProvider</span><span class="vdnyYxSRCi-c7"> class, there are the following variables:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span><br></span><span class="vdnyYxSRCi-c33 vdnyYxSRCi-c34"> DATABASE_NAME = ‘clipboardimage.db’</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c34 vdnyYxSRCi-c33"> TABLE_NAME = ‘ClipboardImageTable’</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c34 vdnyYxSRCi-c33"> URL = ‘content://com.sec.android.semclipboardprovider/images’</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c34 vdnyYxSRCi-c33"> CREATE_TABLE = " CREATE TABLE ClipboardImageTable (id INTEGER PRIMARY KEY AUTOINCREMENT, _data TEXT NOT NULL);";</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c34 vdnyYxSRCi-c33"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c51">Unlike content providers that live in “normal” apps and can restrict access via permissions in their manifest as explained above, content providers in the system server are responsible for restricting access in their own code.</span><span class="vdnyYxSRCi-c44 vdnyYxSRCi-c49"> The system server is a single JAR (services.jar) on the firmware image and doesn’t have a manifest for any permissions to go in. Therefore it’s up to the code within the system server to do its own access checking. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c11">UPDATE 10 Nov 2022: The system server code is not an app in its own right. Instead its code lives in a JAR, </span><span class="vdnyYxSRCi-c2">services.jar</span><span class="vdnyYxSRCi-c11">. Its manifest is found in </span><span class="vdnyYxSRCi-c2">/system/framework/framework-res.apk</span><span class="vdnyYxSRCi-c11">. In this case, the entry for the </span><span class="vdnyYxSRCi-c2">SemClipboardProvider</span><span class="vdnyYxSRCi-c11 vdnyYxSRCi-c35"> in the manifest is:</span></p> <p class="c30 c8 c14 c38"><span class="vdnyYxSRCi-c33 vdnyYxSRCi-c43"></span></p><a id="t.4abf9747174430e2619adf352acba88a7de57bde"></a><a id="t.0"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c22"><td class="vdnyYxSRCi-c41" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c8 vdnyYxSRCi-c30"><span class="vdnyYxSRCi-c25"><provider</span><span class="vdnyYxSRCi-c4"> android:name</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"com.android.server.semclipboard.SemClipboardProvider" android:enabled</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"true" android:exported</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"true" android:multiprocess</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"false" android:authorities</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"com.sec.android.semclipboardprovider" android:singleUser</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">"true"</span><span class="vdnyYxSRCi-c25 vdnyYxSRCi-c33">/></span></p></td></tr></table> <p class="c1 c8 c14 c38"><span class="vdnyYxSRCi-c32"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c38"><span class="vdnyYxSRCi-c11 vdnyYxSRCi-c50">Like “normal” app-defined components, the system server could use the </span><span class="vdnyYxSRCi-c2">android:permission</span><span class="vdnyYxSRCi-c50"> </span><span class="vdnyYxSRCi-c43 vdnyYxSRCi-c11 vdnyYxSRCi-c45">attribute to control access to the provider, but it does not. Since there is not a permission required to access the SemClipboardProvider via the manifest, any access control must come from the provider code itself. Thanks to Edward Cunningham for pointing this out!</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c2">ClipboardImageTable</span><span> defines only two columns for the table as seen above: </span><span class="vdnyYxSRCi-c2">id</span><span> and </span><span class="vdnyYxSRCi-c2">_data</span><span>. </span><span>The column name </span><span class="vdnyYxSRCi-c2">_data</span><span> has a special use in Android content providers.</span><span> It can be used with the </span><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c27"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/android/content/ContentProvider#openFileHelper(android.net.Uri,%20java.lang.String)">openFileHelper</a></span><span> method to open a file at a specified path. Only the URI of the row in the table is passed to </span><span class="vdnyYxSRCi-c2">openFileHelper</span><span> and a </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/android/os/ParcelFileDescriptor">ParcelFileDescriptor</a></span><span> object for the path stored in that row is returned. The </span><span class="vdnyYxSRCi-c2">ParcelFileDescriptor</span><span> class then provides the </span><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c27"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/android/os/ParcelFileDescriptor#getFd()">getFd</a></span><span> method to get the native file descriptor (fd) for the returned </span><span class="vdnyYxSRCi-c2">ParcelFileDescriptor</span><span class="vdnyYxSRCi-c7">. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.e18ec8258d62d1bc4c9a78a85fafcf74e0c9ab36"></a><a id="t.1"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">public</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> uri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">ContentValues</span><span class="vdnyYxSRCi-c4"> values</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> row </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">this</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">database</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">TABLE_NAME</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">""</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> values</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">row </span><span class="vdnyYxSRCi-c0">></span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> newUri </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">ContentUris</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">withAppendedId</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">CONTENT_URI</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> row</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> getContext</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">getContentResolver</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">notifyChange</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">newUri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">null</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> newUri</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">throw</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">new</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">SQLException</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"Fail to add a new record into "</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">+</span><span class="vdnyYxSRCi-c4"> uri</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The function above is the vulnerable </span><span class="vdnyYxSRCi-c2">insert()</span><span> method in </span><span class="vdnyYxSRCi-c2">com.android.server.semclipboard.SemClipboardProvider</span><span>. </span><span>There is no access control included in this function</span><span> so any app, including the </span><span class="vdnyYxSRCi-c2">untrusted_app</span><span> SELinux context, can modify the </span><span class="vdnyYxSRCi-c2">_data</span><span> column directly</span><span>.</span><span> By calling </span><span class="vdnyYxSRCi-c2">insert</span><span class="vdnyYxSRCi-c7">, an app can open files via the system server that it wouldn’t usually be able to open on its own.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">The exploit triggered the vulnerability with the following code from an untrusted application on the device. This code returned a raw file descriptor.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.c76d3f2b81f0b5658486a0f765013fc550b7979d"></a><a id="t.2"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c12">ContentValues</span><span class="vdnyYxSRCi-c4"> vals </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">new</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">ContentValues</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">vals</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">put</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"_data"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"/data/system/users/0/newFile.bin"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">URI semclipboard_uri </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> URI</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">parse</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"content://com.sec.android.semclipboardprovider"</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c12">ContentResolver</span><span class="vdnyYxSRCi-c4"> resolver </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> getContentResolver</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">URI newFile_uri </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> resolver</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">semclipboard_uri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> vals</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> resolver</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">openFileDescriptor</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">newFile_uri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"w"</span><span class="vdnyYxSRCi-c0">).</span><span class="vdnyYxSRCi-c4">getFd</span><span class="vdnyYxSRCi-c0">();</span><span class="vdnyYxSRCi-c4"> </span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">Let’s walk through what is happening line by line:</span></p><ol class="c36 lst-kix_olk2lnn0afau-0 start" start="1"><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Create a </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/android/content/ContentValues">ContentValues</a></span><span class="vdnyYxSRCi-c7"> object. This holds the key, value pair that the caller wants to insert into a provider’s database table. The key is the column name and the value is the row entry.</span></li><li style="margin-left: 46pt;" class="c1 c8 c21 li-bullet-0"><span class="vdnyYxSRCi-c7">Set the ContentValues object: the key is set to “_data” and the value to an arbitrary file path, controlled by the exploit.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Get the URI to access the </span><span class="vdnyYxSRCi-c2">semclipboardprovider</span><span>. This is set in the </span><span class="vdnyYxSRCi-c2">SemClipboardProvider</span><span class="vdnyYxSRCi-c7"> class.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Get the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/android/content/ContentResolver">ContentResolver</a></span><span class="vdnyYxSRCi-c7"> object that allows apps access to ContentProviders.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Call </span><span class="vdnyYxSRCi-c2">insert</span><span> on the </span><span class="vdnyYxSRCi-c2">semclipboardprovider</span><span class="vdnyYxSRCi-c7"> with our key-value pair.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Open the file that was passed in as the value and return the raw file descriptor. </span><span class="vdnyYxSRCi-c2">openFileDescriptor</span><span> calls the content provider’s </span><span class="vdnyYxSRCi-c2">openFile</span><span>, which in this case simply calls </span><span class="vdnyYxSRCi-c2">openFileHelper</span><span class="vdnyYxSRCi-c7">.</span></li></ol> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Th</span><span>e</span><span> exploit wrote their next stage binary to the directory </span><span class="vdnyYxSRCi-c2">/data/system/users/0/</span><span>. The dropped file will have an SELinux context of </span><span class="vdnyYxSRCi-c2">users_system_data_file</span><span>. Normal </span><span class="vdnyYxSRCi-c2">untrusted_app</span><span>’s don’t have access to open or create </span><span class="vdnyYxSRCi-c2">users_system_data_file</span><span> files so in this case they are proxying the open through </span><span class="vdnyYxSRCi-c2">system_server</span><span> who can open </span><span class="vdnyYxSRCi-c2">users_system_data_file</span><span>. While </span><span class="vdnyYxSRCi-c2">untrusted_app</span><span> can’t open </span><span class="vdnyYxSRCi-c2">users_system_data_file</span><span>, it can read and write to </span><span class="vdnyYxSRCi-c2">users_system_data_file</span><span class="vdnyYxSRCi-c7">. Once the clipboard content provider opens the file and passess the fd to the calling process, the calling process can now read and write to it.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">The exploit first uses this fd to write their next stage ELF file onto the file system. The contents for the stage 2 ELF were embedded within the original sample.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">This vulnerability is triggered three more times throughout the chain as we’ll see below.</span></p><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.a85my9321daw"><span class="vdnyYxSRCi-c16">Fixing the vulnerability</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To fix the vulnerability, Samsung added access checks to the functions in the </span><span class="vdnyYxSRCi-c2">SemClipboardProvider</span><span>. </span><span>The </span><span class="vdnyYxSRCi-c2">insert</span><span> method now checks if the PID of the calling process is UID 1000, meaning that it is already also running with system privileges.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b1677b3cf45fa7aae1529da0f69fbe999785be5f"></a><a id="t.3"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">public</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> uri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">ContentValues</span><span class="vdnyYxSRCi-c4"> values</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">if</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c12 vdnyYxSRCi-c9">Binder</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">getCallingUid</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">()</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">!=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c13 vdnyYxSRCi-c9">1000</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">)</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">Log</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">e</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">TAG</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"Fail to insert image clip uri. blocked the access of package : "</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">+</span><span class="vdnyYxSRCi-c4"> getContext</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">getPackageManager</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">getNameForUid</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c12">Binder</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">getCallingUid</span><span class="vdnyYxSRCi-c0">()));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">null</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> row </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">this</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">database</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">TABLE_NAME</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">""</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> values</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">row </span><span class="vdnyYxSRCi-c0">></span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">Uri</span><span class="vdnyYxSRCi-c4"> newUri </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">ContentUris</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">withAppendedId</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">CONTENT_URI</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> row</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> getContext</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">getContentResolver</span><span class="vdnyYxSRCi-c0">().</span><span class="vdnyYxSRCi-c4">notifyChange</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">newUri</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">null</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> newUri</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">throw</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">new</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">SQLException</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"Fail to add a new record into "</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">+</span><span class="vdnyYxSRCi-c4"> uri</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.qqt6xm6hc0pl"><span class="vdnyYxSRCi-c16">Executing the stage 2 ELF</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit has now written its stage 2 binary to the file system, but how do they load </span><span>it</span><span class="vdnyYxSRCi-c7"> outside of their current app sandbox? Using the Samsung Text to Speech application (SamsungTTS.apk).</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://galaxystore.samsung.com/detail/com.samsung.SMT?langCd=en">Samsung Text to Speech application (com.samsung.SMT)</a></span><span> is a pre-installed system app running on Samsung devices. It is also running as the system UID, though as a slightly less privileged SELinux context, </span><span class="vdnyYxSRCi-c2">system_app</span><span> rather than </span><span class="vdnyYxSRCi-c2">system_server</span><span>. There has been at least one previously public vulnerability where this app was used</span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://blog.flanker017.me/text-to-speech-speaks-pwned/"> to gain code execution as system</a></span><span class="vdnyYxSRCi-c7">. What’s different this time though is that the exploit doesn’t need another vulnerability; instead it reuses the stage 1 vulnerability in the clipboard to arbitrarily write files on the file system.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Older versions of the SamsungTTS application stored the file path for their engine in their Settings files. When a service in the application was started, it obtained the path from the Settings file and would load that file path as a native library using the </span><span class="vdnyYxSRCi-c24 vdnyYxSRCi-c46"><a class="vdnyYxSRCi-c61" href="https://developer.android.com/reference/java/lang/System#load(java.lang.String)">System.load</a></span><span class="vdnyYxSRCi-c7"> API. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit takes advantage of this by using the stage 1 vulnerability to write its file path to the Settings file and then starting the service which will then load its stage 2 executable file as system UID and </span><span class="vdnyYxSRCi-c2">system_app</span><span class="vdnyYxSRCi-c7"> SELinux context.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To do this, the exploit uses the stage 1 vulnerability to write the following contents to two different files: </span><span class="vdnyYxSRCi-c2">/data/user_de/0/com.samsung.SMT/shared_prefs/SamsungTTSSettings.xml</span><span> and </span><span class="vdnyYxSRCi-c2">/data/data/com.samsung.SMT/shared_prefs/SamsungTTSSettings.xml</span><span class="vdnyYxSRCi-c7">. Depending on the version of the phone and application, the SamsungTTS app uses these 2 different paths for its Settings files.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.1e9fc9b7503d012c58ac54bface058bfa0e1cf3d"></a><a id="t.4"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0"><?</span><span class="vdnyYxSRCi-c4">xml version</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c18">'1.0'</span><span class="vdnyYxSRCi-c4"> encoding</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c18">'utf-8'</span><span class="vdnyYxSRCi-c4"> standalone</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c18">'yes'</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">?></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c25"><map></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c25"><string</span><span class="vdnyYxSRCi-c4"> name</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">\"eng-USA-Variant Info\"</span><span class="vdnyYxSRCi-c25">></span><span class="vdnyYxSRCi-c4">f00</span><span class="vdnyYxSRCi-c25"></string></span><span class="vdnyYxSRCi-c3">\n"</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c25"><string</span><span class="vdnyYxSRCi-c4"> name</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">\"SMT_STUBCHECK_STATUS\"</span><span class="vdnyYxSRCi-c25">></span><span class="vdnyYxSRCi-c4">STUB_SUCCESS</span><span class="vdnyYxSRCi-c25"></string></span><span class="vdnyYxSRCi-c3">\n"</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c25"><string</span><span class="vdnyYxSRCi-c4"> name</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4">\"SMT_LATEST_INSTALLED_ENGINE_PATH\"</span><span class="vdnyYxSRCi-c25">></span><span class="vdnyYxSRCi-c4">/data/system/users/0/newFile.bin</span><span class="vdnyYxSRCi-c25"></string></span><span class="vdnyYxSRCi-c3">\n"</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c25"></map></span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c2">SMT_LATEST_INSTALLED_ENGINE_PATH</span><span> is the file path passed to </span><span class="vdnyYxSRCi-c2">System.load()</span><span>. To initiate the process of the system loading, the exploit stops and restarts the </span><span class="vdnyYxSRCi-c2">SamsungTTSService</span><span> by sending two intents to the application. The </span><span class="vdnyYxSRCi-c2">SamsungTTSService</span><span> then initiates the load and the stage 2 ELF begins executing as the system user in the </span><span class="vdnyYxSRCi-c2">system_app</span><span class="vdnyYxSRCi-c7"> SELinux context. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">The exploit sample is from at least November 2020. As of November 2020, some devices had a version of the SamsungTTS app that did this arbitrary file loading while others did not. App versions 3.0.04.14 and before included the arbitrary loading capability. It seems like devices released on Android 10 (Q) were released with the updated version of the SamsungTTS app which did not load an ELF file based on the path in the settings file. For example, the A51 device that launched in late 2019 on Android 10 launched with version 3.0.08.18 of the SamsungTTS app, which does not include the functionality that would load the ELF.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Phones released on Android P and earlier seemed to have a version of the app pre-3.0.08.18 which does load the executable up through December 2020. For example, the SamsungTTS app from </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://www.sammobile.com/samsung/galaxy-a50/firmware/SM-A505F/XID/download/A505FDDS5BTJA/517463/">this A50 device</a></span><span class="vdnyYxSRCi-c7"> on the November 2020 security patch level was 3.0.03.22, which did load from the Settings file. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Once the ELF file is loaded via the </span><span class="vdnyYxSRCi-c2">System.load</span><span class="vdnyYxSRCi-c7"> api, it begins executing. It includes two additional exploits to gain kernel read and write privileges as the root user.</span></p><h1 class="vdnyYxSRCi-c28 vdnyYxSRCi-c8" id="h.4iueiufi5tqe"><span class="vdnyYxSRCi-c31">Vulnerability #2 - task_struct and sys_call_table address leak</span></h1> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Once the second stage ELF is running (and as system), the exploit then continues. The second vulnerability (CVE-2021-25369) used by the chain is an information leak to leak the address of the </span><span class="vdnyYxSRCi-c2">task_struct</span><span> and </span><span class="vdnyYxSRCi-c2">sy</span><span class="vdnyYxSRCi-c2">s_call_tab</span><span class="vdnyYxSRCi-c2">le</span><span>. The leaked </span><span class="vdnyYxSRCi-c2">sys_call_table</span><span> address is used to defeat KASLR. The </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> pointer, which is used later to gain arbitrary kernel read and write, is calculated from the leaked </span><span class="vdnyYxSRCi-c2">task_struct</span><span class="vdnyYxSRCi-c7"> address.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The vulnerability is in the access permissions of a custom Samsung logging file: </span><span class="vdnyYxSRCi-c2">/data/log/sec_log.log</span><span class="vdnyYxSRCi-c7">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTUlyiPJg1awjnkx_0jTgZw-hLYtjWLrtD4kRaCW0J6sj9FGrAbEgC_nDgM36G5ctdm9r1iHukN2Wt7YivoW1znRECs_cNdoISTW_mzkF0Ylh48-zsoLsEzlWOJ8iLnSFejIzSYGG5t7lbfNMuNt3v-01FpJcWsYQmtT01kB3AQVV58nSgiCKmjqOa/s1103/image3.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Screenshot of the CVE-2021-25369 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19897 (CVE-2021-25369): Potential kernel information exposure from sec_log  Severity: Moderate Affected versions: O(8.x), P(9.0), Q(10.0) Reported on: December 10, 2020 Disclosure status: Privately disclosed. An improper access control vulnerability in sec_log file prior to SMR MAR-2021 Release 1 exposes sensitive kernel information to userspace. The patch removes vulnerable file." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTUlyiPJg1awjnkx_0jTgZw-hLYtjWLrtD4kRaCW0J6sj9FGrAbEgC_nDgM36G5ctdm9r1iHukN2Wt7YivoW1znRECs_cNdoISTW_mzkF0Ylh48-zsoLsEzlWOJ8iLnSFejIzSYGG5t7lbfNMuNt3v-01FpJcWsYQmtT01kB3AQVV58nSgiCKmjqOa/s1103/image3.png" style="max-height: 750px; max-width: 600px;" title="Screenshot of the CVE-2021-25369 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19897 (CVE-2021-25369): Potential kernel information exposure from sec_log  Severity: Moderate Affected versions: O(8.x), P(9.0), Q(10.0) Reported on: December 10, 2020 Disclosure status: Privately disclosed. An improper access control vulnerability in sec_log file prior to SMR MAR-2021 Release 1 exposes sensitive kernel information to userspace. The patch removes vulnerable file." /></a></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit abused a </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> in order to leak the two kernel addresses and therefore break ASLR</span><span class="vdnyYxSRCi-c2">.</span><span> </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> is intended to only be used in situations where a kernel bug is detected because it prints a full backtrace, including stack trace and register values, to the kernel logging buffer, </span><span class="vdnyYxSRCi-c2">/dev/kmsg</span><span class="vdnyYxSRCi-c7">. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c33 vdnyYxSRCi-c44"></span></p><a id="t.dac6441018d72ea602c04b6dc61a0ad5d92c980f"></a><a id="t.5"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">oid __warn</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">const</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">file</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> line</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c5">caller</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> taint</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> pt_regs </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">regs</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> warn_args </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">args</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> disable_trace_on_warning</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> pr_warn</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"------------[ cut here ]------------\n"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">file</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> pr_warn</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"WARNING: CPU: %d PID: %d at %s:%d %pS\n"</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> raw_smp_processor_id</span><span class="vdnyYxSRCi-c0">(),</span><span class="vdnyYxSRCi-c4"> current</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">pid</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> file</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> line</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">caller</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> pr_warn</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"WARNING: CPU: %d PID: %d at %pS\n"</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> raw_smp_processor_id</span><span class="vdnyYxSRCi-c0">(),</span><span class="vdnyYxSRCi-c4"> current</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">pid</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">caller</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">args</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> vprintk</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">args</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fmt</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> args</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">args</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">panic_on_warn</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/*</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * This thread may hit another WARN() in the panic path.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * Resetting this prevents additional WARN() from panicking the</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * system on this thread. Other threads are blocked by the</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * panic_mutex in panic().</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> panic_on_warn </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> panic</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"panic_on_warn set ...\n"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> print_modules</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dump_stack</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> print_oops_end_marker</span><span class="vdnyYxSRCi-c0">();</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/* Just a warning, don't kill lockdep. */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> add_taint</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">taint</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> LOCKDEP_STILL_OK</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c33 vdnyYxSRCi-c44"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>On Android, the ability to read from </span><span class="vdnyYxSRCi-c2">kmsg</span><span> is scoped to privileged users and contexts. </span><span>While </span><span class="vdnyYxSRCi-c2">kmsg</span><span> is readable by </span><span class="vdnyYxSRCi-c2">system_server</span><span>, it is not readable from the </span><span class="vdnyYxSRCi-c2">system_app</span><span> context, which means it’s not readable by the e</span><span>xploit.</span><span class="vdnyYxSRCi-c7"> </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.1187718de85bb2eac5503bd22ce0d6a9ea90e292"></a><a id="t.6"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c18">/ $ ls -alZ /</span><span class="vdnyYxSRCi-c4">dev</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c3">kmsg</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">crw</span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">rw</span><span class="vdnyYxSRCi-c0">----</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c4"> root system u</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">object_r</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">kmsg_device</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">s0 </span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">11</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">2022</span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c13">10</span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c13">27</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">21</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c13">48</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">dev</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c3">kmsg</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">$ sesearch </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">A </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">s system_server </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">t kmsg_device </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c3">p read precompiled_sepolicy</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow domain dev_type</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">lnk_file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow system_server kmsg_device</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">chr_file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read write </span><span class="vdnyYxSRCi-c0">};</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Samsung however has added a custom logging feature that copies </span><span class="vdnyYxSRCi-c2">kmsg</span><span> to the </span><span class="vdnyYxSRCi-c2">sec_log</span><span>. The </span><span class="vdnyYxSRCi-c2">sec_log</span><span> is a file found at </span><span class="vdnyYxSRCi-c2">/data/log/sec_log.log</span><span class="vdnyYxSRCi-c7">. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> that the exploit triggers is in the Mali GPU graphics driver provided by ARM. ARM replaced the </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> with a call to the more appropriate helper </span><span class="vdnyYxSRCi-c2">pr_warn</span><span> in </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://developer.arm.com/downloads/-/mali-drivers/bifrost-kernel">release BX304L01B-SW-99002-r21p0-01rel1 in February 2020</a></span><span class="vdnyYxSRCi-c7">. However, the A51 (SM-A515F) and A50 (SM-A505F) still used a vulnerable version of the driver (r19p0) as of January 2021. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.f06791a45fc9ef7d932e2432c73114a95e71a32c"></a><a id="t.7"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">/**</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * kbasep_vinstr_hwcnt_reader_ioctl() - hwcnt reader's ioctl.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @filp: Non-NULL pointer to file structure.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @cmd: User command.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @arg: Command's argument.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> *</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * Return: 0 on success, else error code.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">static</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> kbasep_vinstr_hwcnt_reader_ioctl</span><span class="vdnyYxSRCi-c0">(</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> file </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">filp</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> cmd</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> arg</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> rcode</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_vinstr_client </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">cli</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">filp </span><span class="vdnyYxSRCi-c0">||</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">_IOC_TYPE</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">cmd</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> KBASE_HWCNT_READER</span><span class="vdnyYxSRCi-c0">))</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> cli </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> filp</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">private_data</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">cli</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">switch</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">cmd</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">case</span><span class="vdnyYxSRCi-c4"> KBASE_HWCNT_READER_GET_API_VERSION</span><span class="vdnyYxSRCi-c0">:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> rcode </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> put_user</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">HWCNT_READER_API</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">u32 __user </span><span class="vdnyYxSRCi-c0">*)</span><span class="vdnyYxSRCi-c4">arg</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">break</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">case</span><span class="vdnyYxSRCi-c4"> KBASE_HWCNT_READER_GET_HWVER</span><span class="vdnyYxSRCi-c0">:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> rcode </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> kbasep_vinstr_hwcnt_reader_ioctl_get_hwver</span><span class="vdnyYxSRCi-c0">(</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> cli</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">u32 __user </span><span class="vdnyYxSRCi-c0">*)</span><span class="vdnyYxSRCi-c4">arg</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">break</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">case</span><span class="vdnyYxSRCi-c4"> KBASE_HWCNT_READER_GET_BUFFER_SIZE</span><span class="vdnyYxSRCi-c0">:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> rcode </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> put_user</span><span class="vdnyYxSRCi-c0">(</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">u32</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4">cli</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">vctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">metadata</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">dump_buf_bytes</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">u32 __user </span><span class="vdnyYxSRCi-c0">*)</span><span class="vdnyYxSRCi-c4">arg</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">break</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"> </span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">[...]</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">default</span><span class="vdnyYxSRCi-c0">:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> WARN_ON</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">true</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> rcode </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">break</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> rcode</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Specifically the </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> is in the function </span><span class="vdnyYxSRCi-c2">kbase_vinstr_hwcnt_reader_ioctl</span><span>. To trigger, the exploit only needs to call an invalid ioctl number for the HWCNT driver and the </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> will be hit. The exploit makes two ioctl calls: the first is the Mali driver’s </span><span class="vdnyYxSRCi-c2">HWCNT_READER_SETUP</span><span class="vdnyYxSRCi-c7"> ioctl to initialize the hwcnt driver and be able to call ioctl’s and then to the hwcnt ioctl target with an invalid ioctl number: 0xFE.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.5740d1b795e6d8f3d2789813a2e7543c30152854"></a><a id="t.8"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> hwcnt_fd </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> ioctl</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">dev_mali_fd</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x40148008</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">v4</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> ioctl</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">hwcnt_fd</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x4004BEFE</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">);</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To trigger the vulnerability the exploit sends an invalid ioctl to the HWCNT driver a few times and then </span><span>triggers a bug report</span><span class="vdnyYxSRCi-c7"> by calling:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.71e1e052b900c100803e05db5cd418f563a1e76f"></a><a id="t.9"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">setprop dumpstate</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">options bugreportfull</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">setprop ctl</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">start bugreport</span><span class="vdnyYxSRCi-c0">;</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>In Android, t</span><span>he property </span><span class="vdnyYxSRCi-c2">ctl.start</span><span> starts a service that is defined in </span><span class="vdnyYxSRCi-c2">init</span><span>. On the targeted Samsung devices, the SELinux policy for who has access to the </span><span class="vdnyYxSRCi-c2">ctl.start</span><span> property is much more permissive than AOSP’s policy. Most notably in this exploit’s case, </span><span class="vdnyYxSRCi-c2">system_app</span><span> has access to set </span><span class="vdnyYxSRCi-c2">ctl_start</span><span class="vdnyYxSRCi-c7"> and thus initiate the bugreport. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.22b28c103561fd0f51d7a87c9f8dd7a76750e7dd"></a><a id="t.10"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow at_distributor ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow at_distributor ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow bootchecker ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow bootchecker ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow dumpstate property_type</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow hal_keymaster_default ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow hal_keymaster_default ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow ikev2_client ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow ikev2_client ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow init property_type</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append create getattr map open read relabelto rename setattr unlink write </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow init property_type</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow keystore ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow keystore ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow mediadrmserver ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow mediadrmserver ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow multiclientd ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow multiclientd ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow radio ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow radio ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow shell ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow shell ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow surfaceflinger ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow surfaceflinger ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">allow system_app ctl_start_prop</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">:</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> getattr map open read </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">allow system_app ctl_start_prop</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">:</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">property_service </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">set</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow system_server ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow system_server ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow vold ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow vold ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow wlandutservice ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow wlandutservice ctl_start_prop</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">property_service </span><span class="vdnyYxSRCi-c5">set</span><span class="vdnyYxSRCi-c0">;</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The bugreport service is defined in </span><span class="vdnyYxSRCi-c2">/system/etc/init/dumpstate.rc</span><span class="vdnyYxSRCi-c7">:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.a28f679f59f4b6f4de860e93ae1130f9799f221a"></a><a id="t.11"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">service bugreport </span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">system</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">bin</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">dumpstate </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">d </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">p </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">B </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">z </span><span class="vdnyYxSRCi-c0">\</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">o </span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">user_de</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">com</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">android</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">shell</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">files</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">bugreports</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c3">bugreport</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">class</span><span class="vdnyYxSRCi-c3"> main</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"> disabled</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> oneshot</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c2">bugreport</span><span> service in </span><span class="vdnyYxSRCi-c2">dumpstate.rc</span><span> is a Samsung-specific customization. The </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://cs.android.com/android/platform/superproject/+/master:frameworks/native/cmds/dumpstate/dumpstate.rc;l=1?q=dumpstate.rc&sq=&ss=android%2Fplatform%2Fsuperproject">AOSP version of </a></span><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c27"><a class="vdnyYxSRCi-c61" href="https://cs.android.com/android/platform/superproject/+/master:frameworks/native/cmds/dumpstate/dumpstate.rc;l=1?q=dumpstate.rc&sq=&ss=android%2Fplatform%2Fsuperproject">dumpstate.rc</a></span><span class="vdnyYxSRCi-c7"> doesn’t include this service.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The Samsung version of the </span><span class="vdnyYxSRCi-c2">dumpstate</span><span> (</span><span class="vdnyYxSRCi-c2">/system/bin/dumpstate</span><span>) binary then copies everything from </span><span class="vdnyYxSRCi-c2">/proc/sec_log</span><span> to </span><span class="vdnyYxSRCi-c2">/data/log/</span><span class="vdnyYxSRCi-c2">sec_log</span><span class="vdnyYxSRCi-c2">.log</span><span> as shown in the pseudo-code below. This is the first few lines of the </span><span class="vdnyYxSRCi-c2">dumpstate()</span><span> function within the </span><span class="vdnyYxSRCi-c2">dumpstate</span><span> binary. The </span><span class="vdnyYxSRCi-c2">dump_sec_log</span><span class="vdnyYxSRCi-c7"> (symbols included within the binary) function copies everything from the path provided in argument two to the path provided in argument three.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.d1bbc0067f19bca88f96968d3c911e88cda5b376"></a><a id="t.12"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> _ReadStatusReg</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ARM64_SYSREG</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">3</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">3</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">13</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">2</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> LOBYTE</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">s</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">18</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> v650</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">]</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0LL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> s_8 </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">17664LL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*(</span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">**)((</span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*)&</span><span class="vdnyYxSRCi-c4">s </span><span class="vdnyYxSRCi-c0">+</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*(</span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">**)</span><span class="vdnyYxSRCi-c18">"DUMPSTATE"</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c12">DurationReporter</span><span class="vdnyYxSRCi-c0">::</span><span class="vdnyYxSRCi-c12">DurationReporter</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">v636</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">__int64</span><span class="vdnyYxSRCi-c0">)&</span><span class="vdnyYxSRCi-c4">s</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">((</span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> __int8</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4">s </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">operator</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">delete</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">v650</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">]);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> dump_sec_log</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c18 vdnyYxSRCi-c9">"SEC LOG"</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c18 vdnyYxSRCi-c9">"/proc/sec_log"</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c18 vdnyYxSRCi-c9">"/data/log/sec_log.log"</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">);</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>After starting the </span><span class="vdnyYxSRCi-c2">bugreport</span><span> service, the exploit uses </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://man7.org/linux/man-pages/man7/inotify.7.html">inotify</a></span><span> to monitor for </span><span class="vdnyYxSRCi-c2">IN_CLOSE_WRITE</span><span> events in the </span><span class="vdnyYxSRCi-c2">/data/log/</span><span> directory. </span><span class="vdnyYxSRCi-c2">IN_CLOSE_WRITE</span><span> triggers when a file that was opened for writing is closed. So this watch will occur when </span><span class="vdnyYxSRCi-c2">dumpstate</span><span> is finished writing to </span><span class="vdnyYxSRCi-c2">sec_log.log</span><span>.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>An example of the </span><span class="vdnyYxSRCi-c2">sec_log.log</span><span> file contents generated after hitting the </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> statement is shown below. The exploit combs through the file contents looking for two values on the stack that are at address </span><span class="vdnyYxSRCi-c2">*b60</span><span> and </span><span class="vdnyYxSRCi-c2">*bc0</span><span>: the </span><span class="vdnyYxSRCi-c2">task_struct</span><span> and the </span><span class="vdnyYxSRCi-c2">sys_call_table</span><span class="vdnyYxSRCi-c7"> address.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b661857298a41a556dd5123b1b902d7684a5d3bb"></a><a id="t.13"></a><table class="vdnyYxSRCi-c47"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c48" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635627] [4: poc:25943] ------------[ cut here ]------------</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635654] [4: poc:25943] WARNING: CPU: 4 PID: 25943 at drivers/gpu/arm/b_r19p0/mali_kbase_vinstr.c:992 kbasep_vinstr_hwcnt_reader_ioctl+0x36c/0x664</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635663] [4: poc:25943] Modules linked in:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635675] [4: poc:25943] CPU: 4 PID: 25943 Comm: poc Tainted: G W 4.14.113-20034833 #1</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635682] [4: poc:25943] Hardware name: Samsung BEYOND1LTE EUR OPEN 26 board based on EXYNOS9820 (DT)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635689] [4: poc:25943] Call trace:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635701] [4: poc:25943] [<0000000000000000>] dump_backtrace+0x0/0x280</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635710] [4: poc:25943] [<0000000000000000>] show_stack+0x18/0x24</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635720] [4: poc:25943] [<0000000000000000>] dump_stack+0xa8/0xe4</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635731] [4: poc:25943] [<0000000000000000>] __warn+0xbc/0x164tv</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635738] [4: poc:25943] [<0000000000000000>] report_bug+0x15c/0x19c</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635746] [4: poc:25943] [<0000000000000000>] bug_handler+0x30/0x8c</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635753] [4: poc:25943] [<0000000000000000>] brk_handler+0x94/0x150</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635760] [4: poc:25943] [<0000000000000000>] do_debug_exception+0xc8/0x164</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635766] [4: poc:25943] Exception stack(0xffffff8014c2bb40 to 0xffffff8014c2bc80)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635775] [4: poc:25943] bb40: ffffffc91b00fa40 000000004004befe 0000000000000000 0000000000000000</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"><4>[90808.635781] [4: poc:25943] bb60: </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">ffffffc061b65800</span><span class="vdnyYxSRCi-c3"> 000000000ecc0408 000000000000000a 000000000000000a</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635789] [4: poc:25943] bb80: 000000004004be30 000000000000be00 ffffffc86b49d700 000000000000000b</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635796] [4: poc:25943] bba0: ffffff8014c2bdd0 0000000080000000 0000000000000026 0000000000000026</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"><4>[90808.635802] [4: poc:25943] bbc0: </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">ffffff8008429834</span><span class="vdnyYxSRCi-c3"> 000000000041bd50 0000000000000000 0000000000000000</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635809] [4: poc:25943] bbe0: ffffffc88b42d500 ffffffffffffffea ffffffc96bda5bc0 0000000000000004</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635816] [4: poc:25943] bc00: 0000000000000000 0000000000000124 000000000000001d ffffff8009293000</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635823] [4: poc:25943] bc20: ffffffc89bb6b180 ffffff8014c2bdf0 ffffff80084294bc ffffff8014c2bd80</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635829] [4: poc:25943] bc40: ffffff800885014c 0000000020400145 0000000000000008 0000000000000008</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c3"><4>[90808.635836] [4: poc:25943] bc60: 0000007fffffffff 0000000000000001 ffffff8014c2bdf0 ffffff800885014c</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"><4>[90808.635843] [4: poc:25943] [<0000000000000000>] el1_dbg+0x18/0x74</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The file </span><span class="vdnyYxSRCi-c2">/data/log/sec_log.log</span><span> has the SELinux context </span><span class="vdnyYxSRCi-c2">dumplog_data_file</span><span> which is widely accessible to many apps as shown below. The exploit is currently running within the </span><span class="vdnyYxSRCi-c2">SamsungTTS</span><span> app which is the </span><span class="vdnyYxSRCi-c2">system_app</span><span> SELinux context. While the exploit does not have access to </span><span class="vdnyYxSRCi-c2">/dev/kmsg</span><span> due to SELinux access controls, it can access the same contents when they are copied to the </span><span class="vdnyYxSRCi-c2">sec_log.log</span><span class="vdnyYxSRCi-c7"> which has more permissive access.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.f01b7aab76bb09bed75d19a610b808d720f8deb7"></a><a id="t.14"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">$ sesearch </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">A </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">t dumplog_data_file </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">c file </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">p open precompiled_sepolicy </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c3"> grep _app</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow aasa_service_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow dualdar_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append create getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read rename setattr unlink write </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow platform_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append create getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read rename setattr unlink write </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow priv_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append create getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read rename setattr unlink write </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">allow system_app dumplog_data_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">:</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> append create getattr ioctl </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">lock</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> map open read rename setattr unlink write </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow teed_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> append create getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read rename setattr unlink write </span><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">allow vzwfiltered_untrusted_app dumplog_data_file</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">file </span><span class="vdnyYxSRCi-c0">{</span><span class="vdnyYxSRCi-c4"> getattr ioctl </span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c4"> map open read </span><span class="vdnyYxSRCi-c0">};</span></p></td></tr></table><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.hmfvv1p1wpse"><span class="vdnyYxSRCi-c16">Fixing the vulnerability</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">There were a few different changes to address this vulnerability:</span></p><ul style="padding: 0;" class="c36 lst-kix_hbruetvjkelf-0 start"><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Modified the </span><span class="vdnyYxSRCi-c2">dumpstate</span><span> binary on the device – As of the March 2021 update, dumpstate no longer writes to </span><span class="vdnyYxSRCi-c2">/data/log/sec_log.log</span><span class="vdnyYxSRCi-c7">.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>Removed the </span><span class="vdnyYxSRCi-c2">bugreport</span><span> service from </span><span class="vdnyYxSRCi-c2">dumpstate.rc</span><span class="vdnyYxSRCi-c7">.</span></li></ul> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">In addition there were a few changes made earlier in 2020 that when included would prevent this vulnerability in the future:</span></p><ul style="padding: 0;" class="c36 lst-kix_x9fpm1r5kf6m-0 start"><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>As mentioned above, in February 2020 ARM had released version r21p0 of the Mali driver which had replaced the </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span> with the more appropriate </span><span class="vdnyYxSRCi-c2">pr_warn</span><span> which does not log a full backtrace. The March 2021 Samsung firmware included updating from version r19p0 of the Mali driver to r26p0 which used </span><span class="vdnyYxSRCi-c2">pr_warn</span><span> instead of </span><span class="vdnyYxSRCi-c2">WARN_ON</span><span class="vdnyYxSRCi-c7">.</span></li><li style="margin-left: 46pt;" class="c1 c21 c8 li-bullet-0"><span>In April 2020, </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/arch/arm64/kernel/traps.c?h=linux-4.14.y&id=6dc0256f802be6bc783fb9542affb48d267f592c">upstream Linux made a change</a></span><span class="vdnyYxSRCi-c7"> to no longer include raw stack contents in kernel backtraces.</span></li></ul> <p class="c1 c8 c14 c39"><span class="vdnyYxSRCi-c7"></span></p><h1 class="vdnyYxSRCi-c28 vdnyYxSRCi-c8" id="h.9gyws0wgb00m"><span class="vdnyYxSRCi-c31">Vulnerability #3 - Arbitrary kernel read and write</span></h1> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The final vulnerability in the chain (CVE-2021-25370) is a use-after-free of a file struct in the Display and Enhancement Controller (DECON) Samsung driver for the Display Processing Unit (DPU). According to the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://patchwork.kernel.org/project/dri-devel/patch/1417097460-18403-1-git-send-email-ajaykumar.rs@samsung.com/">upstream commit message</a></span><span class="vdnyYxSRCi-c7">, DECON is responsible for creating the video signals from pixel data. This vulnerability is used to gain arbitrary kernel read and write access. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDPl73oa-7g4YO-V5owrIWmBkxynQqdOD4lgWyubSEx7dg2BoS502R_3o4QZjFfP3gjwoEVNv7__hYBltjZuj5aIq22wdDPg8klVTrahHcwp-TxgHoOkeIerXdOUh2igwrLtPYFBlDqEJdaccmMuV88suhYn9v07QfM3b2NlHZ0zNCux84B4RojB-U/s1213/image1.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Screenshot of the CVE-2021-25370 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19925 (CVE-2021-25370): Memory corruption in dpu driver  Severity: Moderate Affected versions: O(8.x), P(9.0), Q(10.0), R(11.0) devices with selected Exynos chipsets Reported on: December 12, 2020 Disclosure status: Privately disclosed. An incorrect implementation handling file descriptor in dpu driver prior to SMR Mar-2021 Release 1 results in memory corruption leading to kernel panic. The patch fixes incorrect implementation in dpu driver to address memory corruption." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDPl73oa-7g4YO-V5owrIWmBkxynQqdOD4lgWyubSEx7dg2BoS502R_3o4QZjFfP3gjwoEVNv7__hYBltjZuj5aIq22wdDPg8klVTrahHcwp-TxgHoOkeIerXdOUh2igwrLtPYFBlDqEJdaccmMuV88suhYn9v07QfM3b2NlHZ0zNCux84B4RojB-U/s1200/image1.png" style="max-height: 750px; max-width: 600px;" title="Screenshot of the CVE-2021-25370 entry from Samsung's March 2021 security update. It reads: &quot;SVE-2021-19925 (CVE-2021-25370): Memory corruption in dpu driver  Severity: Moderate Affected versions: O(8.x), P(9.0), Q(10.0), R(11.0) devices with selected Exynos chipsets Reported on: December 12, 2020 Disclosure status: Privately disclosed. An incorrect implementation handling file descriptor in dpu driver prior to SMR Mar-2021 Release 1 results in memory corruption leading to kernel panic. The patch fixes incorrect implementation in dpu driver to address memory corruption." /></a></span></p><h2 class="vdnyYxSRCi-c8 vdnyYxSRCi-c17" id="h.ueqobjujvt37"><span class="vdnyYxSRCi-c16">Find the PID of android.hardware.graphics.composer</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To be able to trigger the vulnerability the exploit needs an fd for the driver in order to send ioctl calls. To find the fd, the exploit has to to iterate through the fd </span><span class="vdnyYxSRCi-c2">proc</span><span class="vdnyYxSRCi-c7"> directory for the target process. Therefore the exploit first needs to find the PID for the graphics process. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit connects to </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://cs.android.com/android/platform/superproject/+/586af4e17de6b8bf665dc2a1bb61f46fddb326f7:system/logging/logd/main.cpp;l=256">LogReader which listens at</a></span><span> </span><span class="vdnyYxSRCi-c2">/dev/socket/logdr</span><span class="vdnyYxSRCi-c7">. When a client connects to LogReader, LogReader writes the log contents back to the client. The exploit then configures LogReader to send it logs for the main log buffer (0), system log buffer (3), and the crash log buffer (4) by writing back to LogReader via the socket:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.ead3e3d8bb8dda4abd6611b15560f906e9e1ef80"></a><a id="t.15"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">stream lids</span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c13">3</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c13">4</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit then monitors the log contents until it sees the words ‘display’ or </span><span>‘SDM’</span><span class="vdnyYxSRCi-c7">. Once it finds a ‘display’ or ‘SDM’ log entry, the exploit then reads the PID from that log entry.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Now it has the PID of android.hardware.graphics.composer, where </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://source.android.com/docs/core/graphics/hwc">android.hardware.graphics composer is the Hardware Composer HAL</a></span><span class="vdnyYxSRCi-c7">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Next the exploit needs to find the full file path for the DECON driver. The full file path can exist in a few different places on the filesystem so to find which one it is on this device, the exploit iterates through the </span><span class="vdnyYxSRCi-c2">/proc/<PID>/fd/</span><span> directory looking for any file path that contains “</span><span class="vdnyYxSRCi-c2">graphics/fb0</span><span>”, the DECON driver. It uses </span><span class="vdnyYxSRCi-c2">readlink</span><span> to find the file path for each </span><span class="vdnyYxSRCi-c2">/proc/<PID>/fd/<fd></span><span>. The </span><span class="vdnyYxSRCi-c2">semclipboard</span><span class="vdnyYxSRCi-c7"> vulnerability (vulnerability #1) is then used to get the raw file descriptor for the DECON driver path. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.tv7cyox27woi"><span class="vdnyYxSRCi-c16">Triggering the Use-After-Free</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The vulnerability is in the </span><span class="vdnyYxSRCi-c2">decon_set_win_config</span><span> function in the Samsung DECON driver. The vulnerability is a </span><span>relatively common use-after-free pattern in kernel drivers. First, the driver acquires an fd for a </span><span>fence</span><span>. This fd is associated with a file pointer in a </span><span class="vdnyYxSRCi-c2">sync_file</span><span> struct, specifically the </span><span class="vdnyYxSRCi-c2">file</span><span> member. A “</span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://www.kernel.org/doc/html/latest/driver-api/sync_file.html">fence</a></span><span class="vdnyYxSRCi-c7">” is used for sharing buffers and synchronizing access between drivers and different processes. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.79013f3cd36b222e13f8754dff072e9c46a78a1d"></a><a id="t.16"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">/**</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * struct sync_file - sync file to export to the userspace</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @file: file representing this fence</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @sync_file_list: membership in global file list</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @wq: wait queue for fence signaling</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @fence: fence with the fences in the sync_file</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @cb: fence callback information</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> sync_file </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">struct</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> file </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">*</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/**</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @user_name:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> *</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * Name of the sync file provided by userspace, for merged fences.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * Otherwise generated through driver callbacks (in which case the</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * entire array is 0).</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> user_name</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c13">32</span><span class="vdnyYxSRCi-c0">];</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#ifdef</span><span class="vdnyYxSRCi-c3"> CONFIG_DEBUG_FS</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> list_head sync_file_list</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#endif</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> wait_queue_head_t wq</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> flags</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> dma_fence </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> dma_fence_cb cb</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">};</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The driver then calls </span><span class="vdnyYxSRCi-c2">fd_install</span><span> on the </span><span>fd and file pointer, which makes the fd</span><span> accessible from userspace and transfers ownership of the reference to the fd table. Userspace is able to call </span><span class="vdnyYxSRCi-c2">close</span><span class="vdnyYxSRCi-c7"> on that fd. If that fd holds the only reference to the file struct, then the file struct is freed. However, the driver continues to use the pointer to that freed file struct.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.3acf649b0273fce30d25bdc90665e3e537859c59"></a><a id="t.17"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">static</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> decon_set_win_config</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_device </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_win_config_data </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">win_data</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> num_of_window </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_reg_data </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">regs</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> sync_file </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> i</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> j</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> ret </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">[...]</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> num_of_window </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> decon_get_active_win_count</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> win_data</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">num_of_window</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">retire_fence </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> decon_create_fence</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">decon</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">&</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">retire_fence </span><span class="vdnyYxSRCi-c0"><</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">goto</span><span class="vdnyYxSRCi-c4"> err_prepare</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">[...]</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">num_of_window</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">fd_install</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">retire_fence</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">decon_create_release_fences</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">decon</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">)</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> regs</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">retire_fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> dma_fence_get</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#endif</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">[...]</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> ret</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c33">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>In this case, </span><span class="vdnyYxSRCi-c2">decon_set_win_config</span><span> acquires the </span><span>fd</span><span> for </span><span class="vdnyYxSRCi-c2">retire_fence</span><span> in </span><span class="vdnyYxSRCi-c2">decon_create_fence</span><span class="vdnyYxSRCi-c7">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.1cdd8ca32332a13a24b96424b52e329a1e3820d4"></a><a id="t.18"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> decon_create_fence</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_device </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> sync_file </span><span class="vdnyYxSRCi-c0">**</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> dma_fence </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> fd </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EMFILE</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> kzalloc</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">sizeof</span><span class="vdnyYxSRCi-c0">(*</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">),</span><span class="vdnyYxSRCi-c4"> GFP_KERNEL</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">ENOMEM</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dma_fence_init</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">decon_fence_ops</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">context</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> atomic_inc_return</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">timeline</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">sync_file </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> sync_file_create</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dma_fence_put</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!(*</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon_err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"%s: failed to create sync file\n"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> __func__</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">ENOMEM</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">fd </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> decon_get_valid_fd</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">();</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">fd </span><span class="vdnyYxSRCi-c0"><</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon_err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"%s: failed to get unused fd\n"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> __func__</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> fput</span><span class="vdnyYxSRCi-c0">((*</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">)-></span><span class="vdnyYxSRCi-c4">file</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> fd</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The function then calls </span><span class="vdnyYxSRCi-c2">fd_install(win_data->retire_fence, sync_file->file)</span><span> which means that userspace can now access the fd. When </span><span class="vdnyYxSRCi-c2">fd_install</span><span> is called, another reference is not taken on the file so when userspace calls </span><span class="vdnyYxSRCi-c2">close(fd)</span><span>, the only reference on the file is dropped and the file struct is freed. The issue is that after calling </span><span class="vdnyYxSRCi-c2">fd_install</span><span> the function then calls </span><span class="vdnyYxSRCi-c2">decon_create_release_fences(decon, win_data, sync_file)</span><span> with the same </span><span class="vdnyYxSRCi-c2">sync_file</span><span> that contains the pointer to the freed file struct.</span><span class="vdnyYxSRCi-c7"> </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.07bbc43ea1d81ec2c0dcdfead664a1f6cc977475"></a><a id="t.19"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> decon_create_release_fences</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_device </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> decon_win_config_data </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">win_data</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> sync_file </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> i </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">for</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">i </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span><span class="vdnyYxSRCi-c4"> i </span><span class="vdnyYxSRCi-c0"><</span><span class="vdnyYxSRCi-c4"> decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">dt</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">max_win</span><span class="vdnyYxSRCi-c0">;</span><span class="vdnyYxSRCi-c4"> i</span><span class="vdnyYxSRCi-c0">++)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> state </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">].</span><span class="vdnyYxSRCi-c4">state</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> rel_fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">state </span><span class="vdnyYxSRCi-c0">==</span><span class="vdnyYxSRCi-c4"> DECON_WIN_STATE_BUFFER</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">rel_fence </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> decon_get_valid_fd</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">();</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">rel_fence </span><span class="vdnyYxSRCi-c0"><</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon_err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"%s: failed to get unused fd\n"</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> __func__</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">goto</span><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">fd_install</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">rel_fence</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> get_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">].</span><span class="vdnyYxSRCi-c4">rel_fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> rel_fence</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">err</span><span class="vdnyYxSRCi-c0">:</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">while</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">--</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">></span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">].</span><span class="vdnyYxSRCi-c4">state </span><span class="vdnyYxSRCi-c0">==</span><span class="vdnyYxSRCi-c4"> DECON_WIN_STATE_BUFFER</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> put_unused_fd</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">].</span><span class="vdnyYxSRCi-c4">rel_fence</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> win_data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c4">i</span><span class="vdnyYxSRCi-c0">].</span><span class="vdnyYxSRCi-c4">rel_fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c2">decon_create_release_fences</span><span> gets a new fd, but then associates that new fd with the freed file struct, </span><span class="vdnyYxSRCi-c2">sync_file->file,</span><span> in the call to </span><span class="vdnyYxSRCi-c2">fd_install</span><span class="vdnyYxSRCi-c7">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>When </span><span class="vdnyYxSRCi-c2">decon_set_win_config</span><span> returns, </span><span class="vdnyYxSRCi-c2">retire_fence</span><span> is the closed fd that points to the freed file struct and </span><span class="vdnyYxSRCi-c2">rel_fence</span><span class="vdnyYxSRCi-c7"> is the open fd that points to the freed file struct.</span></p><h3 class="vdnyYxSRCi-c8 vdnyYxSRCi-c52" id="h.k6qkjz8un3r0"><span class="vdnyYxSRCi-c33 vdnyYxSRCi-c42">Fixing the vulnerability</span></h3> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Samsung fixed this use-after-free in March 2021 as CVE-2021-25370. The fix was to move the call to </span><span class="vdnyYxSRCi-c2">fd_install</span><span> in </span><span class="vdnyYxSRCi-c2">decon_set_win_config</span><span> to the latest possible point in the function after the call to </span><span class="vdnyYxSRCi-c2">decon_create_release_fences</span><span class="vdnyYxSRCi-c7">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.1c8f2a2ff722387482b98ebe34bbd0555192a9e2"></a><a id="t.20"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">num_of_window</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">-</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c29"> fd_install</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c29">win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c29">retire_fence</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c29"> sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c29">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c29">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon_create_release_fences</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> win_data</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> sync_file</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#if !defined(CONFIG_SUPPORT_LEGACY_FENCE)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> regs</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">retire_fence </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> dma_fence_get</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">sync_file</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">fence</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#endif</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon_hiber_block</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> mutex_lock</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">up</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> list_add_tail</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">regs</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">list</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">up</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">list</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">+</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> atomic_inc</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">(&</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">decon</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">up</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">remaining_frame</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">update_regs_list_cnt</span><span class="vdnyYxSRCi-c0">++;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">+</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">extra</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">remained_frames </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> atomic_read</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">(&</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">decon</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">up</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">remaining_frame</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> mutex_unlock</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">up</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kthread_queue_work</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">up</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">worker</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">up</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">work</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">+</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">/*</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * The code is moved here because the DPU driver may get a wrong fd</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * through the released file pointer,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * if the user(HWC) closes the fd and releases the file pointer.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ *</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * Since the user land can use fd from this point/time,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * it can be guaranteed to use an unreleased file pointer</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ * when creating a rel_fence in decon_create_release_fences(...)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c15">+ */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">+</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c15">if</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">num_of_window</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">+</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> fd_install</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">win_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">retire_fence</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15"> sync_file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c15">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c15">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> mutex_unlock</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">decon</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c5">lock</span><span class="vdnyYxSRCi-c0">);</span></p></td></tr></table><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.3x34vnbxlb65"><span class="vdnyYxSRCi-c16">Heap Grooming and Spray</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To groom the heap the exploit first opens and closes 30,000+ files using </span><span class="vdnyYxSRCi-c2">memfd_create</span><span>. Then, the exploit sprays the </span><span>heap</span><span class="vdnyYxSRCi-c7"> with fake file structs. On this version of the Samsung kernel, the file struct is 0x140 bytes. In these new, fake file structs, the exploit sets four of the members:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.a0f322910268bcdf319b3b8a5c5329726f92499f"></a><a id="t.21"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">fake_file</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">f_u </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x1010101</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">fake_file</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">f_op </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> kaddr </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x2071B0</span><span class="vdnyYxSRCi-c0">+</span><span class="vdnyYxSRCi-c13">0x1094E80</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">fake_file</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">f_count </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x7F</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">fake_file</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">private_data </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4">addr_limit_ptr</span><span class="vdnyYxSRCi-c0">;</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The </span><span class="vdnyYxSRCi-c2">f_op</span><span> member is set to the </span><span class="vdnyYxSRCi-c2">signalfd_op</span><span> for reasons we</span><span> will cover below in the “Overwriting the addr_limit” section. </span><span class="vdnyYxSRCi-c2">kaddr</span><span> is the address leaked using vulnerability #2 described previously. The </span><span class="vdnyYxSRCi-c2">addr_limit_ptr</span><span> was calculated by adding 8 to the </span><span class="vdnyYxSRCi-c2">task_struct</span><span class="vdnyYxSRCi-c7"> address also leaked using vulnerability #2.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit sprays 25 of these structs across the </span><span>heap</span><span> using the </span><span class="vdnyYxSRCi-c2">MEM_PROFILE_ADD</span><span class="vdnyYxSRCi-c7"> ioctl in the Mali driver. </span></p><a id="t.f09c2146baf213427f9b780c78be6f03204fa8b6"></a><a id="t.22"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">/**</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * struct kbase_ioctl_mem_profile_add - Provide profiling information to kernel</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @buffer: Pointer to the information</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @len: Length</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * @padding: Padding</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> *</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * The data provided is accessible through a debugfs file</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_ioctl_mem_profile_add </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> __u64 buffer</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> __u32 len</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> __u32 padding</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">};</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#define</span><span class="vdnyYxSRCi-c4"> KBASE_ioctl_MEM_PROFILE_ADD </span><span class="vdnyYxSRCi-c0">\</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> _IOW</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">KBASE_ioctl_TYPE</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">27</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_ioctl_mem_profile_add</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">static</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> kbase_api_mem_profile_add</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_context </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_ioctl_mem_profile_add </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">len </span><span class="vdnyYxSRCi-c0">></span><span class="vdnyYxSRCi-c4"> KBASE_MEM_PROFILE_MAX_BUF_SIZE</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dev_err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">kbdev</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">dev</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"mem_profile_add: buffer too big\n"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> buf </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> kmalloc</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">len</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> GFP_KERNEL</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ZERO_OR_NULL_PTR</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">))</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">ENOMEM</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> err </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> copy_from_user</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> u64_to_user_ptr</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">buffer</span><span class="vdnyYxSRCi-c0">),</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">len</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">err</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kfree</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EFAULT</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> kbasep_mem_profile_debugfs_insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> data</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">len</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>This ioctl takes a pointer to a buffer, the length of the buffer, and padding as arguments. </span><span class="vdnyYxSRCi-c2">kbase_api_mem_profile_add</span><span class="vdnyYxSRCi-c7"> will allocate a buffer on the kernel heap and then will copy the passed buffer from userspace into the newly allocated kernel buffer.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Finally, </span><span class="vdnyYxSRCi-c2">kbase_api_mem_profile_add</span><span> calls </span><span class="vdnyYxSRCi-c2">kbasep_mem_profile_debugfs_insert</span><span>. This technique only works when the device is running a kernel with </span><span class="vdnyYxSRCi-c2">CONFIG_DEBUG_FS</span><span> enabled. The purpose of the </span><span class="vdnyYxSRCi-c2">MEM_PROFILE_ADD</span><span> ioctl is to write a buffer to DebugFS. As of Android 11, DebugFS should not be enabled on production devices. </span><span>Whenever Android launches new requirements like this, it only applies to devices launched on that new version of Android. Android 11 launched in September 2020 and the exploit was found in November 2020 so it makes sense that the exploit targeted devices Android 10 and before where DebugFS would have been mounted.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYOnatV5Tbxu0n0Q5oP5HGHvZowemJC_GKvaFf-FAuwlNfx-m5AxwqWU0g5oMMejAoIUcCi10u7pE3n_uuhT3mmfE-7e74_B-ZifuKTmF44asidpw34Gg0jFNAMVKa2i9MsZ6MSGJrN2RayBa3kiPTBc-9JSqhk42W-wjV-IEAYhaQUiZ1hwiVmBpB/s903/image4.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Screenshot of the DebugFS section from https://source.android.com/docs/setup/about/android-11-release#debugfs.  The highlighted text reads: &quot;Android 11 removes platform support for DebugFS and requires that it not be mounted or accessed on production devices&quot;" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYOnatV5Tbxu0n0Q5oP5HGHvZowemJC_GKvaFf-FAuwlNfx-m5AxwqWU0g5oMMejAoIUcCi10u7pE3n_uuhT3mmfE-7e74_B-ZifuKTmF44asidpw34Gg0jFNAMVKa2i9MsZ6MSGJrN2RayBa3kiPTBc-9JSqhk42W-wjV-IEAYhaQUiZ1hwiVmBpB/s903/image4.png" style="max-height: 750px; max-width: 600px;" title="Screenshot of the DebugFS section from https://source.android.com/docs/setup/about/android-11-release#debugfs.  The highlighted text reads: &quot;Android 11 removes platform support for DebugFS and requires that it not be mounted or accessed on production devices&quot;" /></a></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>For example, on the A51 exynos device (SM-A515F) which launched on Android 10, both </span><span class="vdnyYxSRCi-c2">CONFIG_DEBUG_FS</span><span class="vdnyYxSRCi-c7"> is enabled and DebugFS is mounted. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.7ecb17c683a1281f47d4612e785891bb86b4709f"></a><a id="t.23"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c18">/ $ getprop ro.build.fingerprint</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c18">samsung/</span><span class="vdnyYxSRCi-c4">a51nnxx</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c13">11</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">RP1A</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c13">200720.012</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">A515FXXU4DUB1</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c4">user</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">release</span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c3">keys</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c18">/ $ getprop ro.build.version.security_patch</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c18">2021-02-01</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c18">a51:/</span><span class="vdnyYxSRCi-c4"> $ uname </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c3">a</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c12">Linux</span><span class="vdnyYxSRCi-c4"> localhost </span><span class="vdnyYxSRCi-c13">4.14</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c13">113</span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c13">20899478</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">#1 SMP PREEMPT Mon Feb 1 15:37:03 KST 2021 aarch64</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c18">/ $ cat /</span><span class="vdnyYxSRCi-c4">proc</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">config</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">gz </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> gunzip </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> cat </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c3"> grep CONFIG_DEBUG_FS </span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">CONFIG_DEBUG_FS</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c3 vdnyYxSRCi-c9">y</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">a51</span><span class="vdnyYxSRCi-c0">:</span><span class="vdnyYxSRCi-c18">/ $ cat /</span><span class="vdnyYxSRCi-c4">proc</span><span class="vdnyYxSRCi-c0">/</span><span class="vdnyYxSRCi-c4">mounts </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c3"> grep debug </span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sys</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">kernel</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">debug </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sys</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">kernel</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">/</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">debug debugfs rw</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">seclabel</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">relatime </span><span class="vdnyYxSRCi-c13 vdnyYxSRCi-c9">0</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c13 vdnyYxSRCi-c9">0</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Because DebugFS is mounted, the exploit is able to use the </span><span class="vdnyYxSRCi-c2">MEM_PROFILE_ADD</span><span> ioctl </span><span>to </span><span>groom the heap. </span><span>If DebugFS wasn’t enabled or mounted, </span><span class="vdnyYxSRCi-c2">kbasep_mem_profile_debugfs_insert</span><span> would simply free the newly allocated kernel buffer and return.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b54890eb264f51ceef4db4a7d94fde1a7369f3a7"></a><a id="t.24"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#ifdef</span><span class="vdnyYxSRCi-c3"> CONFIG_DEBUG_FS</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> kbasep_mem_profile_debugfs_insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_context </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> size_t size</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> err </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> mutex_lock</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">mem_profile_lock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dev_dbg</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">kbdev</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">dev</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"initialised: %d"</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kbase_ctx_flag</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> KCTX_MEM_PROFILE_INITIALIZED</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">kbase_ctx_flag</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> KCTX_MEM_PROFILE_INITIALIZED</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">IS_ERR_OR_NULL</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">kctx_dentry</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> err </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">ENOMEM</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">debugfs_create_file</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"mem_profile"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0444</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">kctx_dentry</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> kctx</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">kbasep_mem_profile_debugfs_fops</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> err </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EAGAIN</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kbase_ctx_flag_set</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> KCTX_MEM_PROFILE_INITIALIZED</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kbase_ctx_flag</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> KCTX_MEM_PROFILE_INITIALIZED</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kfree</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">mem_profile_data</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">mem_profile_data </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> data</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">mem_profile_size </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> size</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kfree</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> dev_dbg</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">kbdev</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">dev</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"returning: %d, initialised: %d"</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> kbase_ctx_flag</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> KCTX_MEM_PROFILE_INITIALIZED</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> mutex_unlock</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">mem_profile_lock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#else</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/* CONFIG_DEBUG_FS */</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c4"> kbasep_mem_profile_debugfs_insert</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> kbase_context </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">kctx</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">char</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> size_t size</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kfree</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">data</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">#endif</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/* CONFIG_DEBUG_FS */</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">By writing the fake file structs as a singular 0x2000 size buffer rather than as 25 individual 0x140 size buffers, the exploit will be writing their fake structs to two whole pages which increases the odds of reallocating over the freed file struct.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit then calls </span><span class="vdnyYxSRCi-c2">dup2</span><span> on the dangling FD’s. The </span><span class="vdnyYxSRCi-c2">dup2</span><span> syscall will open another fd on the same open file structure that the original points to. In this case, the exploit is calling </span><span class="vdnyYxSRCi-c2">dup2</span><span> to verify that they successfully reallocated a fake file structure in the same place as the freed file structure. </span><span class="vdnyYxSRCi-c2">dup2</span><span> will increment the reference count (</span><span class="vdnyYxSRCi-c2">f_count</span><span>) in the file structure. In all of our fake file structures, the </span><span class="vdnyYxSRCi-c2">f_count</span><span class="vdnyYxSRCi-c7"> was set to 0x7F. So if any of them are incremented to 0x80, the exploit knows that it successfully reallocated over the freed file struct.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>To determine if any of the file struct’s refcounts were incremented, the exploit iterates through each of the directories under </span><span class="vdnyYxSRCi-c2">/sys/kernel/debug/mali/mem/</span><span> and reads each directory’s </span><span class="vdnyYxSRCi-c2">mem_profile</span><span> contents. If it finds the byte 0x80, then it knows that it successfully reallocated the freed struct and that the </span><span class="vdnyYxSRCi-c2">f_count</span><span> of the fake file struct was incremented.</span></p><h2 class="vdnyYxSRCi-c17 vdnyYxSRCi-c8" id="h.yfq0poarwpr9"><span>Overwriting the addr_limit</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Like many previous Android exploits, to gain arbitrary kernel read and write, the exploit overwrites the kernel address limit (</span><span class="vdnyYxSRCi-c2">addr_limit</span><span>). The </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> defines the address range that the kernel may access when dereferencing userspace pointers. For userspace threads, the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> is usually </span><span class="vdnyYxSRCi-c2">USER_DS</span><span> or</span><span> </span><span class="vdnyYxSRCi-c2">0x7FFFFFFFFF</span><span>. For kernel threads, it’s usually </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> or </span><span class="vdnyYxSRCi-c2">0xFFFFFFFF</span><span class="vdnyYxSRCi-c2">FFFFFFFF</span><span class="vdnyYxSRCi-c7">. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>Userspace operations only access addresses below the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span>. Therefore, by raising the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> by overwriting it, we will make kernel memory accessible to our unprivileged process. </span><span>The exploit uses the syscall </span><span class="vdnyYxSRCi-c2">signalfd</span><span class="vdnyYxSRCi-c7"> with the dangling fd to do this.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b20c3d5f1283b74946e58ab9889119293a7eef9f"></a><a id="t.25"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">signalfd</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">dangling_fd</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0xFFFFFF8000000000</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">8</span><span class="vdnyYxSRCi-c0">);</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>According to the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://man7.org/linux/man-pages/man2/signalfd.2.html">man pages</a></span><span>, the syscall </span><span class="vdnyYxSRCi-c2">signalfd</span><span class="vdnyYxSRCi-c7"> is:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c39"><span class="vdnyYxSRCi-c35 vdnyYxSRCi-c11">signalfd() creates a file descriptor that can be used to accept signals targeted at the caller. This provides an alternative to the use of a signal handler or sigwaitinfo(2), and has the advantage that the file descriptor may be monitored by select(2), poll(2), and epoll(7).</span></p> <p class="c1 c8 c14 c39"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b0e9b96cf2ebdb989f730bf0fd87809d54a61b18"></a><a id="t.26"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c11">int</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> signalfd</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c11">(</span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c11">int</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> fd</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c11">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c11">const</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> sigset_t </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c11">*</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11">mask</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c11">,</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c11">int</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c11"> flags</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c11">);</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit called </span><span class="vdnyYxSRCi-c2">signalfd</span><span> on the file descriptor that was found to replace the freed one in the previous step. When </span><span class="vdnyYxSRCi-c2">signalfd</span><span> is called on an existing file descriptor, only the </span><span class="vdnyYxSRCi-c2">mask</span><span> is updated based on the mask passed as the argument, which gives the exploit an 8-byte write to the </span><span class="vdnyYxSRCi-c2">signmask</span><span> of the </span><span class="vdnyYxSRCi-c2">signalfd_ctx</span><span class="vdnyYxSRCi-c7"> struct.. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.650b5ef7704bd97d603a385d99bc69b0dafbdd0f"></a><a id="t.27"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">typedef</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> sigset_t</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> signalfd_ctx </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> sigset_t sigmask</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">};</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The file struct includes a field called </span><span class="vdnyYxSRCi-c2">private_data</span><span> that is a </span><span class="vdnyYxSRCi-c2">void *</span><span>. File structs for </span><span class="vdnyYxSRCi-c2">signalfd</span><span> file descriptors store the pointer to the </span><span class="vdnyYxSRCi-c2">signalfd_ctx</span><span> struct in the </span><span class="vdnyYxSRCi-c2">private_data</span><span> field. As shown above, the </span><span class="vdnyYxSRCi-c2">signalfd_ctx</span><span class="vdnyYxSRCi-c7"> struct is simply an 8 byte structure that contains the mask.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">Let’s walk through how the signalfd source code updates the mask: </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.07266e9a1020f50ea1d3e9e61a15aa713d19b5aa"></a><a id="t.28"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">SYSCALL_DEFINE4</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">signalfd4</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> ufd</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> sigset_t __user </span><span class="vdnyYxSRCi-c0">*,</span><span class="vdnyYxSRCi-c4"> user_mask</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> size_t</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> sizemask</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">int</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> flags</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> sigset_t sigmask</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> signalfd_ctx </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">ctx</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/* Check the SFD_* constants for consistency. */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> BUILD_BUG_ON</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">SFD_CLOEXEC </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> O_CLOEXEC</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> BUILD_BUG_ON</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">SFD_NONBLOCK </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> O_NONBLOCK</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">flags </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">~(</span><span class="vdnyYxSRCi-c4">SFD_CLOEXEC </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> SFD_NONBLOCK</span><span class="vdnyYxSRCi-c0">))</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">sizemask </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">sizeof</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">sigset_t</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">||</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> copy_from_user</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">sigmask</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> user_mask</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">sizeof</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">sigmask</span><span class="vdnyYxSRCi-c0">)))</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4">sigdelsetmask</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">sigmask</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> sigmask</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">SIGKILL</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> sigmask</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">SIGSTOP</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">signotset</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(&</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sigmask</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">);</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [1]</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">if</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">ufd </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">==</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-</span><span class="vdnyYxSRCi-c9 vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">)</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [2]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> ctx </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> kmalloc</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">sizeof</span><span class="vdnyYxSRCi-c0">(*</span><span class="vdnyYxSRCi-c4">ctx</span><span class="vdnyYxSRCi-c0">),</span><span class="vdnyYxSRCi-c4"> GFP_KERNEL</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">ctx</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">ENOMEM</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> ctx</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">sigmask </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> sigmask</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">/*</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * When we call this, the initialization must be complete, since</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> * anon_inode_getfd() will install the fd.</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20"> */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> ufd </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> anon_inode_getfd</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"[signalfd]"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">signalfd_fops</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> ctx</span><span class="vdnyYxSRCi-c0">,</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> O_RDWR </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">flags </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">O_CLOEXEC </span><span class="vdnyYxSRCi-c0">|</span><span class="vdnyYxSRCi-c4"> O_NONBLOCK</span><span class="vdnyYxSRCi-c0">)));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ufd </span><span class="vdnyYxSRCi-c0"><</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> kfree</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ctx</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">}</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">else</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [3]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> fd f </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> fdget</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ufd</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(!</span><span class="vdnyYxSRCi-c4">f</span><span class="vdnyYxSRCi-c0">.</span><span class="vdnyYxSRCi-c4">file</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EBADF</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">ctx </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> f</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">private_data</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">;</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [4]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5 vdnyYxSRCi-c9">if</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">(</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">f</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">.</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">file</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">f_op </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">!=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">&</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">signalfd_fops</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">)</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">{</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [5]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> fdput</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">f</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">-</span><span class="vdnyYxSRCi-c4">EINVAL</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> spin_lock_irq</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">current</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">sighand</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">siglock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">ctx</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">-></span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9">sigmask </span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">=</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> sigmask</span><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c9">;</span><span class="vdnyYxSRCi-c4 vdnyYxSRCi-c9"> </span><span class="vdnyYxSRCi-c20 vdnyYxSRCi-c9">// [6] WRITE!</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> spin_unlock_irq</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">current</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">sighand</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">siglock</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> wake_up</span><span class="vdnyYxSRCi-c0">(&</span><span class="vdnyYxSRCi-c4">current</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">sighand</span><span class="vdnyYxSRCi-c0">-></span><span class="vdnyYxSRCi-c4">signalfd_wqh</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> fdput</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">f</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c3"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">return</span><span class="vdnyYxSRCi-c4"> ufd</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>First the function modifies the mask that was passed in. The </span><span class="vdnyYxSRCi-c2">mask</span><span> passed into the function is the signals that should be accepted via the file descriptor, but the </span><span class="vdnyYxSRCi-c2">sigmask</span><span> member of the </span><span class="vdnyYxSRCi-c2">signalfd</span><span> struct represents the signals that should be blocked. The </span><span class="vdnyYxSRCi-c2">sigdelsetmask</span><span> and </span><span class="vdnyYxSRCi-c2">signotset</span><span> calls at [1] makes this change. The call to </span><span class="vdnyYxSRCi-c2">sigdelsetmask</span><span> ensures that the </span><span class="vdnyYxSRCi-c2">SIG_KILL</span><span> and </span><span class="vdnyYxSRCi-c2">SIG_STOP</span><span> signals are always blocked so it clears bit 8 (</span><span class="vdnyYxSRCi-c2">SIG_KILL</span><span>) and bit 18 (</span><span class="vdnyYxSRCi-c2">SIG_STOP</span><span>) in order for them to be set in the next call. Then </span><span class="vdnyYxSRCi-c2">signotset</span><span> flips each bit in the mask. The mask that is written is </span><span class="vdnyYxSRCi-c2">~(mask_in_arg & 0xFFFFFFFFFFFBFEFF)</span><span>. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The function checks whether or not the file descriptor passed in is </span><span class="vdnyYxSRCi-c2">-1</span><span> at [2]. In this exploit’s case it’s not so we fall into the </span><span class="vdnyYxSRCi-c2">else</span><span> block at [3]. At [4] the </span><span class="vdnyYxSRCi-c2">signalfd_ctx*</span><span> is set to the </span><span class="vdnyYxSRCi-c2">private_data</span><span class="vdnyYxSRCi-c7"> pointer. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The signalfd manual page also says that the fd argument “</span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://man7.org/linux/man-pages/man2/signalfd.2.html#:~:text=If%20fd%20is%20not%20%2D1%2C%20then%20it%20must%20specify%20a%0A%20%20%20%20%20%20%20valid%20existing%20signalfd%20file%20descriptor">must specify a valid existing signalfd file descriptor</a></span><span>”. To verify this, at [5] the syscall checks if the underlying file’s </span><span class="vdnyYxSRCi-c2">f_op</span><span> equals the </span><span class="vdnyYxSRCi-c2">signalfd_ops</span><span>. This is why the </span><span class="vdnyYxSRCi-c2">f_op</span><span> was set to </span><span class="vdnyYxSRCi-c2">signalfd_ops</span><span> in the previous section. Finally at [6], the overwrite occurs. The user provided </span><span class="vdnyYxSRCi-c2">mask</span><span> is written to the address in </span><span class="vdnyYxSRCi-c2">private_data</span><span>. In the exploit’s case, the fake file struct’s </span><span class="vdnyYxSRCi-c2">private_data</span><span> was set to the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> pointer. So when the mask is written, we’re actually overwriting the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span class="vdnyYxSRCi-c2 vdnyYxSRCi-c33 vdnyYxSRCi-c44">.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The exploit calls </span><span class="vdnyYxSRCi-c2">signalfd</span><span> with a mask argument</span><span> of </span><span class="vdnyYxSRCi-c2">0xFFFFFF8000000000</span><span>. So the value </span><span class="vdnyYxSRCi-c2">~(0xFFFFFF8000000000 & 0xFFFFFFFFFFFCFEFF) = 0x7FFFFFFFFF</span><span>, also known as</span><span> </span><span class="vdnyYxSRCi-c2">USER_DS</span><span>. We’ll talk about why they’re overwriting the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> as </span><span class="vdnyYxSRCi-c2">USER_DS</span><span> rather than </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span class="vdnyYxSRCi-c7"> in the next section. </span></p><h2 class="vdnyYxSRCi-c17" id="h.k1hy6h4fm2ex"><span class="vdnyYxSRCi-c16">Working Around UAO and PAN</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>“User-Access Override” (UAO) and “Privileged Access Never” (PAN) are two exploit mitigations that are commonly found on modern Android devices. Their kernel configs are </span><span class="vdnyYxSRCi-c2">CONFIG_ARM64_UAO</span><span> and </span><span class="vdnyYxSRCi-c2">CONFIG_ARM64_PAN</span><span class="vdnyYxSRCi-c7">. Both PAN and UAO are hardware mitigations released on ARMv8 CPUs. PAN protects against the kernel directly accessing user-space memory. UAO works with PAN by allowing unprivileged load and store instructions to act as privileged load and store instructions when the UAO bit is set.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>It’s often said that the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> overwrite technique detailed above doesn’t work on devices with UAO and PAN turned on. The commonly used </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> overwrite technique was to change the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> to a very high address, like 0xFFFFFFFFFFFFFFFF (</span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span>), and then use a pair of pipes for arbitrary kernel read and write. This is what Jann and I did in our </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://bugs.chromium.org/p/project-zero/issues/attachmentText?aid=414885#176">proof-of-concept for CVE-2019-2215</a></span><span> back in 2019. Our </span><span class="vdnyYxSRCi-c2">kernel_write</span><span class="vdnyYxSRCi-c7"> function is shown below.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.b8a4e52cd732ff2532144e609e086484605b13e2"></a><a id="t.29"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> kernel_write</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> kaddr</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> errno </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">len </span><span class="vdnyYxSRCi-c0">></span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x1000</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> errx</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"kernel writes over PAGE_SIZE are messy, tried 0x%lx"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">write</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">],</span><span class="vdnyYxSRCi-c4"> buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"kernel_write failed to load userspace buffer"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">read</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe</span><span class="vdnyYxSRCi-c0">[</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">],</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c0">*)</span><span class="vdnyYxSRCi-c4">kaddr</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">!=</span><span class="vdnyYxSRCi-c4"> len</span><span class="vdnyYxSRCi-c0">)</span><span class="vdnyYxSRCi-c4"> err</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c18">"kernel_write failed to overwrite kernel memory"</span><span class="vdnyYxSRCi-c0">);</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c7">This technique works by first writing the pointer to the buffer of the contents that you’d like written to one end of the pipe. By then calling a read and passing in the kernel address you’d like to write to, those contents are then written to that kernel memory address.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>With UAO and PAN enabled, if the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> is set to </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> and we attempt to execute this function, the first </span><span class="vdnyYxSRCi-c2">write</span><span> call will fail because </span><span class="vdnyYxSRCi-c2">buf</span><span class="vdnyYxSRCi-c7"> is in user-space memory and PAN prevents the kernel from accessing user space memory.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>Let’s say we didn’t set the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> to </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> (-1) and instead set it to -2, a high kernel address that’s not </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span class="vdnyYxSRCi-c7">. PAN wouldn’t be enabled, but neither would UAO. Without UAO enabled, the unprivileged load and store instructions are not able to access the kernel memory.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>The way the exploit works around the constraints of UAO and PAN is pretty straightforward: the exploit switches the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> between </span><span class="vdnyYxSRCi-c2">USER_DS</span><span> and </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> based on whether it needs to access user space or kernel space memory. As shown in the </span><span class="vdnyYxSRCi-c2">uao_thread_switch</span><span> function below, UAO is enabled when </span><span class="vdnyYxSRCi-c2">addr_limit == KERNEL_DS</span><span class="vdnyYxSRCi-c7"> and is disabled when it does not.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.48779bb108abd35cde6afd98c7e3e69e08c1473f"></a><a id="t.30"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c20">/* Restore the UAO state depending on next's addr_limit */</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> uao_thread_switch</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">struct</span><span class="vdnyYxSRCi-c4"> task_struct </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c5">next</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">IS_ENABLED</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">CONFIG_ARM64_UAO</span><span class="vdnyYxSRCi-c0">))</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">if</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">task_thread_info</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">next</span><span class="vdnyYxSRCi-c0">)-></span><span class="vdnyYxSRCi-c4">addr_limit </span><span class="vdnyYxSRCi-c0">==</span><span class="vdnyYxSRCi-c4"> KERNEL_DS</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">asm</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ALTERNATIVE</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"nop"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> SET_PSTATE_UAO</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">1</span><span class="vdnyYxSRCi-c0">),</span><span class="vdnyYxSRCi-c4"> ARM64_HAS_UAO</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">else</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">asm</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">ALTERNATIVE</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c18">"nop"</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> SET_PSTATE_UAO</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c13">0</span><span class="vdnyYxSRCi-c0">),</span><span class="vdnyYxSRCi-c4"> ARM64_HAS_UAO</span><span class="vdnyYxSRCi-c0">));</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">}</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>The exploit was able to use this technique of toggling the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> between </span><span class="vdnyYxSRCi-c2">USER_DS</span><span> and </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> because they had such a good primitive from the use-after-free and could reliably and repeatedly write a new value to the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> by calling </span><span class="vdnyYxSRCi-c2">signalfd</span><span class="vdnyYxSRCi-c7">. The exploit’s function to write to kernel addresses is shown below:</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p><a id="t.5c077e3ae1ab4068a405f87ee040f15caac71721"></a><a id="t.31"></a><table class="vdnyYxSRCi-c23"><tr class="vdnyYxSRCi-c19"><td class="vdnyYxSRCi-c10" colspan="1" rowspan="1"> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4">kernel_write</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">kaddr</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">const</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">void</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">*</span><span class="vdnyYxSRCi-c4">buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> buf_len</span><span class="vdnyYxSRCi-c0">)</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0">{</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">unsigned</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c5">long</span><span class="vdnyYxSRCi-c4"> USER_DS </span><span class="vdnyYxSRCi-c0">=</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">0x7FFFFFFFFF</span><span class="vdnyYxSRCi-c0">;</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> write</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe2</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> buf</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> buf_len</span><span class="vdnyYxSRCi-c0">);</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">// [1]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> write</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe2</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c0">&</span><span class="vdnyYxSRCi-c4">USER_DS</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">8u</span><span class="vdnyYxSRCi-c0">);</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">// [2]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> set_addr_limit_to_KERNEL_DS</span><span class="vdnyYxSRCi-c0">();</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">// [3] </span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> read</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> kaddr</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> buf_len</span><span class="vdnyYxSRCi-c0">);</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">// [4]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c4"> read</span><span class="vdnyYxSRCi-c0">(</span><span class="vdnyYxSRCi-c4">kernel_rw_pipe</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> addr_limit_ptr</span><span class="vdnyYxSRCi-c0">,</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c13">8u</span><span class="vdnyYxSRCi-c0">);</span><span class="vdnyYxSRCi-c4"> </span><span class="vdnyYxSRCi-c20">// [5]</span></p> <p class="vdnyYxSRCi-c1"><span class="vdnyYxSRCi-c0 vdnyYxSRCi-c33">}</span></p></td></tr></table> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>The function takes three arguments: the kernel address to write to (</span><span class="vdnyYxSRCi-c2">kaddr</span><span>), a pointer to the buffer of contents to write (</span><span class="vdnyYxSRCi-c2">buf</span><span>), and the length of the buffer (</span><span class="vdnyYxSRCi-c2">buf_len</span><span>). </span><span class="vdnyYxSRCi-c2">buf</span><span> is in userspace. When the </span><span class="vdnyYxSRCi-c2">kernel_write</span><span> function is entered, the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> is currently set to </span><span class="vdnyYxSRCi-c2">USER_DS</span><span>. At [1] the exploit writes the buffer pointer to the pipe. A pointer to the </span><span class="vdnyYxSRCi-c2">USER_DS</span><span class="vdnyYxSRCi-c7"> value is written to the pipe at [2].</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>The </span><span class="vdnyYxSRCi-c2">set_addr_limit_to_KERNEL_DS</span><span> function at [3] sends a signal to tell another process in the exploit to call </span><span class="vdnyYxSRCi-c2">signalfd</span><span> with a mask of 0. Because </span><span class="vdnyYxSRCi-c2">signalfd</span><span> performs a NOT on the bits provided in the mask in </span><span class="vdnyYxSRCi-c2">signotset</span><span>, the value </span><span class="vdnyYxSRCi-c2">0xFFFFFFFFFFFFFFFF</span><span> (</span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span>) is written to the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span class="vdnyYxSRCi-c7">. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>Now that the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> is set to </span><span class="vdnyYxSRCi-c2">KERNEL_DS</span><span> the exploit can access kernel memory. At [4], the exploit reads from the pipe, writing the contents to </span><span class="vdnyYxSRCi-c2">kaddr</span><span>. Then at [5] the exploit returns </span><span class="vdnyYxSRCi-c2">addr_limit</span><span> back to </span><span class="vdnyYxSRCi-c2">USER_DS</span><span> by reading the value from the pipe that was written at [2] and writing it back to the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span class="vdnyYxSRCi-c7">. The exploit’s function to read from kernel memory is the mirror image of this function.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1"><span>I deliberately am not calling this a bypass because UAO and PAN are acting exactly as they were designed to act: preventing the kernel from accessing user-space memory. UAO and PAN were not developed to protect against arbitrary write access to the </span><span class="vdnyYxSRCi-c2">addr_limit</span><span>. </span></p><h2 class="vdnyYxSRCi-c17" id="h.2exfdp6lv8zm"><span class="vdnyYxSRCi-c16">Post-exploitation</span></h2> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>T</span><span>he exploit now has arbitrary </span><span>kernel read and write</span><span>. It then follows the steps as seen in most other Android exploits: overwrite the </span><span class="vdnyYxSRCi-c2">cred</span><span> struct for the current process and overwrite the loaded SELinux policy to change the current process’s context to </span><span class="vdnyYxSRCi-c2">vold</span><span>.</span><span> </span><span class="vdnyYxSRCi-c2">vold</span><span> is the “Volume Daemon” which is responsible for mounting and unmounting of external storage. </span><span class="vdnyYxSRCi-c2">vold</span><span> runs as </span><span class="vdnyYxSRCi-c2">root</span><span> and while it's a userspace service, it’s considered kernel-equivalent as described in the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://source.android.com/docs/security/overview/updates-resources#process_types">Android documentation on security contexts</a></span><span>. Because it’s a highly privileged security context, it makes a prime target for changing the SELinux context to.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNmMRLkK2FzuHtZxYxOkwpwKjbotI2a0OrAKlIWHd24SXekvuwcem4iCOzsu3ssOq2eqwDeWZi9uaLjfh1oh8a1_foBtkvDt4qM-vqbT3wEp-dmVnv4DZHos2mtCe2nFBGe1ZmxDLOXdOuSyBu0_qRdxQvc0nfDltRu3IftdQ2rVH47dTN_qWAExff/s920/image5.png" style="display: block; padding: 1em 0;text-align: center;"><img alt="Screen shot of the &quot;OS Kernel&quot; section from https://source.android.com/docs/security/overview/updates-resources#process_types. It says:  Functionality that: - is part of the kernel - runs in the same CPU context as the kernel (for example, device drivers) - has direct access to kernel memory (for example, hardware components on the device) - has the capability to load scripts into a kernel component (for example, eBPF) - is one of a handful of user services that's considered kernel equivalent (such as, apexd, bpfloader, init, ueventd, and vold)." border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNmMRLkK2FzuHtZxYxOkwpwKjbotI2a0OrAKlIWHd24SXekvuwcem4iCOzsu3ssOq2eqwDeWZi9uaLjfh1oh8a1_foBtkvDt4qM-vqbT3wEp-dmVnv4DZHos2mtCe2nFBGe1ZmxDLOXdOuSyBu0_qRdxQvc0nfDltRu3IftdQ2rVH47dTN_qWAExff/s920/image5.png" style="max-height: 750px; max-width: 600px;" title="Screen shot of the &quot;OS Kernel&quot; section from https://source.android.com/docs/security/overview/updates-resources#process_types. It says:  Functionality that: - is part of the kernel - runs in the same CPU context as the kernel (for example, device drivers) - has direct access to kernel memory (for example, hardware components on the device) - has the capability to load scripts into a kernel component (for example, eBPF) - is one of a handful of user services that's considered kernel equivalent (such as, apexd, bpfloader, init, ueventd, and vold)." /></a></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>As stated at the beginning of this post, the sample obtained was discovered in the preparatory stages of the attack. Unfortunately, it did not include the final payload that would have been deployed with this exploit.</span></p><h1 class="vdnyYxSRCi-c8 vdnyYxSRCi-c28" id="h.5zhj1ffk4us0"><span class="vdnyYxSRCi-c31">Conclusion</span></h1> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>This in-the-wild exploit chain is a great example of different attack surfaces and “shape” than many of the Android exploits we’ve seen in the past. All three vulnerabilities in this chain were in the manufacturer’s custom components rather than in the AOSP platform or the Linux kernel. It’s also interesting to note that 2 out of the 3 vulnerabilities were logic and design vulnerabilities rather than memory safety. Of the </span><span class="vdnyYxSRCi-c24"><a class="vdnyYxSRCi-c61" href="https://docs.google.com/spreadsheets/d/1lkNJ0uQwbeC1ZTRrxdtuPLCIl7mlUreoKfSIgajnSyY/edit#gid=1190662839">10 other Android in-the-wild 0-days</a></span><span class="vdnyYxSRCi-c7"> that we’ve tracked since mid-2014, only 2 of those were not memory corruption vulnerabilities.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>The first vulnerability in this chain, the arbitrary file read and write, CVE-2021-25337, was the foundation of this chain, used </span><span>4</span><span> different times and used at least once in each step. The vulnerability was in the Java code of a custom content provider in the </span><span class="vdnyYxSRCi-c2">system_server</span><span class="vdnyYxSRCi-c7">. The Java components in Android devices don’t tend to be the most popular targets for security researchers despite it running at such a privileged level. This highlights an area for further research.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">Labeling when vulnerabilities are known to be exploited in-the-wild is important both for targeted users and for the security industry. When in-the-wild 0-days are not transparently disclosed, we are not able to use that information to further protect users, using patch analysis and variant analysis, to gain an understanding of what attackers already know. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span class="vdnyYxSRCi-c7">The analysis of this exploit chain has provided us with new and important insights into how attackers are targeting Android devices. It highlights a need for more research into manufacturer specific components. It shows where we ought to do further variant analysis. It is a good example of how Android exploits can take many different “shapes” and so brainstorming different detection ideas is a worthwhile exercise. But in this case, we’re at least 18 months behind the attackers: they already know which bugs they’re exploiting and so when this information is not shared transparently, it leaves defenders at a further disadvantage. </span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8"><span>This transparent disclosure of in-the-wild status is necessary for both the safety and autonomy of targeted users to protect themselves </span><span>as well as</span><span> the security industry to work together to best prevent these 0-days in the future.</span></p> <p class="vdnyYxSRCi-c1 vdnyYxSRCi-c8 vdnyYxSRCi-c14"><span class="vdnyYxSRCi-c7"></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'> <meta content='https://www.blogger.com/profile/08975904405228580347' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/08975904405228580347' rel='author' title='author profile'> <span itemprop='name'>Google Project Zero</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html' itemprop='url'/> <a class='timestamp-link' href='https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2022-11-04T08:50:00-07:00'>8:50 AM</abbr></a> </span> <span class='post-comment-link'> <a class='comment-link' href='https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html#comment-form' onclick=''> No comments: </a> </span> <span class='post-icons'> <span class='item-control blog-admin pid-1053444070'> <a href='https://www.blogger.com/post-edit.g?blogID=4838136820032157985&postID=5910791167813767823&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=5910791167813767823&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=5910791167813767823&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=5910791167813767823&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=5910791167813767823&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=5910791167813767823&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=2023-01-12T08:59: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=2022-11-04T08:50:00-07:00&max-results=1' id='Blog1_blog-pager-older-link' title='Older Posts'>Older Posts</a> </span> <a class='home-link' href='https://googleprojectzero.blogspot.com/'>Home</a> </div> <div class='clear'></div> <div class='blog-feeds'> <div class='feed-links'> Subscribe to: <a class='feed-link' href='https://googleprojectzero.blogspot.com/feeds/posts/default' target='_blank' type='application/atom+xml'>Posts (Atom)</a> </div> </div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget BlogSearch' data-version='1' id='BlogSearch1'> <h2 class='title'>Search This Blog</h2> <div class='widget-content'> <div id='BlogSearch1_form'> <form action='https://googleprojectzero.blogspot.com/search' class='gsc-search-box' target='_top'> <table cellpadding='0' cellspacing='0' class='gsc-search-box'> <tbody> <tr> <td class='gsc-input'> <input autocomplete='off' class='gsc-input' name='q' size='10' title='search' type='text' value=''/> </td> <td class='gsc-search-button'> <input class='gsc-search-button' title='search' type='submit' value='Search'/> </td> </tr> </tbody> </table> </form> </div> </div> <div class='clear'></div> </div><div class='widget PageList' data-version='1' id='PageList1'> <h2>Pages</h2> <div class='widget-content'> <ul> <li> <a href='https://googleprojectzero.blogspot.com/p/about-project-zero.html'>About Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/working-at-project-zero.html'>Working at Project Zero</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/0day.html'>0day "In the Wild"</a> </li> <li> <a href='https://googleprojectzero.github.io/0days-in-the-wild/rca.html'>0day Exploit Root Cause Analyses</a> </li> <li> <a href='https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html'>Vulnerability Disclosure FAQ</a> </li> </ul> <div class='clear'></div> </div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Archives</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(9)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2024/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2023/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate 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/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 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/2022/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> <ul class='posts'> <li><a href='https://googleprojectzero.blogspot.com/2022/11/mind-the-gap.html'>Mind the Gap</a></li> <li><a href='https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html'>A Very Powerful Clipboard: Analysis of a Samsung i...</a></li> <li><a href='https://googleprojectzero.blogspot.com/2022/11/gregor-samsa-exploiting-java-xml.html'>Gregor Samsa: Exploiting Java's XML Signature Veri...</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/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 collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(24)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='https://googleprojectzero.blogspot.com/2021/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate 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/984859869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY4RLk52tVEvD7Hm7rpTK61ruk-wTg:1732528852099';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d4838136820032157985','//googleprojectzero.blogspot.com/2022/11/','4838136820032157985'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '4838136820032157985', 'title': 'Project Zero', 'url': 'https://googleprojectzero.blogspot.com/2022/11/', 'canonicalUrl': 'https://googleprojectzero.blogspot.com/2022/11/', 'homepageUrl': 'https://googleprojectzero.blogspot.com/', 'searchUrl': 'https://googleprojectzero.blogspot.com/search', 'canonicalHomepageUrl': 'https://googleprojectzero.blogspot.com/', 'blogspotFaviconUrl': 'https://googleprojectzero.blogspot.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': false, 'httpsEnabled': true, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-240546891-1', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Project Zero - RSS\x22 href\x3d\x22https://googleprojectzero.blogspot.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Project Zero - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/4838136820032157985/posts/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/da8f33dd880cc4f1', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'archive', 'pageName': 'November 2022', 'pageTitle': 'Project Zero: November 2022'}}, {'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/2022/11/', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': true, 'isLabelSearch': false, 'archive': {'year': 2022, 'month': 11, 'rangeMessage': 'Showing posts from November, 2022'}}}]); _WidgetManager._RegisterWidget('_NavbarView', new _WidgetInfo('Navbar1', 'navbar', document.getElementById('Navbar1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2646514562-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch1', 'sidebar-right-1', document.getElementById('BlogSearch1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_PageListView', new _WidgetInfo('PageList1', 'sidebar-right-1', document.getElementById('PageList1'), {'title': 'Pages', 'links': [{'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/about-project-zero.html', 'id': '4384467920505278144', 'title': 'About Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/working-at-project-zero.html', 'id': '2459334498880008057', 'title': 'Working at Project Zero'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/0day.html', 'id': '3414239791814532209', 'title': '0day \x22In the Wild\x22'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.github.io/0days-in-the-wild/rca.html', 'title': '0day Exploit Root Cause Analyses'}, {'isCurrentPage': false, 'href': 'https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html', 'id': '2935252455704572784', 'title': 'Vulnerability Disclosure FAQ'}], 'mobile': false, 'showPlaceholder': true, 'hasCurrentPage': false}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>