See blog Menu
Make it Crash! Fuzzing Web Applications

lenin.alevski | July 1st, 2019


Companies dream of building apps that run without errors and give their users the freedom to do whatever they want. This requires removing validations, data-type constraints, and accepting various user inputs and formats to represent the same information, all while ensuring the application won’t crash and burn.

So, how does this work? There’s a step during the software development lifecycle dedicated to ensuring product quality, which means developers need to write tests (unit testing, integration testing, manual testing, etc.) that ensure the application won’t crash in unexpected situations. Writing these kinds of tests is sometimes difficult, because we need to anticipate any scenario in which users might interact with an application. This is where Fuzzing helps detect flaws in application logic.

The first step to fuzzing an application begins with identifying all the ways a user can input information to the service. Tools like Postman simplify this task, but also having access to service endpoint documentation is even better!

Postman UI for sending requests to Service endpoint

It’s clear how users can send information to the service by using the headers and parameters in the HTTP request.

This service already has some logic in place for handling errors. It displays nice, informative messages instead of an ugly, and potentially risky (because of an information disclosure), backtrace derived from an unhandled exception.

class NetsuiteProxyService < Sinatra::Base

  set :show_exceptions, false

  ...
  ...
  ...

  get "/get_roles" do
    content_type :json
    roles = client.list_select_values_for(
      record_type: 'employee',
      sublist: 'rolesList',
      field: 'selectedRole',
      page_index: 1  
    )
    return {
      'roles' => roles
    }.to_json
  end

  error do
    content_type :json
    status 400
    {
      error: env['sinatra.error'].message
    }.to_json
  end

end

At this point, it’s possible the development team already had some tests in place in order to check for the Happy, Sad, and Bad paths. These tests may return between two hundred and four hundred status responses, but never five hundred. However, the goal of Fuzzing is to go beyond that—we want to throw as much garbage as possible at the service and see what happens or breaks.

GET /get_roles HTTP/1.1
Host: localhost:3000
NS-account: account-id
NS-consumer_key: consumer-key
NS-consumer_secret: consumer-secret
NS-token_id: token-id
NS-token_secret: token-secret
NS-email: example@email.com
NS-password: supersecretpassword
NS-API_VERSION: 2
NS-WSDL_DOMAIN: example.com
NS-WSDL: example.com/wsdl
User-Agent: PostmanRuntime/7.15.0
Accept: */*
Cache-Control: no-cache
Postman-Token: f7567113-0ee5-4410-ba21-0be8397895f3,2a1632b5-3c6b-4ef2-b56d-bd3b513da4fa
Host: localhost:3000
cookie: session=COOKIE
accept-encoding: gzip, deflate
Connection: keep-alive
cache-control: no-cache


We do this by writing a simple python script using the HTTP request generated by Postman (or whatever other tool) and a FOR LOOP, on each iteration we change the values for the headers and parameters using different types of payloads.

Choosing the Payloads

The type of payloads we use during the application fuzzing is important. I recommend downloading SecLists. SecList is a compilation of lists that include different usernames, passwords, SQL injection and XSS payloads, known path and files, etc.

The Fuzzing section contains many lists with the most horrendous (and evil) payloads you can feed the application.

Fuzzing section

Automatic Fuzzing with Burp Suite

You can do all of this manually, but it’s sometimes necessary to Fuzz hundreds of endpoints. That’s simply too much work. If faced with that scenario, we can automate the Fuzzing process with a tool like Burp Suite, specifically using the Intruder module.

Burp Suite Payload positions

In Burp Suite, we can choose which parts of the requests we want to “rotate” using different payloads. By default, Burp Suite suggests some of them like parameters and the session cookie, but we can add or delete existing ones.

Burp Suite payload options

Next, we need to choose which payload lists to use. We can use the ones we download from the SecList repository. In this example, I’m using SQLi and XSS Polyglots.

Polyglots payload list

Once everything is configured, we can start the intruder attack. Burp Suite will send garbage to our service endpoint, showing the request headers/parameters and the obtained response.

Three payloads that crashed the server

A quick way to see if something went wrong is to look at the response status and length. An abnormally long length indicates some form of information disclosure from the server. In this example, there are three payloads that crashed the service, specifically on the NS-WSDL header.

NS-WSDL: SLEEP(1)%20%2f%2a‘%20or%20SLEEP(1)%20or%20‘“%20or%20SLEEP(1)%20or%20“%2a%2f

NS-WSDL: IF(SUBSTR(@@version,1,1)%3c5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))%2f%2a'XOR(IF(SUBSTR(@@version,1,1)%3c5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'%7c%22XOR(IF(SUBSTR(@@version,1,1)%3c5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),​SLEEP(1)))OR%22%2a%2f

NS-WSDL: “%20onclick%3dalert(1)%2f%2f%3cbutton%20‘%20onclick%3dalert(1)%2f%2f%3e%20%2a%2f%20alert(1)%2f%2f

Backtrace error

This backtrace can disclose details about the server environment path where the application and usernames exist. We can take a look at the service log to confirm the application crashed and learn that the error occurred within the function that handles the errors, which is ironic.

Service log

This seems to be an issue with encoding, specifically in how Ruby converts objects (Hashes) to JSON format. We proceed to create a fix for this issue and now the error no longer occurs.

 error do
   content_type :json
   message = env['sinatra.error'].message || ''
   status 400
   {
     error: message.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
   }.to_json
 end


Final thoughts

Fuzzing is commonly associated with penetration testing and exploit development, but it’s a testing technique that—if used correctly—can help development teams deliver more robust (less crashes) applications while helping the company identify potential vulnerabilities at early stages.

Happy hacking!


OneLogin blog author

Lenin Alevski is a Full Stack Engineer at OneLogin. Before joining OneLogin, Lenin worked at Oracle and Websec Mexico as a security consultant and penetration tester, then joined Freeagent crm as founding member in the engineering team. He is very passionate about privacy, CTFs, blockchain and home automation. He also enjoys giving talks and workshops about security in many events and local universities. Follow Lenin on Twitter @alevskey.