• Non ci sono risultati.

Applying fuzzing techniques to automate testing of the DCV remotization protocol

N/A
N/A
Protected

Academic year: 2021

Condividi "Applying fuzzing techniques to automate testing of the DCV remotization protocol"

Copied!
93
0
0

Testo completo

(1)

DEPARTMENT OF COMPUTER SCIENCE Master Degree in Computer Science

Applying fuzzing

techniques to automate testing of the

DCV remotization protocol

Tutor:

Prof.ssa Laura Semini

Ing. Silvio Lazzeretti

Candidate:

Italo Guerrieri

(2)
(3)

Abstract

Fuzz testing was initially developed by Barton Miller [1] at the University of Wisconsin in 1989. Fuzzing is an automated or semi-automated software testing technique used to discover coding errors and security flaws by in-putting invalid or random data. The system is then monitored to check if any failure arises during the fuzzing sessions.

This thesis will present what I have done during the four months intern-ship, started in November 2017, that took place in NICE, an Amazon Web Services company. NICE has its headquarter in Asti and a branch in Pula, near Cagliari. The company is composed of three teams, each of them work-ing on a swork-ingle NICE product. The products in question are EnginFrame, Desktop Cloud Visualization (DCV) and Amazon Elastic GPU. During this experience, I was in the office of Asti, as part of the DCV team, which was at that time composed of eight people with three software developers working from the office of Pula.

DCV is a remote visualization technology that gives the possibility to multiple clients to securely connect at the same time to a high-performance remote server, permitting the use of applications, which require many re-sources, with relatively low-end client computers by using the server’s com-puting power.

DCV provides two types of clients, which are the web application and the Windows application. The clients are used to establish a connection

(4)

4

with the DCV server application, to enable the remotization. All the DCV applications use the DCV protocol to exchange messages through the Web-Socket protocol, which gives full-duplex communication channels over a single TCP connection. Another technology used by the DCV applications are the Google Protocol Buffers (protobuf) to interchange data by serializing and deserializing a part of the content of the DCV message.

The purpose of this internship was to detect some particular types of bugs, such as assertion failures and memory leaks. A further goal was also to check that the applications could correctly manage invalid input, to guarantee that the error-handling routine was working well. Finally, we wanted to have an automatic testing tool to be used as a component of the pipeline of the continuous integration. Each time the fuzzers found one of the problems listed above, one member of the team had to fix it to avoid that the next fuzzing sessions detect again, the issues already found.

More in detail, the goal of the internship was to fuzz the DCV applications and also to fuzz some of the third-party underlying levels used by DCV applications such as websocket-sharp, libsoup and protobuf-c. Websocket-sharp and libsoup implement the WebSocket protocol, protobuf-c is instead a C implementation of the Google protocol buffers.

During the internship, various methods and tools have been used to fuzz the different applications. For the underlying levels of the DCV protocol, I used some third-party tool: american fuzzy lop for protobuf-c and autobahn for the WebSocket libraries. For what regards the fuzzing of the DCV ap-plications, a man in the middle (MITM) aware of the structure of the DCV protocol has been implemented, to deserialize, fuzz, and reserialize the DCV messages. The third-party library Radamsa was used to perform the fuzz operation.

(5)

Contents

1 Introduction 11

2 Fuzzing 15

2.1 Structure of a fuzzer . . . 17

2.2 Fuzzer classification . . . 17

2.3 Fuzzing network protocol . . . 19

2.4 Used fuzzer . . . 21

2.4.1 American Fuzzy Lop . . . 21

2.4.2 Autobahn Testsuite . . . 22

2.4.3 Radamsa . . . 23

3 DCV 25 4 protobuf-c fuzzing 29 4.1 Test cases integration . . . 32

4.2 AFL fuzzing . . . 37

5 WebSocket fuzzing 41 5.1 Message structure . . . 41

5.2 Autobahn fuzzing . . . 44

5.2.1 Fuzzing libsoup server . . . 44

5.2.2 Fuzzing websocket-sharp client . . . 49 5

(6)

6 CONTENTS

5.3 Man in the middle fuzzing . . . 51

6 DCV protocol fuzzing 55 6.1 Configuration of the fuzzer . . . 57

6.2 Man in the middle . . . 58

6.2.1 Structure . . . 58

6.2.2 Dynamic Model . . . 69

6.2.3 Design Patterns used . . . 72

6.3 Results . . . 75

6.3.1 DCV server . . . 75

6.3.2 Windows client . . . 80

6.3.3 Web client . . . 81

6.4 Server code coverage . . . 81

7 Conclusion 85 7.1 Personal experience . . . 86

(7)

List of Figures

1.1 DCV architecture . . . 13

2.1 Client fuzzer and server model . . . 19

2.2 Client and server fuzzer model . . . 20

2.3 Man in the middle fuzzer model . . . 20

2.4 Command line interface of AFL . . . 21

3.1 Interaction between a DCV client and a DCV Server . . . 26

3.2 Structure of a DCV message . . . 27

4.1 Structure of Google protobuf test cases . . . 32

4.2 Communication between Tester and Testee in Google protobuf test cases . . . 33

4.3 Comparison between the number of total paths, found by AFL fuzzing protobuf-c, between a bugged version (first) and a fixed version (second) . . . 39

5.1 WebSocket framing protocol . . . 42

5.2 Communication between a client and a server fuzzer of Autobahn 49 5.3 HTTP Upgrade process with the MITM . . . 52

6.1 UML diagram of the DCV MITM fuzzer . . . 59

6.2 Detailed UML diagram of the Proxy class . . . 60 7

(8)

8 LIST OF FIGURES

6.3 Detailed UML diagram of the DcvPacket class . . . 61

6.4 Detailed UML diagram of the PacketHandler class . . . 62

6.5 Detailed UML diagram of the Channel class . . . 62

6.6 Detailed UML diagram of the AuthChannel class . . . 63

6.7 Detailed UML diagram of the DataChannel class . . . 64

6.8 Detailed UML diagram of the SetupProtoFuzzer class . . . 64

6.9 Detailed UML diagram of the ProtoFuzzer class . . . 66

6.10 Detailed UML diagram of the ProtoDictionary class . . . 67

6.11 Detailed UML diagram of the PayloadFuzzer class . . . 68

6.12 Detailed UML diagram of the RandomPayloadFuzzer class . . 68

6.13 Detailed UML diagram of the Log class . . . 69

6.14 Detailed UML diagram of the FuzzingUtilities class . . . 69

6.15 Sequence diagram when fuzzing a DCV channel . . . 70

6.16 Sequence diagram of the setup of a data channel . . . 71

(9)

List of Tables

6.1 Code coverage result of the DCV server . . . 82

(10)
(11)

CHAPTER

1

Introduction

Fuzz testing was originally defined by Barton Miller at the University of Wisconsin in 1989. Miller applied the fuzzing technique to the UNIX utilities. In his paper An Empirical Study of the Reliability of UNIX Utilities[1] he explained the surprising results he obtained with this technique. He was able to crash more than 24% of the programs under test. Miller decided to apply this technique to the UNIX utilities because the utilities should be robust and should not crash on receiving unusual input. The method he used consisted in creating programs to generate random strings, that are then used as input to test the UNIX utilities. The strings that crashed these utilities were later identified to understand the causes of the crashes. Miller decided to use this technique because the use of a formal verification would have been too onerous. Since fuzzing covers only some aspects of the testing routines, it does not substitute the standard testing techniques, but it complements them.

Fuzz testing has been the core technique used during my four months internship done in NICE, which is an Amazon Web Services (AWS) company. The stage started in November 2017 and ended in March 2018. During this period, I worked in the headquarter of NICE in Asti, collaborating with the

(12)

12 CHAPTER 1. INTRODUCTION colleagues in the office in Sardinia, Pula. This project was realized with the supervision of Silvio Lazzeretti as company supervisor, and Paolo Borelli, manager of the DCV team.

Founded in 1996, NICE is an Italian industry, leader in the delivery of software products, aimed at company-wide management and optimization of computing and visualization resources. In 2016 NICE was acquired by Amazon Web Services, the world’s most comprehensive and broadly adopted cloud platform. Over the years NICE has developed the following products: • EnginFrame, a user-friendly application used to simplify the access of High Performance Computing (HPC) infrastructures. This application permits to easily submit and control HPC application and monitor workloads and data;

• Desktop Cloud Visualization (DCV), a real-time desktop remotization protocol that allows the users to connect, via native or web client, to applications running in a data center, allowing the use of applications, which require several resources, with relatively low-end client comput-ers by using the server’s computing power;

• Amazon Elastic GPU, an application to economize the cost of cloud infrastructures by sharing GPU resources across multiple servers.

During the internship, I worked with the DCV team. The purpose of this internship was to detect failures and memory leaks, but also to check that the applications could manage correctly invalid input, testing that the error-handling routines were working well. To this end, a more specific goal of the internship was to develop a fuzzer for DCV. The fuzzer built is a man in the middle (MITM) aware of the architecture of DCV (figure 1.1).

(13)

13

DCV

Protocol Buffers

WebSocket

OpenSSL

TCP

Figure 1.1: DCV architecture

• Google protocol buffers (or Google protobuf) are mostly used to seri-alize data in communications protocols. They are defined in an open source library, which is language-neutral and platform-neutral. This library is used by the native client and the web client of DCV, that are written respectively in C# and JavaScript. Since Google protobuf

does not support C while the server is written in C, DCV had to use protobuf-c for the server. Protobuf-c is a C implementation of Google protocol buffers data serialization format, allowing C applications to use protobuf;

• WebSocket is a communications protocol, providing full-duplex com-munication channels over a single TCP connection. DCV native client uses websocket-sharp while the DCV server uses libsoup. Both these libraries implement the WebSocket protocol for both client and server sides;

• OpenSSL is a robust, commercial-grade, and full-featured toolkit for the Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols;

• TCP is one of the main protocols of the Internet protocol suite. TCP provides reliable, ordered, and error-checked delivery of a stream of octets between applications running on hosts communicating via an IP

(14)

14 CHAPTER 1. INTRODUCTION network.

Before starting to implement the fuzzer for DCV, the guarantee of ro-bustness of the underlying layers was required. During the internship, when it came to fuzzing, after analyzing all the different layers, a distinction was made.

Google protobuf, OpenSSL and TCP, thanks to their extensive use by the software developer community, were considered robust enough by the team, so they have not been fuzzed during the internship.

The fuzzing of the remaining layers, such as protobuf-c and WebSocket (websocket-sharp and libsoup), alongside the fuzzing of the DCV protocol, will be explained in the next chapters. The layers will be presented in a temporal order, according to when the fuzzing has been done throughout the internship.

(15)

CHAPTER

2

Fuzzing

Fuzzing is an automated or semi-automated software testing technique used to discover coding errors and security flaws by inputting invalid or random data. The system is then monitored to check if any failure arises during the fuzzing sessions. Fuzz testing was originally defined by Barton Miller at the University of Wisconsin in 1989. Miller applied the fuzzing technique to the UNIX utilities. In his paper An Empirical Study of the Reliability of UNIX Utilities[1] he explained the impressive results he obtained with this technique. He was able to crash more than 24% of the programs under test. Miller decided to apply this technique to the UNIX utilities because these utilities should be robust and should not crash on receiving unusual input.

The technique he used consisted in creating a program, the fuzz, that generates random strings. Then, a script passes the generated strings as input to the UNIX utility under test and reads back the output generated by the utility. Miller identified three types of results, that can be obtained by executing one test:

• Crash: when the utility under test crashes. The crash can be detected by the script when a core dump file is produced;

(16)

16 CHAPTER 2. FUZZING • Hang: when the utility under test loops indefinitely. The utility is considered hang if it produces no more output while there is still some input to be used or if the utility continues producing output while there is no more input to be used;

• Success: when giving an input to the utility, the utility returns an output to the script.

Miller decided to use this technique because the use of a formal verification would have been too onerous. Since fuzzing covers only some aspects of the testing routines, it does not substitute the standard testing techniques, but it complements them. Indeed, while the conventional testing techniques concentrate on giving a particular input to a program under test, to see if the program gives back an expected output result or not. The fuzz testing techniques instead have as a goal to find inputs that cause crashes to fix it and make the program more robust.

The strings that crashed the UNIX utilities were identified to understand the causes of the crashes. The causes were also categorized to give some general rules and example to the programmer’s community to explain how to avoid these errors.

In 1995, Miller published Fuzz Revisited: A Re-examination of the Reli-ability of UNIX Utilities and Services[2] where he redid the experiment on the new versions of the UNIX utilities, finding a noticeable improvement in reliability, but still having a significant rate of crashes.

A fuzzer is considered more efficient if it reaches a higher degree of code coverage. Indeed, if a fuzzer does not stress certain elements of the program, then it is also not able to reveal crashes and errors that are present in these elements.

(17)

2.1. STRUCTURE OF A FUZZER 17

2.1

Structure of a fuzzer

A fuzzer needs to perform the following tasks:

• Generate test cases. This task can be done completely random or by mutating test cases that need to be provided as an input seed;

• Save the test cases or other information needed to reproduce the test in case of crashes or errors. This step is useful in complex cases, to better analyse the errors and to understand how to do the fix;

• Interface with the application that has to be fuzzed with the generated test cases;

• Detect crashes. This task is essential because it is important to deter-mine when a program has crashed; otherwise it will not be possible to identify which are the test cases that provoke the crash.

2.2

Fuzzer classification

Fuzzers can be classified according to the following dimensions:

Generation-based or mutation-based fuzzer

A fuzzer can be based on generation or mutation, depending on whether inputs are randomly generated from scratch or by modifying existing inputs provided by the user. In the first case, we can talk about generation-based fuzzer while in the second about mutation-based fuzzer. A generation-based fuzzer generates input from scratch, without having any initial dataset [4].

A mutation-based fuzzer, instead, needs a first set of inputs. Its task is to generate input by modifying the initial supplied set. For example, when

(18)

18 CHAPTER 2. FUZZING fuzzing a DBMS, the user has to provide a complete set of SQL examples. The user may give a starting seed of test cases which contains duplicates or similar inputs. For this reason, a mutation-based fuzzer might have an automatic seed selection, that can select the best seeds to maximize the total number of bugs found during a fuzzer execution. The mutation is accomplished by having various kinds of heuristics and change patterns [3].

Smart or dumb fuzzer

A fuzzer can be smart or dumb, depending on whether it is aware of the input structure passed to the program under test. A smart fuzzer, which is aware of the grammar or the protocol used by the program, exploits the input model to generate a larger set of valid inputs. Since a smart fuzzer can use more valid input, it can found corner cases much faster than the dumb fuzzer [4].

A dumb fuzzer instead, does not require the input model and can, there-fore, be used to browse a wider variety of programs. A dumb fuzzer is used when the input model is proprietary, unknown or very complex [5][6].

Black-box, grey-box or white-box fuzzer

Like in traditional testing techniques, a fuzzer can be black, grey or white, depending on whether it is aware of the structure of the program under test. A black-box fuzzer interacts with the application considering the program as a black box and has no knowledge about the internal structure of the program [5].

A white-box fuzzer instead, has access to the source code and run program analysis to detect the points of the code that can be “attacked”, the input is then mutated to reach the desired part of the code. Sometimes it is better to use a black-box fuzzer because the generation of a new input performed

(19)

2.3. FUZZING NETWORK PROTOCOL 19 by the white-box fuzzer could be too costly [4][6].

A trade-off between the two mentioned versions is the grey-box fuzzer, which uses instrumentation to obtain coverage results. The coverage is used to understand if a test exercises a new path. In the case in which the test found a new path, it is added to the set of input seeds; otherwise, it is discarded [7].

2.3

Fuzzing network protocol

A protocol fuzzer is a software that creates malformed requests of the desired protocol, to cause unexpected situations and to check if the software under analysis can correctly handle such situations. The fuzzer protocols are now considered a necessary tool for testing network software because they allow to improve the test coverage significantly and to identify implementation errors and potential security problems effectively. A fuzzer is considered more efficient if it achieves a higher degree of code coverage.

When fuzzing a communication protocol, like the client-server model, there can be two different ways to implement a fuzzer.

Figure 2.1: Client fuzzer and server model

The first implementation (Figure 2.1), consists in developing a client fuzzer that acts as a client and fuzzes the server. In this case, the client sends fuzzed messages to the server and, in response, the server sends only valid data, that follows the rules of the protocol, through the communication channel.

(20)

20 CHAPTER 2. FUZZING

Figure 2.2: Client and server fuzzer model

A similar approach can be made to fuzz the client as showed in figure 2.2. The disadvantage of this structure is that two fuzzers have to be built to fuzz a communication protocol made of two endpoints. Another downside is that, in some cases, both the two fuzzers have to know how to handle the authentication or how to set up a new connection as in the case of DCV.

Figure 2.3: Man in the middle fuzzer model

On the other hand, the implementation showed in figure 2.3, has an ad-ditional element, called a man in the middle (MITM) that is in between the client and the server. This structure, allows the MITM to receive messages and then choose whether if it has to fuzz the message or not, depending on its content. The possibility to select which message has to be fuzz is very useful because, for example, it can be decided to not fuzz the authentication messages to go deeper in the communication of a network protocol. This implementation, however, has some disadvantages, if there is no interaction with a user, neither the server nor the client, send each other messages auto-matically. This problem can be solved by modifying the client to send new requests automatically.

(21)

2.4. USED FUZZER 21

2.4

Used fuzzer

In this section, I will list the fuzzers used during the internship. Some of them were used to fuzz the interested application directly, others were used as a component to build the MITM solution used to fuzz the DCV applications. More information on how the fuzzers were used during the internship can be found in the next chapters.

2.4.1

American Fuzzy Lop

Figure 2.4: Command line interface of AFL

AFL (American Fuzzy Lop) is a dumb mutation-based grey-box fuzzer that uses genetic algorithms to modify the initial test cases, provided in input by the user.

AFL mutates the test cases by flipping random bits, by substituting ran-dom bytes with “interesting” values, and by moving or deleting blocks of data.

(22)

22 CHAPTER 2. FUZZING AFL utilises lightweight instrumentation to get information about the increase of code coverage during fuzzing, allowing AFL to understand if a test exercises a new path. The new paths are added to the set of test cases; otherwise, they are discarded, allowing AFL to reach a higher degree of code coverage.

Due to its characteristics, AFL is very easy to set up because it just needs a suite of test cases and it also needs to know how to execute the application under test and how to provide the inputs to the application under test. Once a crash is detected, the fuzzer reports in a file the test case that crashed the application.

Figure 2.4 shows the command line interface of AFL, more information can be found on the website of american fuzzy lop [11]. AFL was used during the internship to fuzz the protobuf-c library. How this fuzzer was applied to the library is explained in chapter 4.

2.4.2

Autobahn Testsuite

Autobahn provides a fully automated test suite to verify the implementation of the WebSocket protocol in both client and server side [12]. Autobahn contains over 500 test cases covering:

• Framing • Pings/Pongs • Reserved Bits • Opcodes • Fragmentation • UTF-8 Handling

(23)

2.4. USED FUZZER 23 • Limits/Performance

• Closing Handshake • Opening Handshake • WebSocket compression

The main tools of Autobahn are the fuzzing client and the fuzzing server. The first one has a structure similar to the one in Figure 2.1, the fuzzing client is used to run the 500 test cases on a WebSocket server to check if the implementation of the protocol is correct. The fuzzing server instead, has a structure similar to the one in Figure 2.2, where the tested application is the client one. Even if non of these two tools can be considered a real fuzzer because they do not generate or mutate the test cases in any way, they are beneficial to test the correctness of a WebSocket protocol.

More information can be found on the website of Autobahn Testsuite [12]. Chapter 5 explains how Autobahn was used as the first step to fuzz the libraries websocket-sharp and libsoup.

2.4.3

Radamsa

Radamsa is a dumb mutation-based black-box fuzzer that works by reading input data and generating different interesting outputs from them. Radamsa mutates the input by exploiting various heuristics and change patterns, which are varied during the tests.

The advantages of Radamsa are that it is easily scriptable and easy to get up and running; once Radamsa is correctly installed it can be merely used as shown below.

(24)

24 CHAPTER 2. FUZZING

2 F u z z 2 2 4 6 7 8 6 4 1

Radamsa requires a string in input and returns a fuzzed string in output. The fact that Radamsa can be used from the command line is advantageous because it can be easily used by any language which gives the possibility to run command line commands.

More information can be found on the website of Radamsa [13]. Radamsa was used as a component of the MITM, built to fuzz the DCV protocol. All the detailed information of the MITM are in the chapter 6.

(25)

CHAPTER

3

DCV

DCV is a remote visualization technology that provides the possibility to many clients to securely connect at the same time to a high-performance remote server. This permits the use of applications, which require many resources, with relatively low-end client machines by using the server’s com-puting power.

The remote endpoint where the user sits is named the DCV client or viewer. DCV provides two kinds of clients: which are the web application client and the Windows application client. The endpoint where the remote desktop is running is known as the DCV server.

DCV is designed to facilitate user interaction with a remote physical or virtual computer system. To provide the remote graphical interface to the user, DCV transfers graphics display information from the remote computer to the user. The DCV client instead intercepts and transports the user input commands to the remote computer, where the input commands are replayed. DCV uses the WebSocket protocol as a transport layer, which gives full-duplex communication channels over a single TCP connection. The DCV protocol is independent of the underlying transport and could use any pro-tocol, as long as the transport guarantees the following properties: ordering,

(26)

26 CHAPTER 3. DCV reliable delivery, encryption and integrity. The WebSocket protocol ensures all those properties.

All the DCV applications, the clients and the server, use the DCV proto-col to interchange messages. The DCV messages are exchanged with the use of the Google Protocol Buffers (protobuf). Protobuf is used for serializing and deserializing a part of the content of the DCV message.

Figure 3.1: Interaction between a DCV client and a DCV Server

Figure 3.1 shows how the DCV protocol is used between the DCV client and the DCV server. The DCV protocol is composed of multiple logical channels:

• the “auth” channel that is used to authenticate a client; • the “main” channel that is used as a control channel;

• a collection of “data” channels that transport data. Two examples of data channel are the “display” channel that transports images and the “input” channel that transports mouse and keyboards events.

Each channel is implemented in a separate connection of the WebSocket protocol. The information on which type of channel has been opened is sent through the “main” channel.

The client and server communicate through DCV messages, the structure of one message is showed in figure 3.2.

(27)

27

Frame Header Frame Payload

Body Binary Payload Message Body Binary Length Length (protobuf + padding) Payload

Figure 3.2: Structure of a DCV message

DCV messages have a binary representation where the “Frame Header” is fixed size, 64 bit, used to define the message boundaries. The “Message Body” instead, contains a protobuf message. If the serialized protobuf mes-sage is not 64 bit aligned, the body is “padded” with zeros. At last, the “Binary payload” is the binary payload that follows the message, the con-tent of the binary payload is specific to each message. Note that both the body and the payload are optional, for instance, if the data channel is only sending small metadata messages, they will usually contain just the body. If the channel, instead, is encapsulating a third party protocol, it can omit the body; however, it should ensure that overall the message has 64-bit align-ment.

Each channel of the DCV protocol has its protobuf definition. However, all the DCV channels are defined in a similar way, and every definition con-tains two types of messages: “ClientMessage”, that are the messages sent by the client, and “ServerMessage”, the message sent by the server. These messages are composed of a series of nested sub-messages and only one at a time can be sent for each DCV message. For example, the “ClientMessage” of the input channel could have as sub-message the mouse click message, that sends information on which button of the mouse was clicked by a user. Below it is shown an example of how a possible message definition can be built, that defines a channel that sends only pings and pongs.

1 message P i n g {

(28)

28 CHAPTER 3. DCV 3 } 4 5 message Pong { 6 i n t 6 4 p i n g t i m e s t a m p = 1 ; 7 i n t 6 4 p o n g t i m e s t a m p = 2 ; 8 } 9 10 message C l i e n t M e s s a g e { 11 oneof msg { 12 Pong pong = 1 ; 13 } 14 } 15 16 message S e r v e r M e s s a g e { 17 oneof msg { 18 P i n g p i n g = 1 ; 19 } 20 }

(29)

CHAPTER

4

protobuf-c fuzzing

Protobuf-c is a C implementation of the Google protocol buffers data serial-ization format, used by DCV server. The first objective for this layer was to integrate the test cases of Google protobuf into the protobuf-c library. Then the second objective was to fuzz the protobuf-c with american fuzzy loop and using as an initial seed the integrated test cases. More information on AFL is in the section 2.4.1 where I have listed the fuzzer used during the internship.

Before explaining in details how this library was fuzzed during the in-ternship, more information is given on how Google protobuf can be used in order to serialize and deserialize communications messages.

First of all, the structure of the message has to be defined. It can be done through a file “.proto” shown in an example below.

1 message Car { 2 s t r i n g b r a n d = 1 ; 3 i n t 3 2 y e a r = 2 ; 4 s t r i n g model = 3 ; 5 } 29

(30)

30 CHAPTER 4. PROTOBUF-C FUZZING From a file “.proto” it is possible to generate a data access class with protoc, the protocol buffer compiler. The generated classes provide some functions which are: one method to serialize the whole structure to raw bytes, one method to parse the structure from raw bytes, getters and setters to set the properties of the message. Below an example is shown on how the generated data access class can be used in C++.

After a class “Car” is instantiated, it is possible to set some values for the fields of the class (e.g. brand, year and model). Now the class has all the desired values, so it can be serialized and then sent through a stream to another endpoint. See below.

1 Car c a r ; 2 c a r . s e t b r a n d (”BMW”) ; 3 c a r . s e t y e a r ( 2 0 1 3 ) ; 4 c a r . s e t m o d e l (” F21 ”) ; 5 f s t r e a m o u t p u t (” m y f i l e ”, i o s : : o u t | i o s : : b i n a r y ) ; 6 c a r . S e r i a l i z e T o O s t r e a m (& o u t p u t ) ;

The code below instead, shows how to handle serialized protobuf messages received from another endpoint. The first thing to do is to deserialize the raw bytes into the generated data access class, afterwards, once the instance of a data access class is created, it is possible to access the fields of the message with the provided getters.

1 f s t r e a m i n p u t (” m y f i l e ”, i o s : : i n | i o s : : b i n a r y ) ; 2 Car c a r ;

3 c a r . P a r s e F r o m I s t r e a m (& i n p u t ) ;

4 c o u t << ” Brand : ” << c a r . b r a n d ( ) << e n d l ; 5 c o u t << ” Model : ” << c a r . model ( ) << e n d l ;

(31)

4.1. TEST CASES INTEGRATION 31 The previous examples were a simple explanation on how Google protobuf can serialize and deserialize communications messages. More information on how the data are effectively serialized in google protobuf can be found on the website [15].

A question on why protobuf should be used instead of XML may arise. Protobuf is a binary-based serializer while XML or JSON are text-based serializers. Next, are listed the advantages that protobuf messages have over XML:

• they are simpler

• they are smaller

• they are faster

• they are less ambiguous

• they generate data access classes that are easier to use

For languages that are not supported by Google protobuf such as the C, there is the need for creating a library as protobuf-c. This library is composed by libprotobuf-c, which implements protobuf encoding and decoding, and protoc-c, which is a code generator that converts “.proto” files to C descriptor code. Protoc-c is based on the original protoc.

Protobuf-c is not as largely used as Google protobuf, a reason why it was fuzzed during the internship. To check the robustness and correctness of this library two actions were taken. In the next sections, it is explained how the integration of test cases of Google protobuf into protobuf-c was done and how AFL was used to fuzz protobuf-c.

(32)

32 CHAPTER 4. PROTOBUF-C FUZZING

Figure 4.1: Structure of Google protobuf test cases

4.1

Test cases integration

Google protobuf contributors have added a series of test cases in order to test the new versions of the library automatically. Figure 4.1 shows the components that execute the test cases. A Tester, written in C++, sends a test case to a Testee through a pipe. The Testee, written in the target language that has to be tested, receives a test case containing a payload that is deserialized, parsed and then reserialized. The obtained value is then returned to the Tester that evaluates if the value is what it was expecting for.

The communication between the Tester and Testee is defined in the “con-formance.proto” file. The Tester sends a “Conformance Request” message in which there is the following information:

• the payload that has to be parsed and reserialized by the Testee can be in protobuf syntax or JSON syntax;

• the type of payload format that the Testee expects to receive;

(33)

4.1. TEST CASES INTEGRATION 33 After the Testee has deserialized and reserialized the payload, it returns a “Conformance Response” to the Tester with one the following information (depending on the result of the test):

• parse error, when an error occurred during the parsing operation; • serialize error, when an error occurred during the serializing operation; • runtime error, when an expected error occurred. This will always

indi-cate that the test failed;

• protobuf payload, when no error occurred and the protobuf payload is correctly serialized;

• JSON payload, when no error occurred and the JSON payload is cor-rectly serialized.

Figure 4.2: Communication between Tester and Testee in Google protobuf test cases

The Tester knows which response it should receive from the Testee and, if the test case is not passed, it is added to the failing test cases that are then notified to the user. The communication process between the Tester and

(34)

34 CHAPTER 4. PROTOBUF-C FUZZING the Testee is showed in figure 4.2, both the “ConformanceRequest” and the “ConformanceResponse” are preceded from a message containing the length of these messages.

The only requirement to integrate the test cases in protobuf-c was to write a Testee in C. The Tester was modified to not execute test cases that involve the JSON syntax; In fact, the canonical encoding in JSON is not supported by the protobuf-c library.

The protobuf payload sent between the Tester and the Testee is defined in the files “test messages proto2.proto” and “test messages proto3.proto”, depending on the proto syntax. There had been a problem generating the data access classes of the proto2 syntax with protoc-c, because the protobuf-c library has not implemented the resolution of the keyword “group”, depre-cated in the proto3 syntax. To not fail the compilation, some modifications were done on the file “test messages proto2.proto”. These modifications are shown next. In fact, the “group” keyword has been removed using the key-words “message” and “repeated”.

1 // Not w o r k i n g g r o u p k e y w o r d 2 o p t i o n a l group Data = 201 { 3 o p t i o n a l i n t 3 2 g r o u p i n t 3 2 = 2 0 2 ; 4 o p t i o n a l u i n t 3 2 g r o u p u i n t 3 2 = 2 0 3 ; 5 } ; 6 7 // R e w r i t t e n v e r s i o n i n o r d e r t o be c o m p i l e d by p r o t o c −c 8 message Data { 9 message Group { 10 o p t i o n a l i n t 3 2 g r o u p i n t 3 2 = 2 0 2 ; 11 o p t i o n a l u i n t 3 2 g r o u p u i n t 3 2 = 2 0 3 ; 12 } 13 r e p e a t e d Group group = 2 0 4 ;

(35)

4.1. TEST CASES INTEGRATION 35

14 }

Thanks to the modification above, the Testee could accept both proto2 and proto3 syntax test cases.

The result of the testing session showed that the only test cases that was not passing by protobuf-c were those classified as “IllegalZeroFieldNum”.

The structure of a protocol buffer message has to be explained to under-stand why these test cases fail. A protobuf message is a series of key-value pairs. The key is composed by the tag, that is defined in the “.proto” file and by a wire type, that provides information on the length of the value. The wire type is useful to let protobuf skip the fields which the parser is not able to recognize. In this way, new fields can be added to a message without breaking old programs that do not know anything about the new modifications.

The tag and the wire type are encoded using the varint method, in which a smaller number requires fewer bytes to be encoded. Each byte in a varint uses the most significant bit to indicate if there are further bytes to come or not. The first bit of a message is set to 0 when the tag is a value from 1 to 15. If it is greater than 15, the first bit is set to 1 (more information can be found on the guide of google protobuf [15]). In this case, a parse error must arise when the tag is 0, and this is verified when the first 5 bits are equals to zero, the last three bits of the first byte are used to identify the wire type.

The “IllegalZeroFieldNum” test cases are used to ensure that the pro-tobuf library does not parse a message with tag value equal to zero. Some modifications are needed to make the library pass these tests, below it can be found the code that fix this problem. The lines from 13 to 15, were added to the function parse tag and wiretype, in order to return an error if the tag is equal to 0, this is done by checking if the first 5 bit are equal to 0.

(36)

36 CHAPTER 4. PROTOBUF-C FUZZING 1 s t a t i c s i z e t 2 p a r s e t a g a n d w i r e t y p e ( s i z e t l e n , 3 c o n s t u i n t 8 t ∗ data , 4 u i n t 3 2 t ∗ tag out , 5 P r o t o b u f C W i r e T y p e ∗ w i r e t y p e o u t ) 6 { 7 u n s i g n e d m a x r v = l e n > 5 ? 5 : l e n ; 8 u i n t 3 2 t t a g = ( d a t a [ 0 ] & 0 x 7 f ) >> 3 ; 9 u n s i g n e d s h i f t = 4 ; 10 u n s i g n e d r v ; 11 12 /∗ 0 i s not a v a l i d tag v a l u e ∗/ 13 i f ( ( d a t a [ 0 ] & 0 x f 8 ) == 0 ) { 14 r e t u r n 0 ; 15 } 16 17 ∗ w i r e t y p e o u t = data [ 0 ] & 7 ; 18 i f ( ( d a t a [ 0 ] & 0 x80 ) == 0 ) { 19 ∗ t a g o u t = tag ; 20 r e t u r n 1 ; 21 } 22 f o r ( r v = 1 ; r v < m a x r v ; r v ++) { 23 i f ( d a t a [ r v ] & 0 x80 ) { 24 t a g |= ( d a t a [ r v ] & 0 x 7 f ) << s h i f t ; 25 s h i f t += 7 ; 26 } e l s e { 27 t a g |= d a t a [ r v ] << s h i f t ; 28 ∗ t a g o u t = tag ; 29 r e t u r n r v + 1 ; 30 } 31 } 32 r e t u r n 0 ; /∗ e r r o r : bad header ∗/ 33 }

(37)

4.2. AFL FUZZING 37

4.2

AFL fuzzing

This section explains how AFL was used to fuzz protobuf-c. As written in section 2.4.1, AFL (American Fuzzy Lop) is a brute-force fuzzer that uses genetic algorithms, to mutate the initial input seeds.

As in the test cases integration, a very similar application of the C Testee version has been developed, but instead of receiving a “Conformance Re-quest”, it only receives the protobuf payload. This application, as the Testee one, parses the payload and reserializes it again. The afl-fuzz calls this appli-cation and sends inputs through the standard input, and checks if there had been any crashes (SIGSEGV, SIGILL, SIGABRT). AFL stores the inputs that cause crashes and timeouts on several files.

The payload inputs, used as initial test cases, are extracted from the ones of google protobuf test cases and are the ones that the Testee application was able to parse.

After the first run of fuzzing, a bug was found. A debugger had been attached to the new application to understand where the problem was. The input that crashes protobuf-c was saved to a file by AFL and it was used as input while the debugger was running. Below is reported the modifications that were needed to fix this bug. In line 23 it is shown the old expression that causes an overflow, so, when “val” was equal to the maximum possible uint32 t value, summing to it a positive value, it leads to an overflow. Line 24 has been substituted by line 25 to avoid the overflow.

1 s t a t i c i n l i n e u i n t 3 2 t

2 s c a n l e n g t h p r e f i x e d d a t a ( s i z e t l e n , c o n s t u i n t 8 t ∗ data , 3 s i z e t ∗ p r e f i x l e n o u t )

4 {

(38)

38 CHAPTER 4. PROTOBUF-C FUZZING 6 u n s i g n e d h d r l e n ; 7 u i n t 3 2 t v a l = 0 ; 8 u n s i g n e d i ; 9 u n s i g n e d s h i f t = 0 ; 10 11 f o r ( i = 0 ; i < h d r m a x ; i ++) { 12 v a l |= ( d a t a [ i ] & 0 x 7 f ) << s h i f t ; 13 s h i f t += 7 ; 14 i f ( ( d a t a [ i ] & 0 x80 ) == 0 ) 15 break; 16 } 17 i f ( i == h d r m a x ) {

18 PROTOBUF C UNPACK ERROR(” e r r o r p a r s i n g l e n g t h f o r 19 l e n g t h −p r e f i x e d d a t a ”) ; 20 r e t u r n 0 ; 21 } 22 h d r l e n = i + 1 ; 23 ∗ p r e f i x l e n o u t = h d r l e n ; 24 // i f ( h d r l e n + v a l > l e n ) { 25 i f ( h d r l e n > l e n | | v a l > l e n − h d r l e n ) {

26 PROTOBUF C UNPACK ERROR(” d a t a t o o s h o r t a f t e r 27 l e n g t h − p r e f i x o f %u ”, v a l ) ;

28 r e t u r n 0 ;

29 }

30 r e t u r n h d r l e n + v a l ; 31 }

After this fix was applied, afl-fuzz was restarted with the fixed version. In figure 4.3 is showed a comparison between the fuzzing on the not fixed version and the fuzzing on the fixed one. The difference is that the number of total paths stops increasing after few minutes of iteration. A path is a test case that triggers new execution patterns, crashes or hangs.

(39)

4.2. AFL FUZZING 39

Figure 4.3: Comparison between the number of total paths, found by AFL fuzzing protobuf-c, between a bugged version (first) and a fixed version (sec-ond)

This result could mean that all the possible paths have already been executed. Although, to be sure that this is true, a new process of fuzzing has been started, also using the Google protobuf test cases that fail at parsing as well. The new set of seeds only leads afl-fuzz to find around 200 more paths. Although, the result is similar to the previous one, in fact, after 15/20 minutes, the fuzz has difficulties to find new paths. The test cases that found a new path are added to the input seeds, while the others are discarded.

This result is positive because it could mean two things: either that the fuzzer has covered the majority of the possible paths or that the initial test case was not a good input sample. Those inputs, coming from the tests of Google protobuf are made to check all the possible protobuf type defined in the proto syntax. Another significant result is that AFL has a colour code in

(40)

40 CHAPTER 4. PROTOBUF-C FUZZING the colour cycle (figure 2.4), and it was run until the colour was green, that means that the fuzzer has not found a new path for a while.

The fuzzing sessions have tested both proto2 and proto3 syntax leading to similar results.

(41)

CHAPTER

5

WebSocket fuzzing

WebSocket is a communication protocol that provides two-way communica-tion between a client and a server over a TCP conneccommunica-tion. The protocol uses the HTTP upgrade header, as an opening handshake, to change from the HTTP protocol to the WebSocket one. After the handshake is done, the data transfer part can start, and the connection between the server and the client will remain open until one of the party decides to close it.

The DCV protocol uses the WebSocket protocol for most of the commu-nication between the client and the server. The server side, written in C, uses libsoup to use the WebSocket protocol. Instead, the native client, written in C# , uses the websocket-sharp library. In the next section, it will be

explained how the WebSocket messages are encoded, to better understand the fuzzing of the two libraries, libsoup and websocket-sharp.

5.1

Message structure

The WebSocket message encoding is very different in comparison to the HTTP one. Indeed, the WebSocket reduces the message overhead elimi-nating the HTTP header and transmitting data using a sequence of frames.

(42)

42 CHAPTER 5. WEBSOCKET FUZZING The client must mask all the frames sent to the server, if the server receives an unmasked message, it has to close the connection. On the contrary, a server must not send a masked message, and if the client receives a masked message, it has to close the connection.

Figure 5.1: WebSocket framing protocol

Figure 5.1 shows the structure of how a WebSocket message is encoded. The first bit (FIN) is used to indicate if the frame is the final frame of a message or not, besides, the first frame can also be the final one. The follow-ing 3 bit (RSV1, RSV2 and RSV3) must be 0 unless an extension has been negotiated at handshake time, in which it has been defined the meanings for non-zero values. If a non-zero value is received, and no negotiated extension defines it, the receiving endpoint must close the connection. The next 4 bits (opcode) are used to define how “Payload Data” has to be interpreted, if an endpoint receives an opcode reserved for a future version of the WebSocket protocol, it has to close the connection. Below are listed the possible values of the opcode and their meanings:

(43)

5.1. MESSAGE STRUCTURE 43 • 0 continuation frame

• 1 text frame • 2 binary frame

• 3-7 reserved for future non-control frames • 8 connection close

• 9 ping • 10 pong

• 11-15 reserved for future control frames

The next bit (MASK) defines if the message is masked or not if set to 1 a masking key will be inserted after the payload length. The following 7 bits identify the length of the payload, but three cases can arise. The first case is when the length is a value between 0 and 125, this value is then the payload length. In the second case instead, if the length is equal to 126, the next 2 bytes have to be interpreted as a 16-bit unsigned integer and the value obtained is the length of the payload. At last, the third case is when the length is equal to 127, in this case, the following 8 bytes have to be interpreted as a 64-bit unsigned integer, and the obtained value is the payload length.

As written before, if the MASK bit is set to 0 there will not be any masking key, on the contrary, if the MASK bit is set to 1, the next 4 bytes after the payload length are used for the masking key. More information on why this mask is used can be found on the website that defines the rules of the WebSocket protocol [17].

The last part of the message encoding is composed by the “Payload data”, which is defined as “Extension data”, concatenated with “Application data”.

(44)

44 CHAPTER 5. WEBSOCKET FUZZING The first one is 0 bytes unless an extension has been negotiated. Any exten-sion has to provide, at negotiation time, the length of the “Extenexten-sion data”. Indeed, the “Application data” contains the corpus of the message.

5.2

Autobahn fuzzing

A simple echo server with libsoup and a simple echo client with websocket-sharp were created to test these libraries with Autobahn. In the next sessions are explained the obtained results of the fuzzing and the fixes that have been done to resolve the found problems.

5.2.1

Fuzzing libsoup server

Only a few steps are requested to make Autobahn fuzz the echo libsoup server. Therefore, the libsoup server has to listen on this URL https://127. 0.0.1:9001/, accept all the connections from the fuzzer client of Autobahn and respond to all binary and text messages with the same content. The libsoup library handles all the other messages such as close, ping and pong.

Next, are described the bugs found by Autobahn and the relative fixes that were done. All the patches done regarding libsoup server can be found here https://bugzilla.gnome.org/show_bug.cgi?id=792113.

Ping/Pong message

Ping and pong allow each endpoint of a WebSocket connection to know if the opposite endpoint is still present and available. Autobahn has found two problems regarding these messages.

The first test that libsoup does not pass is when the client sends a ping with a binary payload of 126 octets, in this situation the server must im-mediately return a protocol error, since control frames, such as pings/pongs,

(45)

5.2. AUTOBAHN FUZZING 45 are only allowed to have payload up to and including 125 octets. To solve this problem, instead of merely making a debug print, as it was previously done, a call to the function that closes the connection with a protocol error was done.

The second test that is not passed, is when the client sends ten pings with a different payload, but instead of receiving from the server the pongs in order, it receives the pongs on the opposite order of how the pings were sent. The problem is that libsoup has a queue of frames that have to be sent, and the control frames have a higher priority so, for this reason, they are put on the head of the queue. Two possible implementations could be realised, to solve this problem:

• The first implementation consists in using two queues. One can be used for the priority frame, the other for the non-priority frame. When a frame is requested from the queues, the application has to check if there is a frame to be sent in the priority queue. Otherwise, it checks if there is a frame in the non-priority queue.

• The second version instead, puts a priority frame right after all the priority frames present in the queue.

The first implementation would have required much time to be imple-mented because it involves many fixes in different functions of the library. The second implementation instead, is less efficient in theory but, in prac-tice, there is not much difference with the first one, because the number of control frames usually sent are few. For these two reasons, it was chosen to realize the second implementation. Next, is reported a portion of the patch that fixes this bug. The complete patch, with “Fix order control frame” as commit name, can be found on https://bugzilla.gnome.org/show_bug. cgi?id=792113

(46)

46 CHAPTER 5. WEBSOCKET FUZZING 1 f r a m e = g s l i c e n e w 0 ( Frame ) ; 2 f r a m e −>d a t a = g b y t e s n e w t a k e ( d a t a , l e n ) ; 3 f r a m e −>amount = amount ; 4 f r a m e −> f l a g s = f l a g s ; 5 6 /∗ I f u r g e n t put at f r o n t o f queue ∗/ 7 i f ( f l a g s & SOUP WEBSOCKET QUEUE URGENT) { 8 G L i s t ∗ l ;

9

10 /∗ Find out the f i r s t frame t h a t i s not u r g e n t or

p a r t i a l l y s e n t ∗/

11 f o r ( l = g q u e u e p e e k h e a d l i n k (&pv−>o u t g o i n g ) ; l !=

NULL ; l = l −>n e x t ) {

12 Frame ∗ prev = l −>data ; 13

14 i f ( ! ( p r e v −> f l a g s & SOUP WEBSOCKET QUEUE URGENT)

&& 15 p r e v −>s e n t == 0 ) 16 break; 17 } 18 19 g q u e u e i n s e r t b e f o r e (&pv−>o u t g o i n g , l , f r a m e ) ; 20 } e l s e { 21 g q u e u e p u s h t a i l (&pv−>o u t g o i n g , f r a m e ) ; 22 } Extension bits

As written in the section 5.1, in a WebSocket message the extension bits are reserved for negotiated extensions. The protocol has the following rule: if a client sends a message with a reserved bit for an extension that has not been negotiated, the server must return a protocol error and close the connection.

(47)

5.2. AUTOBAHN FUZZING 47 Since libsoup has not implemented the extensions, it has been added at parse time a simple check that controls that the reserved bits are equal to zero; otherwise, it returns a protocol error.

Opcode

As written in the previous sections, the WebSocket protocol has reserved some opcode value for future implementation. The protocol states that if an endpoint receives these values, it should give a protocol error and close the connection. Although, libsoup, instead of doing this, only makes a debug print. As for the ping/pong section, a simple fix has been done calling the function that closes the connection with a protocol error.

Text message

The text messages in WebSocket are encoded using the standard UTF-8, Autobahn found two defects related to UTF-8 encoding.

The first problem is that libsoup, in case of fragmented messages, checks frame by frame if the payload of the frame is UTF-8 valid or not, instead of making this check when the string is completely reassembled. For example, if the client sends two frames of the length of 1 byte: “0xC2”, “0xB5”, parsing those two frames, one at a time, could lead to an invalid utf8 payload. Otherwise, if the check is done on the concatenation of these frames, the string will be valid because it is the utf8 encoding of the “µ” character. This problem has been solved by just checking if a fragmented message is utf8 valid or not only after it is reassembled.

The second problem instead, arises when a payload of the length of 1 byte containing “0x00” (“NULL” character) is sent to the server. Therefore, this string is UTF-8 valid, but the libsoup server fails this test case because it returns an error. Indeed, the function g utf8 validate() of the library glib

(48)

48 CHAPTER 5. WEBSOCKET FUZZING has the following limitation [20]:

Note that g utf8 validate() returns FALSE if max len is positive and any of the max len bytes are null.

Due to this limitation of glib, no fixes were done for this bug, but the problem was still reported to the libsoup developer community.

Close message

The close message is a control message of the WebSocket protocol, used to communicate to another endpoint that the connection is going to be closed and it explains the reason why it had been closed. For this section three problems have been found by Autobahn.

The first one is a very similar problem of the ping one. If the size of the payload of the closing message is greater than 125 octets, the server should return a protocol error or close the connection. This rule is valid for all the control frames, and this has been partially fixed in the section of the ping. A small fix was then required to pass the payload of the close frames to the function, that checks if the dimension is not greater than 125 octets.

The second problem instead, is that libsoup does not return a protocol error when a close frame contains an invalid UTF-8 payload, but it merely does a print debug. This problem was resolved in the same way of the issues seen in the previous sections, calling the function that closes the connection and returns a protocol error.

The last problem of this part is that when the client sends a close frame with an invalid close code (ex: 999, 1004) the libsoup server does not return a protocol error or close the connection. This problem was solved with a simple check on the value of the close code.

(49)

5.2. AUTOBAHN FUZZING 49

5.2.2

Fuzzing websocket-sharp client

Figure 5.2: Communication between a client and a server fuzzer of Autobahn Respect to the client fuzzer of Autobahn, the server side requires a more significant effort to be set up. Indeed this fuzzing server is typically used to check that the browsers handle the WebSocket protocol correctly. An

(50)

50 CHAPTER 5. WEBSOCKET FUZZING analysis of how the browser and the server fuzzer communicate was needed, to allow the websocket-sharp client to be fuzzed as well. Figure 5.2 shows how the communication is done between the Firefox browser and the autobahn fuzzing server.

First of all, the client has to request the number of test cases that have to be executed. Then, for each test case, it sends a new WebSocket connection passing as a parameter the test case number, and it will receive in response a message from the fuzzing server. The fuzzing server will then analyze the response of the client, to affirm if the client behaves correctly or not. Then, as the last thing, the client has to request to the server to save the result of the fuzzing in the report. Autobahn provides a user-friendly report to show the user which test cases are not passed and which were the messages sent between the client and the server. The parameter “agent” passed to each test case and to the report request is only needed for the generation of the report.

To be able to test the websocket-sharp library from the client side as well, a simple client has been implemented to communicate with the Autobahn server fuzzer correctly.

Below are reported some bugs found by Autobahn in the websocket-sharp library.

Continuation frame

The WebSocket protocol allows fragmenting a message in pieces. Autobahn found that the client does not close the connection returning a protocol error when a continuation frame arrives, also if there is nothing to continue. This problem was fixed by checking that if a frame with opcode equal to “0x00” (continuation frame) is received, the previous frame received must have the FIN bit set to false. If this rule is not respected, the connection has to be

(51)

5.3. MAN IN THE MIDDLE FUZZING 51 closed.

Text message

The client accepts messages that are invalid utf8, instead of failing and closing the connection. The C# class “Encoding.UTF8” causes the problem

because it did not throw any exception when a parsed string is UTF-8 invalid. For this reason the class “Encoding.UTF8” has been substituted with the C# class “UTF8Encoding”, which gives the possibility to set a parameter

to throw an exception if the string is not valid. That exception is then caught by the websocket-sharp library and returns a protocol error automatically.

Close message

The bugs found by Autobahn and the fixes done in this section are very similar to the ones already explained in section 5.2.1, where it is explained the fuzzing of the libsoup library.

In fact, websocket-sharp accepts close frames that have an invalid UTF-8 payload and also allows invalid close code. The first problem was caused by the fact that websocket-sharp was not parsing the content of the payload. Instead for the second problem, the one of invalid close code, this was fixed with a simple check on the value of the close code.

5.3

Man in the middle fuzzing

In the previous section, it was explained how Autobahn was used during the internship. The problem is that, as explained in section 2.4.2, Autobahn cannot be considered as a real fuzzer, because it does not mutate or generate the input data in any way. For this reason, we have decided to stress the

(52)

52 CHAPTER 5. WEBSOCKET FUZZING

Figure 5.3: HTTP Upgrade process with the MITM

WebSocket libraries with a fuzzer to understand if the libraries were robust or no.

It has been decided to implement a man in the middle (MITM) in between the two endpoints to fuzz the libsoup server and the websocket-sharp client. A handshake needs to be done to establish a WebSocket connection. Figure 5.3 shows how this handshake is done to establish the connection. The client sends the handshake, which is an HTTP upgrade header to the MITM. The MITM has to modify the destination and the origin URL to send a valid handshake to the server. Regarding the response, the MITM does not have to do any modification on the HTTP upgrade response. It is essential that no fuzzing is done to the handshake messages to establish the WebSocket connection correctly.

Once the WebSocket connection is established, a first type of fuzzing of the message was implemented. Indeed, the fuzzer flips one bit of the WebSocket message at random. This bit flip may cause a protocol error followed by the dropping of the WebSocket connection. For this reason, the websocket-sharp client was modified to establish a new connection with the server, when one of the two endpoints closes the WebSocket connection.

(53)

5.3. MAN IN THE MIDDLE FUZZING 53 This fuzzer has worked for approximately two days, mutating the message between the client and the server, but did not find any crash. The fact that no bugs were found, lead to the creation of a new fuzzer.

The new fuzzer was implemented to fuzz the messages in a “focused” way. Instead of randomly permute a bit, it changes only the bits of a component of the WebSocket message: FIN, RSV, opcode, MASK, Payload len, masking key or Payload Data. All these components had been explained better in section 5.1. This fuzzer can fuzz only the messages that the client sends to the server or the other way round or both at the same time.

Also, this fuzzer has fuzzed the WebSocket messages for approximately two days, fuzzing one component at a time and fuzzing only in one direction of the communication client-server at a time, but, as for the random fuzzer, also this fuzzer did not found any crash.

(54)
(55)

CHAPTER

6

DCV protocol fuzzing

As the last thing of the internship, DCV was fuzzed. As for WebSocket, it was decided to realize a MITM to fuzz the DCV protocol. This time, however, the MITM would have used a WebSocket library to handle this protocol instead of treating the WebSocket messages through the TCP sock-ets as it was done for the WebSocket fuzzing. A complete fuzz on libsoup and websocket-sharp was expected to use one of these WebSocket libraries. Pre-viously though, libsoup and websocket-sharp have not been fuzzed and fixed on both endpoints completely. For what regards libsoup, only the server side was tested, while for websocket-sharp only the client side was fuzzed. The library that has been chosen to be used for the MITM is the websocket-sharp library because developing in C# is way faster than doing it in C.

To be sure that also the server side of the websocket-sharp library follows the rules of the WebSocket protocol and that it was also robust, a simple echo server was built using websocket-sharp. Then, Autobahn was run to fuzz this server and no error was found. This fuzzing was requested to be sure that the MITM was using a robust library.

Since the WebSocket has already been fuzzed in the previous section, the goal of this chapter is to send malformed messages respecting the WebSocket

(56)

56 CHAPTER 6. DCV PROTOCOL FUZZING protocol.

Moreover, since we were not interested in breaking the protobuf messages, because they have already been fuzzed during the internship, the MITM should send only valid protobuf messages.

To sum up, the goal of the fuzzer is to send valid WebSocket messages with valid DCV messages and valid protobuf messages. The task of the fuzzer is to send corner cases values: consider for instance the case of the mouse position, we were interested in seeing if there were any crashes if the mouse position notified by the client to the server was out of bound for the remote display.

The MITM, implemented to fuzz the DCV protocol, is structured in the following way:

• saves all the valid strings and byte arrays, sent between the client and the server, contained in the protobuf messages and stores this informa-tion in a JSON file. Those strings were collected by putting in between a DCV client and a server, a MITM, that would have not fuzzed any-thing. Then I have manually interacted with the client to permit to the MITM to collect the maximum number of valid strings;

• generates new DCV messages using the JSON file mentioned above, for the protobuf messages and randomly mutate/generate the payload. Those messages are mutated by the MITM using Radamsa. More in-formation on Radamsa is in the section 2.4.3 where I have listed the fuzzer used during the internship. The user can specify how these mes-sages have to be generated in the configuration file. More information will be in the next section;

• saves all the fuzzed protobuf messages sent in both directions on a log file;

(57)

6.1. CONFIGURATION OF THE FUZZER 57 • detects crashes on the server and client side, thanks to the already

available crash dumps and logs.

6.1

Configuration of the fuzzer

Below is shown an example of a JSON file configuration that needs to be passed to the fuzzer.

1 { 2 ” u r l S e r v e r ” : ” 1 2 7 . 0 . 0 . 1 : 8 4 4 3 ” , 3 ” u r l P r o x y ” : ” 1 2 7 . 0 . 0 . 1 : 9 0 0 1 ” , 4 ” f u z z i n g C h a n n e l ” : [ ” d i s p l a y ” , ” i n p u t ” ] , 5 ” m a n t a i n O r d e r ” : t r u e , 6 ” s e n d A d d i t i o n a l M e s s ” : f a l s e , 7 ” p r o b a b i l i t y T o F u z z M e s s ” : 0 . 5 , 8 ” f u z z P a y l o a d ” : f a l s e , 9 ” p a t h L o g ” : ” . . / . . / . . / l o g / ” , 10 ” p a t h J s o n ” : ” . . / . . / . . / j s o n / ” , 11 ” pathRadamsa ” : ” . . / . . / . . / r a d a m s a / r a d a m s a . e x e ” , 12 ” c e r t P f x ” : ” . . / . . / . . / c e r t / m y c e r t . p f x ” , 13 ” c e r t P a s s ” : ” p a s s w o r d ” 14 }

In this configuration file the following parameters are specified: • “urlServer” is the URL of the DCV server to be fuzzed;

• “urlProxy” is the URL where the MITM will listen for new requests; • “fuzzingChannel” is an array of channels to be fuzzed;

• “mantainOrder” is a parameter that, if set to true, maintains the order of messages sent between the client and the server;

(58)

58 CHAPTER 6. DCV PROTOCOL FUZZING • “sendAdditionalMess” is a parameter that, if set to true, will allow the fuzzer to automatically generate messages also if the client and the server are not sending anything;

• “probabilityToFuzzMess” is the probability to fuzz the protobuf mes-sages that are passing through the MITM. If set to 1 it will fuzz all the protobuf messages;

• “fuzzPayload” is a parameter that, if set to true, will allow the MITM to fuzz the payload of the DCV messages;

• “pathLog” is the path where to save the logs;

• “pathJson” is the path where to save/load the JSON file that contains all the valid strings and byte arrays sent between the client and the server;

• “pathRadamsa” is the path where Radamsa is located;

• “certPfx” is the path where is located the certificate file in order to establish HTTPS connection with the DCV client;

• “certPass” is the password for the “.pfx” certificate.

6.2

Man in the middle

In this section, it is explained: how the MITM has been built, how the classes interact with each other and which design patterns have been used.

6.2.1

Structure

(59)

6.2. MAN IN THE MIDDLE 59

Figure 6.1: UML diagram of the DCV MITM fuzzer

Proxy

This class is the one that contains the entry point of the application. The main starts parsing the configuration file, passed as an argument from the user, using a function of the Configuration class. After this operation, the main creates two WebSocket services, one for the data channels and one for the authentication channel. Each time the services receive a request for a new WebSocket connection, a new instance of the class AuthChannel or DataChannel is created depending on the type of the service.

The Proxy class has also a function to redirect HTTP requests to the server and to redirect the HTTP responses to the client. The DCV protocol, in fact, uses the HTTP protocol to send the files (HTML, CSS, JavaScript

(60)

60 CHAPTER 6. DCV PROTOCOL FUZZING etc.) needed by the web client. The HTTP protocol is also used to allow users, that are using a client, to download files (PDF, Word etc.) which are on the server.

Figure 6.2: Detailed UML diagram of the Proxy class

DcvPacket

The DcvPacket class is used to deserialize and reserialize the DCV message shown in figure 3.2. A DCV message is composed of a body which contains the protobuf message, and a binary payload that varies from channel to channel. More information on DCV is on chapter 3.

This class has two methods, the first one serializes the DCV messages while the second one deserializes the DCV messages. The first method se-rializes a DCV message from two binary arrays, the message body and the payload. The other method instead that deserialize a DCV message, needs some internal variables to wait for the entire DCV message. Indeed, some DCV messages are sent in different WebSocket packets.

PacketHandler

The PacketHandler class and its subclasses are used by the Channel class to modify or analyze the message body and the payload of a DCV message. The PacketHandler class returns the message body and the payload, when they are ready, with a callback to the Channel class.

(61)

6.2. MAN IN THE MIDDLE 61

Figure 6.3: Detailed UML diagram of the DcvPacket class

The PacketHandler class does not modify those two binary arrays (the body and the payload), and this class is useful in the case in which the user does not want to fuzz the messages passing through a specific channel. For example, if the user wants to fuzz the “display” channel, and he does not want to fuzz the “auth” channel, otherwise the display channel connection will never be established, he can use a PacketHandler class for the “auth” channel.

The subclasses of PacketHandler can override two methods to modify or analyze the message body and the payload of a DCV message. One method to manage the messages coming from the client side and the other one for the messages arriving from the server. These methods returns the modified messages to the Channel class through a callback.

Channel

This class is abstract and needs to be extended by the DataChannel or the AuthChannel. The goal of this class is to act as an intermediary for the Web-Socket messages sent between the client and the server. When the content of a WebSocket message is received, it is deserialized by one of the DcvPacket instances; in fact, the Channel class has a reference to two DcvPacket classes,

(62)

62 CHAPTER 6. DCV PROTOCOL FUZZING

Figure 6.4: Detailed UML diagram of the PacketHandler class

one for the client side and one for the server side. The deserialized DCV mes-sages are then passed to a PacketHandler class, or one of its subclasses, to modify or analyze the content. Once the Channel class receives the message body and the payload with the callback, those two binary arrays (the body and the payload) are reserialized in a DCV message with the DcvPacket class. The DCV message is then sent with the WebSocket protocol to the other endpoint.

Riferimenti

Documenti correlati

In this regard I propose a modern analysis - critical and social - of the current contexts of detention, therefore, I evaluate the state of the penitentiary

E’ stato valutato opportuno e interessante studiare la possibilità d’integrazione di tali sistemi, considerando che tale esigenza viene suggerita dai seguenti vantaggi:

In that case, we could say that the external control, and in particular the accounting control entrusted to the external auditor, should not be mandatory, but it should be quite

of Mathematics, Computer Science, and Physics University of Udine, Italy. Course on Data Management for

Such a selection is now based on the maximum check interval value present in 

The first step of the work has referred to the definition and analysis of the maximum loads the catamaran is subjected to, during the most common racing

The main result of this paper is a criterion for irrational sequences which consist of rational numbers for which the corresponding convergent series does not converge very

To this end, the Directive establishes that credit institutions must largely offer payment accounts “with basic features in order to allow them to open, operate and close the