Explore The Latest in Cybersecurity

Blog / Configuring Burp Suite for Testing Vaadin-based Applications
Configuring Burp Suite for Testing Vaadin-based Applications
Norbert Bajko
February 7, 2025
vaadinburp suitepentest

Introduction

Modern web applications are often developed using frameworks, which may occasionally result in unexpected behaviours during security testing. One of the most common causes for this is when the framework implements some sort of synchronization or viewstate-like behavior. This implies that any alteration to the user interface, such as entering text in an input field, generates a request, whereby the browser transmits data regarding the changes made to the user interface. It should be noted that in some cases, these aspects may not be critical to the functioning of the application. In such instances, the application may still operate correctly even if a request is lost or there are no incremental IDs in the requests.

In the case of the Vaadin framework, these synchronization values are crucial, as otherwise the UI will reload and the state will be lost. This indicates that if the server does not receive the anticipated IDs, it will terminate the current UIDL session and require the application UI to be reloaded, along with a reset of the synchronization IDs. This introduces complexities to the security testing process, as these IDs must be updated with each request. If a tester does not take this into account and simply initiates a scan, it will be ineffective, as it will simply result in requests being sent with an already terminated session.

Technical Details

Observing the communication it was revealed that the application is using the Vaadin framework. It had several differences compared to “regular” communication used in web applications. More details about Vaadin communication: https://mvysny.github.io/Vaadin8-communication-explained/

  • Use of UIDL - It’s basically a constant communication between the client and the server, that keeps track of every change on the UI. Whether you enter text in a search box, or you click a button, it sends a request to the server, which keeps track of every change on the UI
  • Use of ‘clientId’ and ‘syncId’, which are basically integer values that keep incrementing and are responsible for message ordering.

This means that if we want to use for example Burp Repeater, we can’t just resend a request, because the application will lose track of the message ordering and we’ll have to reload our application the browser.

How do you notice this behaviour in the first place? For example, in case of a search function, when searching for the x-pentest user, the correct Content-Length of the response is about 1800. If we’re not sending the correct syncId and clientId, the response is a larger general response. The below shows a request, where these parameters were not updated correctly.

POST /appname/UIDL/?v-uiId=4 HTTP/1.1
Host: redactedhost.com
***TRUNCATED***
Connection: close

{"csrfToken":"c2516794-1ec7-4f33-95c4-208494e9cfdc","rpc":[["817","v","v",["positionx",["i",1473]]],["817","v","v",["positiony",["i",47]]],["820","v","v",["c",["i",9]]],["820","v","v",["curText",["s","x-pentest"]]]],"syncId":3,"clientId":3}

Response:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json;charset=utf-8
Cache-Control: no-cache
Content-Length: 18956

for(;;);[{"syncId": 6, "resynchronize": true, "clientId": 4, "changes" : [["change",{"pid":"744"},["0",{"id":"744","location":"https:\/\/redactedhost.com\/appname\/#\/index\/index"}]],["change",{"pid":"817"},["35",{"id":"817","v":{"action":""}},["actions",{},["action",{"key":"1","kc":27,"mk":[]}]]]],["change",{"pid":"820"},["36",{"id":"820","iem":"LAZY","iet":400,"nvc":true}]],["change",{"pid":"773"},["1",{"id":"773","ormoh":false,"usehtml":true},["options",{}],["items",{},["item",{"id":2,"text":"Admin PenTest (pentest)","command":true,"icon":"https:\/\/redactedhost.com\/profilepic?id=4"}]],["itemIds",{},["item",{"id":"2","componentId":""}]]]],["change",{"pid":"760"},["1",{"id":"760","ormoh":true,"usehtml":true},["options",{}],["items",{},["item",{"id":2,"text":"Admin PenTest","command":true,"icon":"https:\/\/redactedhost.com\/profilepic?id=4"},["item",{"id":3,"text":"My profile","command":true,"icon":"fonticon:\/\/FontAwesome\/f182"}],["item",{"id":4,"separator":true}],["item",{"id":5,"text":"Substituted persons","disabled":true}],["item",{"id":6,"style":"substitution","text":"Admin ","command":true,"icon":***TRUNCATED****

The following request was sent with the correct IDs set in the previous response.

Request:

POST /appname/UIDL/?v-uiId=4 HTTP/1.1
Host: redactedhost.com
***TRUNCATED***
Connection: close

{"csrfToken":"c2516794-1ec7-4f33-95c4-208494e9cfdc","rpc":[["817","v","v",["positionx",["i",1473]]],["817","v","v",["positiony",["i",47]]],["820","v","v",["c",["i",9]]],["820","v","v",["curText",["s","pentest"]]]],"syncId":6,"clientId":4}

Response:

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json;charset=utf-8
Cache-Control: no-cache
Content-Length: 1889

for(;;);[{"syncId": 8, "clientId": 5, "changes" : [], "state":{"821":{"childData":{"835":{"alignmentBitmask":5,"expandRatio":0}}},"835":{"styles":["i2-quicksearch-result-block"]},"836":{"childData":{"837":{"alignmentBitmask":5,"expandRatio":0},"838":{"alignmentBitmask":5,"expandRatio":1}},"width":"280.0px","styles":["i2-cursor-pointer"],"registeredEventListeners":["lClick"]},"837":{"description":"Assistant PenTest","styles":["i2-profile-picture-compact","i2-margin-vertical-small"],"resources":{"source":{"uRL":"https://redactedhost.com/profilepic?id=4"}}},"838":{"childData":{"839":{"alignmentBitmask":5,"expandRatio":0},"840":

By using the correct syncId and clientId, the application responded with the correct data. It’s possible to automatically update the values by grabbing the syncId and clientId values from the response using the TokenJar Burp extension. https://portswigger.net/bappstore/d9e05bf81c8f4bae8a5b0b01955c5578

We can use the following regexes:

  • "syncId"\s*:\s*(\d+)
  • "clientId"\s*:\s*(\d+)

Even though there’s no checkbox for Scanner, by using these settings, Burp will automatically update the values during a scan too:

This allows us to use the burp scanning for Vaadin applications.

Conclusion

It is essential that penetration testers and bug hunters pay close attention to these behaviours to avoid overlooking crucial bugs and issues.

A couple of suggestions:

  • Monitor the progress of a launched scan to check whether something brokes the session
  • Check for existing Burp extensions that might be useful for your case
  • Hackvertor is a great one that’s useful for a lot of cases
  • If it’s a really specific case then use the Burp Extender API to write your own extension

Explore the Latest in Cybersecurity

Stay ahead of cyber threats with insights from the Naunet blog. Our experts share their knowledge on the latest cyber defense trends, techniques, and technologies. Whether you want to deepen your understanding or apply new strategies, our blog is your go-to resource for reliable, expert-backed content in the cybersecurity domain. Join our community of professionals and elevate your security posture with every post.