See https://owasp.slack.com/archives/C255XSY04/p1649868619646339?thread_ts=1649868619.646339&cid=C255XSY04 for my 😭 for help on the matter...
I recommend to use an existing image for now, because these kinds of problems sometimes take a while to track down.
develop
branch by pinning a dependency that seems to have been broken earlier today.
Hi Bjoern - could you please help me out.
I am running JuiceShop on port 3000 in a local built (via "npm start") on my Macbook Air M1.
When I try to change Bender's password (https://incognitjoe.github.io/hacking-the-juice-shop.html) without SQLi I use this command as Joe Butler does:
http://localhost:3000/rest/user/change-password?new=slurmCl4ssic&repeat=slurmCl4ssic
I get an error: OWASP Juice Shop (Express ^4.17.1) 500 Error: Blocked illegal activity by ::ffff:127.0.0.1
I tried to run it via the Docker and I get the same error. Is there anything I am doing wrong. I could imagine that the docker may give issues for a Mac (especially M1), but running a local built on port 3000 shouldn't as it's truly all coming from the same origin.
Your help is much appreciated!
Hi Bjoern,
I have run the official code now as suggested.
I first logged in as Bender for all of the below.
The "Bonus Round: Delivering the attack via reflected XSS" works (hereafter, Reflected XSS). However, when I paste the link http://localhost:3000/rest/user/change-password?new=B&repeat=B to probe the response, the document writes I should get a 200 response, but I get the (500) error:
"OWASP Juice Shop (Express ^4.17.1)
500 Error: Blocked illegal activity by ::ffff:127.0.0.1"
I intercepted both requests (OWASP ZAP) and what I noticed is that when I use the link: http://localhost:3000/rest/user/change-password?new=B&repeat=B to probe the request, it misses the “Authorization: Bearer=“. Of course, the Bearer token is explicitly added in the Reflected XSS, but shouldn’t this anyway work as I have the Cookie set?
Again, many thanks for clarifying!
Below the two requests (I cut off the token with '...')
Response probe: http://localhost:3000/rest/user/change-password?new=B&repeat=B (500 error)
GET http://localhost:3000/rest/user/change-password?new=B&repeat=B HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Cookie: language=en; cookieconsent_status=dismiss; token=eyJ0eXAi... Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Reflected XSS (accepted)
GET http://localhost:3000/rest/user/change-password?new=slurmCl4ssic&repeat=slurmCl4ssic HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: /
Accept-Language: en-US,en;q=0.5
Authorization: Bearer=eyJ0eXAi...
Connection: keep-alive
Referer: http://localhost:3000/
Cookie: language=en; cookieconsent_status=dismiss; token=eyJ0eXAi...
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
If-None-Match: W/"160-vmzOVmQOjT3XgVHN9jcuyyJLso4"
I suppose for completeness sake, one would have to add to the document (https://pwning.owasp-juice.shop/appendix/solutions.html#change-benders-password-into-slurmcl4ssic-without-using-sql-injection-or-forgot-password) that for probing the change-password API endpoint with http://localhost:3000/rest/user/change-password?new=B&repeat=B one needs add the JWT token as an Authorization Bearer Token because otherwise the request will fail with 500 error?
To do that, run:
http://localhost:3000/rest/user/change-password?new=B&repeat=B
And then intercept the request with Burpsuite or OWASP and add the following header information:
Authorization: Bearer=<YOUR JWT>
You find your JWT token in the cookie of the request you intercepted.
npm install
, then npm start
and that doesn't work when done from VSC's terminal?
npm start
again.
Hey I have a question: why does the </script> allow us to break out of the variable context to close the <script> as seen here. http://brutelogic.com.br/xss.php?c1=</script><svg onload=alert(1)>
I am interested in understanding the exact mechanics of the JS engine that do that. Many thanks!
@bkimminich:matrix.org
Hi Bjoern,
I am trying to understand Juice Shop's santize-html 1.4.2 vulnerability in detail and I went through the threads of you, Thomas Boutell and the htmlparser2 person (forgot his name). I also looked at the npm pages of santize-html and htmlparser2. Is the following description of the vulnerability correct. Thank you very much for explaining this for those that are interested and thank you for your time - I am certainly not taking it for granted. If there is a write up somewhere, I would appreciate if you could point me in the right direction.
This is the payload: <<img src="javascript:evil"/>img src="javascript:evil"/> that exploits sanitize-html 1.4.2.
The sanitize-html package uses htmlparser2 under the hood for reference.
If I understand correctly, htmlparser2 first fires the opentag when it encounters the <img tag and ignores the first opening tag <. After it hits the closing tag />, it removes the entire <img src="javascript:evil"/> tag. Then htmlparser2 treats the rest of the payload as text. The text part in our case is: < and the img src="javascript:evil"/> part which results in a final text of <img src="javascript:evil"/> that gets passed on to sanitize-html as such.
Because sanitize-html believes that the text received from htmlparser2 is encoded, sanitize-html trusts it and does no further encoding. To be safe, sanitize-html would have had to HTML entity encode the text received from htmlparser2, but it didn't. And since sanitize-html needs to keep the text in the document, it ends up outputting the text as follows <img src="javascript:evil"/> which results in the vulnerability. I also think that the <img tag would not get picked up by sanitize-html because users frequently allow the img tag. That's why is img tag is not allowed by default in sanitize-html (at least anymore).
To remidy the problem, htmlparser2 changed the default setting of decodeEntity to true, indicating that anything received from htmlparser2 is in decoded form needs to be encoded, again. Hence, this vulnerability was a sanitize-html vulnerability caused by inproper use of the htmlparser2 package.
Is this a fair characterization or am I mistaken?
Many thanks again,
Andres
Regarding the Zip Slip exploit of the /promotion video subtitles. I am creating the file as explained using a Macbook:
zip exploit.zip ../../frontend/dist/frontend/assets/public/video/owasp_promo.vtt
When I run unzip -vl exploit.zip it shows me the file has exactly the right path.
When I upload this exploit.zip with the payload on /#/complain, I get a response this response on the /api/Complaints request:
{
"status": "success",
"data": {
"id": 6,
"UserId": 1,
"message": "ffff",
"updatedAt": "2022-04-29T21:38:14.767Z",
"createdAt": "2022-04-29T21:38:14.767Z",
"file": null
}
}
I tried various browsers. Am I missing something major - I can't seem to figure it out!
Can anyone help me with making the exploit.zip file for the promotion viode challenge? Much appreciated!
What is still odd is that I cannot produce the videoExploit.zip.
What I am doing is this:
Create the path with: mkdir -p ../../frontend/dist/frontend/assets/public/video
touch owasp_promo.vtt and add the </script><script>alert(xss)</script>
Go to : /frontend/dist/ and execute:
zip exploit.zip ../../frontend/dist/frontend/assets/public/video/owasp_promo.vtt
When I upload the file I get this error so it must be the way I make the file:
Error: ENOENT: no such file or directory, open 'uploads/complaints/../../frontend/dist/frontend/assets/public/video/owasp_promo.vtt'
Hey @bkimminich:matrix.org => thanks again for the help! This has nothing to do with docker vs native Juice Shop - I got it to work in both now. I think it's a simple typo in the solutions. I solved the challenge making my own .zip file now. The directory traversal provided in solutions (https://pwning.owasp-juice.shop/appendix/solutions.html) is wrong - the '/video' directory is supposed to be '/videos': zip exploit.zip ../../frontend/dist/frontend/assets/public/video/owasp_promo.vtt is wrong.
This is the correct directory traversal: zip exploit.zip ../../frontend/dist/frontend/assets/public/videos/owasp_promo.vtt
Appreciate the help!
While the zip slip exploit works, it's a mystery how one would come to the conclusion to upload the payload.zip to ../../frontend/dist/frontend/assets/public/videos/ folder?
I am not familiar with AngularJS, but after doing quite a bit of searching, I cannot find any documents that would allow you to come up with this path. It cannot be brute forced either. So without looking at the solution, how would you find that path? If this was a real life webapp, how would you figure out the path to upload to?
Many thanks!
Hey - I have been trying to use Python requests to deal with Juice Shop. A very simple thing doesn't seem to work. Any help would be very much appreciated. BTW, the goal is to send a GET request to search?q=<script>alert(1)</alert> so to make that happen I first need to check whether I can access the page/html tags.
Is it because of this? https://stackoverflow.com/questions/36854623/requests-content-not-matching-with-chrome-inspect-element or more accurately is it because it is using Ajax and that is not captured by requests_html?
Many thanks
from requests_html import HTMLSession
with HTMLSession() as c:
test = 'http://localhost:3003/'
r = c.get(test)
html = BeautifulSoup(r.html.html, "html.parser")
print(html.prettify())
This returns some html but it's not complete. It is the index file (see dev tools), but there is plenty of stuff missing. For example, it doesn't include the "Search Results" string. What I get back is this:
<!--
~ Copyright (c) 2014-2022 Bjoern Kimminich & the OWASP Juice Shop contributors.
~ SPDX-License-Identifier: MIT
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>
OWASP Juice Shop
</title>
<meta content="Probably the most modern and sophisticated insecure web application" name="description"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<link href="assets/public/favicon_js.ico" id="favicon" rel="icon" type="image/x-icon"/>
<link href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.css" rel="stylesheet" type="text/css"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js">
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js">
</script>
<script>
window.addEventListener("load", function(){
window.cookieconsent.initialise({
"palette": {
"popup": { "background": "#546e7a", "text": "#ffffff" },
"button": { "background": "#558b2f", "text": "#ffffff" }
},
"theme": "classic",
"position": "bottom-right",
"content": { "message": "This website uses fruit cookies to ensure you get the juiciest tracking experience.", "dismiss": "Me want it!", "link": "But me wait!", "href": "https://www.youtube.com/watch?v=9PnbKL3wuH4" }
})});
</script>
<style>
.bluegrey-lightgreen-theme.mat-app-background{background-color:#303030;color:#fff}@charset "UTF-8";@media screen and (-webkit-min-device-pixel-ratio:0){}
</style>
<link href="styles.css" media="print" onload="this.media='all'" rel="stylesheet"/>
<noscript>
<link href="styles.css" rel="stylesheet"/>
</noscript>
</head>
<body class="mat-app-background bluegrey-lightgreen-theme">
<app-root>
</app-root>
<script src="runtime.js" type="module">
</script>
<script src="polyfills.js" type="module">
</script>
<script src="vendor.js" type="module">
</script>
<script src="main.js" type="module">
</script>
</body>
</html>