Name is required.
Email address is required.
Invalid email address
Answer is required.
Exceeding max length of 5KB

Design new extension - Problem with buildRequest and URL Encode

Antoni mortarella Mar 23, 2018 12:11PM UTC

Hi!

I'm new to extending Burp and I wanted to add an active scanner plugin for some injections.

When I making the requests with a payload with special characters, for example <script>alert(1)</script>, the request encoded my payload with "URL encode".

My code is as follows:

for(String payload: payloads){
IHttpRequestResponse test = this.callbacks.makeHttpRequest(httpService,insertionPoint.buildRequest(helpers.stringToBytes(payload)));
}

when I make "insertionPoint.buildRequest(helpers.stringToBytes(payload))" my payload is encoded in" URL encode", I think is because according to the documentation:

https://portswigger.net/burp/extender/api/burp/IScannerInsertionPoint.html#buildRequest(byte[])

"Note: Scan checks should submit raw non-encoded payloads to insertion points, and the insertion point has responsibility for performing any data encoding that is necessary given the nature and location of the insertion point."

How could I send the request without encoding anything?

For example, if I send <script>alert(1)</script>, the request should be:

GET / ...
....

param=<script>alert(1)</script>

and not:

GET / ...
....

param=%3cscript%3ealert(1)%3c%2fscript%3e


Thanks a lot!


Paul Johnston Mar 23, 2018 03:57PM UTC Support Center agent

Hi Antoni,

Thanks for your message. You’re the second person this week who’s asked for this, so I’ve written you a little snippet:

- https://gist.github.com/pajswigger/c1fff3ce6e5637126ff92bf57fba54e1

This should get you going for now, and I’d be interested to hear how you get on.

We’ll have a discussion internally about making something like this part of the API.

Please let us know if you need any further assistance.


Antoni mortarella Mar 27, 2018 07:45AM UTC
Hi!

I explain a little better my code and what I intend to:

Override the doActiveScan function, calling another class:

@Override
public List<IScanIssue> doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint){

....
ActiveScan as = new ActiveScan(callbacks,data);

return as.doAScan(baseRequestResponse, insertionPoint);
....
}


In the ActiveScan class, I define the doScan function and call insertionPoint.buildRequest:

public List<IScanIssue> doAScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint){

...
for(String payload: payloads){
IHttpRequestResponse response = this.callbacks.makeHttpRequest(httpService,insertionPoint.buildRequest(helpers.stringToBytes(payload)));
IScanIssue matches = getMatches(...);
...
}


The problem, for example to look for XSS, is that if you encode the payload in url encode, you can not try to skip filters, using different techniques. Because according to the documentation:

https://portswigger.net/burp/extender/api/burp/IScannerInsertionPoint.html#buildRequest(byte[])

"Note: Scan checks should submit raw non-encoded payloads to insertion points, and the insertion point has responsibility for performing any data encoding that is necessary given the nature and location of the insertion point."

The problem is that it is the insertionPoint.buildRequest function that decides whether to encode the payload or not. For example, in the referer field, it does not encode it:

POST /a.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: <script>alert(1)</script>
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 164

gender=aaaa&class=aaa




but in the class parameter, it does encode it:

POST /a.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/a.php
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 53

gender=aaaa&class=%3cscript%3ealert(1)%3c%2fscript%3e


Thanks a lot!

Paul Johnston Mar 27, 2018 01:38PM UTC Support Center agent

Hi Antoni,

Thanks for following up. I understand what you want to do and it seems sensible. If you try out the snippet I sent, you can replace:

insertionPoint.buildRequest(helpers.stringToBytes(payload))

With:

new BuildUnencodedRequest(helpers).buildUnencodedRequest(insertionPoint, helpers.stringToBytes(payload))

This is something we’d like to support in the core API. I’ve captured your requirement in a user story. We’ll probably not act on this in the short term, but in future when we revise the extender API, we’ll try to incorporate this.

Do let me know how you get on with the snippet.


Antoni mortarella Apr 03, 2018 06:52AM UTC
Hi Paul,

Finally it seems that with the code that you have passed me works for the case that I want. Thank you very much!

On the other hand, is there any way to use the encoding methods here?

https://portswigger.net/burp/help/intruder_payloads_processing


The helpers.urlEncode (String) function does not work for me, I had to use URLEncoder.encode (String, "UTF-8").

Greetings.

Paul Johnston Apr 03, 2018 08:07AM UTC Support Center agent

Hi Antoni,

Good to hear that is works for you. It’s currently not possible to extensions to use Intruder payload encoding. An extension can provide encoding using the IIntruderPayloadProcessor interface, but it’s not possible to use existing ones.

And yes, helpers.urlEncode assumes the ISO-8859-1 charset, so if you need to use utf-8 you’re better using Java encoding functions.


Antoni mortarella Apr 05, 2018 07:03AM UTC
Hi Paul,

The function of BuildUnencodedRequest can fail when the payload that send has a small length, for example "aa", gives the following error:

SEVERE: null
java.lang.Exception: Multiple canary found in request


Could it be solved by putting a canary of fixed length, for example 24 characters?

Thank you,
Regards!

Paul Johnston Apr 05, 2018 07:06AM UTC Support Center agent

Hi Antoni,

That could work. The only issue is you may need to fix up the Content-Length header. Are you ok to have a go at this? Let me know if you want me to tweak the code.


Antoni mortarella Apr 05, 2018 08:18AM UTC
Hi Paul!

It seems that this works fine without updating the Content-Length header:

byte[] buildUnencodedRequest(IScannerInsertionPoint iScannerInsertionPoint) throws Exception
{
byte[] payload = helpers.stringToBytes("TestStringPayload");
byte[] canary = buildCanary(payload.length);
byte[] request = iScannerInsertionPoint.buildRequest(canary);
int canaryPos = findCanary(canary, request);
System.arraycopy(payload, 0, request, canaryPos, payload.length);
return request;
}


What do you think?

Regards!

Paul Johnston Apr 05, 2018 08:23AM UTC Support Center agent

Hi Antoni,

Yeah, that works because iScannerInsertionPoint.buildRequest updates the content length. But it the payload length doesn’t match the canary length, it will set it to the wrong value.


Antoni mortarella Apr 05, 2018 09:38AM UTC
Hi Paul!,

With more tests it seems that it does not work well, as it not only uses the length of the payload, but also the payload itself ... I do not see how it could work for payloads of length 1, 2 or 3 ... and in some cases with more than 3 could also fail ...

Thank you,
Regards!

Paul Johnston Apr 05, 2018 10:04AM UTC Support Center agent

Hi Antoni,

I’ve updated the gist to use a slightly different technique. Rather than use a random canary, it uses this character § which is unlikely to be in a regular request. This may work a little better for you.

Unfortunately, this is just a hacky workaround, so some problems should be expected. I think we will eventually update the API to support this, but that will take a little while.


Antoni mortarella Apr 05, 2018 02:11PM UTC
Hi Paul,

We will have to use another character, with this it tells me:

- incompatible types: possible loss and conversion from char to byte.

I think it's because:

- char is unsigned (has a range of 0 to 2 ^ 16 - 1), so -1 is not within its range.

- byte, on the other hand, is signed, and has a range of -128 to 127.

I will use the "$" character

On the other hand, I will do:

for (String grep: greps) {
                    for (String payload: payloads) {
                        if (payload.length ()> = 2) {
                            IHttpRequestResponse response = this.callbacks.makeHttpRequest (httpService, new BuildUnencodeRequest (helpers) .buildUnencodedRequest (insertionPoint, helpers.stringToBytes (payload)));
                            ...
                        } else {
                            IHttpRequestResponse response = this.callbacks.makeHttpRequest (httpService, insertionPoint.buildRequest (helpers.stringToBytes (payload)));
                            ...
                        }
                    }


Thank you so much for everything!!
Greetings.

Post Your public answer

Your name
Your email address
Answer