I am reporting a reproducible cryptographic arithmetic correctness and availability bug in the currently distributed TeamSpeak 3 Server 3.13.8 FreeBSD AMD64 package. This deserves a maintenance fix even if long-term FreeBSD support has ended.
The server can consume a CPU core during automatic SSH host-key generation. Supplying an externally generated, readable RSA key avoids that generation step but does not fix SSH Query: key exchange can fail because the bundled library cannot correctly sign the session identifier. The library’s own fixed-vector modular-exponentiation self-test fails, independently of Query credentials, networking, database contents or a running TS3 instance.
I have isolated an erroneous Montgomery-reduction calculation in the shipped libts3_ssh.so. A narrowly targeted, byte-guarded correction makes the failing arithmetic test pass, restores RSA-SHA512 SSH key exchange, and allows the actual TS3 server process to generate its default RSA4096 host key successfully.
Support policy and request
I have read the previous reply about discontinuing future FreeBSD support. However, the official download page still offers FreeBSD Server 3.13.8 as of 19 October 2026.
A narrowly scoped maintenance rebuild of that existing artifact would address this serious defect while leaving the broader platform-support policy unchanged. Please provide either:
- a corrected FreeBSD server package or an ABI-compatible replacement
libts3_ssh.so;
- or the matching library source changes, configuration and build instructions needed to produce such a replacement.
Please also run the bundled MPI self-tests in release validation. A deterministic arithmetic failure inside a cryptographic dependency should not remain in a downloadable release.
The other report of sustained FreeBSD CPU usage may be related, but I cannot establish that it has the same cause without comparing its binary hash/self-test result.
Affected artifact and environment
Tested on FreeBSD 15.1-RELEASE-p5 AMD64, with TS3 3.13.8 installed via the FreeBSD port (teamspeak3-server-3.13.8,1). The port installs the vendor binaries. More importantly, the same defective code is present in the official archive and its failure is independently reproduced from that archive; it is not limited to one installed or stripped copy.
Official archive:
https://files.teamspeak-services.com/releases/server/3.13.8/teamspeak3-server_freebsd_amd64-3.13.8.tar.bz2
Archive SHA256:
2bfd96ebfa1fca0c013a0a03d1741c0dc6d79e7c501a2fffb6248fdea58d4315
Unstripped libts3_ssh.so SHA256:
52cece666ac9e0798b2834fb080d51e378e8b86bbf839ed171cab0cde62f0a12
Original executable PT_LOAD SHA256:
a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59
The FreeBSD library contains libssh_0.8.4 and mbed TLS 2.7.16 version strings. These strings identify bundled components; they do not mean every build of upstream libssh 0.8.4 or Mbed TLS 2.7.16 has this defect.
Minimal arithmetic evidence
Calling the library’s built-in MPI self-test with the exact executable code hash verified first gives:
MPI test #1 (mul_mpi): passed
MPI test #2 (div_mpi): passed
MPI test #3 (exp_mod): failed
Unexpected error, return code = 00000001
MPI_SELF_TEST_RETURN=1
The self-test entry is image-relative RVA 0xbd3d0, with the upstream declaration int mbedtls_mpi_self_test(int verbose). It was identified using test strings/vectors and function-range information. It is not an exported stable libssh API, so the reproducer refuses unknown executable hashes before calling it.
This failure was observed on real FreeBSD and independently reproduced by replaying the exact official ELF instructions. No private key, password or network session is needed for this fixed-vector test. I include a small native reproducer below.
Exact emitted-code defect
The Montgomery multiplication helper starts at RVA 0xba240. A multiply-accumulate step has already updated d[0]. The sequence at RVA 0xba64a then adds the same product again while calculating the reduction factor:
mov rbx, [r12] ; B[0]
imul rbx, r10 ; u0 * B[0]
add rbx, [r14] ; d[0] already contains that product
It should use the already updated d[0] here. The tested workaround replaces that 11-byte sequence with a load and padding NOPs:
Before: 49 8b 1c 24 49 0f af da 49 03 1e
After: 49 8b 1e 90 90 90 90 90 90 90 90
mov rbx,[r14] followed by padding
For the official unstripped file the offset is 0xb964a. Do not apply that file offset blindly to another build. The attached patcher derives the file offset from the ELF segment and checks the entire original executable segment, exact instruction bytes and complete resulting file hash.
The instruction ordering is consistent with an inline-assembly memory-side-effect/compiler-reordering problem, but I do not have the vendor’s complete build source/options. The concrete emitted arithmetic error is confirmed; a particular compiler or upstream-source-level cause is not.
Before/after validation
With that correction, real FreeBSD testing passed all five built-in tests: multiplication, division, modular exponentiation, modular inverse and gcd. Independent instruction replay also checked 616 Montgomery outputs against integer reference calculations and 104 additional fixed-seed cases, spanning 13 limb lengths up to 4096 bits, including boundary and aliasing cases. This is not exhaustive cryptographic validation.
In isolated library tests, both an existing RSA key and a newly generated RSA4096 key completed pinned-host-key RSA-SHA512 SSH key exchange. On the actual server, after installing the corrected library and retiring the previous manual key, TS3 itself generated RSA4096 and started normally. Startup plus key readiness took about five seconds; that is not a standalone key-generation benchmark.
The generated FreeBSD key initially had mode 0644; it was restricted to 0600 with the correct service owner/group. Real IPv4 and IPv6 SSH Query connections then verified the new host key and reached password authentication. In a short post-connection CPU sample, displayed TS3 threads were at 0.00–0.06%, with the final two system samples 99.8% and 99.9% idle. Full Query account login, long-term stability and restart persistence are not claimed from these results.
Other platforms: distinguish results from assumptions
| Official 3.13.8 platform |
Evidence available |
Result for this specific failure |
| FreeBSD AMD64 |
Real server/library tests and instruction replay |
Confirmed; fixed by the guarded correction above |
| Windows AMD64 |
Native temporary server test |
Automatic RSA4096 generation and pinned RSA-SHA512 handshake passed; this key-generation failure was not reproduced |
| Linux AMD64 |
Exact library MPI self-test in instruction replay |
All five passed; no native Linux server integration test performed |
| macOS Intel |
Exact Mach-O library MPI self-test in instruction replay |
All five passed; no native macOS server integration test performed |
The emulator mocks allocation/memory/stdio helpers; the macOS run additionally supplies compiler-runtime unsigned 128-bit division/modulo helpers using integer arithmetic. It does not emulate a complete OS, network service or RSA session. Therefore these results do not certify that other platforms are bug-free. A separate Windows RSA-SHA256 signature failure was observed and remains outside the demonstrated FreeBSD Montgomery fix.
Unofficial, version-pinned workaround
An official compatible rebuild is preferable. This is not a confirmed exploitable-vulnerability/CVE claim: the demonstrated impacts are key-generation CPU consumption, failing arithmetic and broken SSH Query signatures. I have not established key recovery, authentication bypass or remote code execution.
The script below only creates a separate corrected library. It does not download anything, replace the live library, stop/restart a server or touch keys/configuration. It refuses different code, already patched input, modified non-code contents, in-place output and overwriting an existing output file. It supports exactly the identified official and installed/stripped layouts.
Byte-guarded offline patcher (Python 3.9+, standard library only)
"""Create a separate, hash-guarded correction of the known TS3 3.13.8 library.
Python 3.9+. No dependencies, downloads, service control or private-key access.
Without --output this only checks eligibility. Never edits the input in place.
This is an unofficial workaround for exactly the identified FreeBSD AMD64 build.
"""
import argparse
import hashlib
import os
from pathlib import Path
import struct
ORIGINAL_RX = "a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59"
CORRECTED_RX = "e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370"
CORRECTED_FILES = {
"08a76c8d781bc9f07cb4dd74f248adb65aa5034268681e3687e189a961e10e9f",
"081f7d87ba920572d329e5dd0da52e23a8c10239d6ca7feee427f7504258e194",
}
BEFORE = bytes.fromhex("49 8b 1c 24 49 0f af da 49 03 1e")
AFTER = bytes.fromhex("49 8b 1e 90 90 90 90 90 90 90 90")
def digest(data):
return hashlib.sha256(data).hexdigest()
def corrected_copy(data):
if len(data) < 64 or len(data) > 16 * 1024 * 1024:
raise ValueError("Unsupported input size")
h = struct.unpack_from("<16sHHIQQQIHHHHHH", data)
if (h[0][:7] != b"\x7fELF\x02\x01\x01" or h[1] != 3 or h[2] != 62
or h[9] != 56 or not 0 < h[10] <= 64
or h[5] > len(data) or h[10] * 56 > len(data) - h[5]):
raise ValueError("Expected the known ELF64 little-endian AMD64 shared library")
candidates = []
for i in range(h[10]):
ph = struct.unpack_from("<IIQQQQQQ", data, h[5] + i * 56)
if ph[0] == 1 and ph[1] == 5 and ph[3] == 0x62410 and ph[5] == 0x94FE0:
candidates.append(ph)
if len(candidates) != 1:
raise ValueError("Unexpected executable segment layout")
ph = candidates[0]
offset, size = ph[2], ph[5]
if offset > len(data) or size > len(data) - offset:
raise ValueError("Executable segment is outside the file")
if digest(data[offset:offset + size]) != ORIGINAL_RX:
raise ValueError("Original executable hash mismatch; do not force this patch")
position = offset + 0xBA64A - ph[3]
if data[position:position + len(BEFORE)] != BEFORE:
raise ValueError("Instruction bytes mismatch")
result = bytearray(data)
result[position:position + len(BEFORE)] = AFTER
result = bytes(result)
if digest(result[offset:offset + size]) != CORRECTED_RX:
raise ValueError("Corrected executable hash mismatch")
if digest(result) not in CORRECTED_FILES:
raise ValueError("Whole-file result is not one of the two identified layouts")
return result
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input", type=Path)
parser.add_argument("--output", type=Path, help="create a NEW separate file; never overwrite")
args = parser.parse_args()
if args.output is not None and args.input.resolve() == args.output.resolve():
parser.error("Input and output must be different paths")
if args.input.stat().st_size > 16 * 1024 * 1024:
parser.error("Unsupported input size")
original = args.input.read_bytes()
try:
corrected = corrected_copy(original)
except ValueError as exc:
parser.error(str(exc))
print("ORIGINAL_FILE_SHA256=" + digest(original))
print("CORRECTED_FILE_SHA256=" + digest(corrected))
print("CORRECTED_RX_SHA256=" + CORRECTED_RX)
if args.output is None:
print("ELIGIBLE_CHECK_ONLY=1")
return
fd = os.open(args.output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
with os.fdopen(fd, "wb") as stream:
stream.write(corrected)
stream.flush()
os.fsync(stream.fileno())
if digest(args.output.read_bytes()) != digest(corrected):
raise RuntimeError("Output verification failed; do not install the output")
print("SEPARATE_CORRECTED_FILE_CREATED=1")
if __name__ == "__main__":
main()
Example, in an extracted official package directory:
python3 patch_freebsd_ts3_ssh.py ./libts3_ssh.so
python3 patch_freebsd_ts3_ssh.py ./libts3_ssh.so --output ./libts3_ssh.corrected.so
Expected corrected hashes:
Corrected executable PT_LOAD:
e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370
Corrected complete official/unstripped file:
08a76c8d781bc9f07cb4dd74f248adb65aa5034268681e3687e189a961e10e9f
Corrected complete tested installed/stripped file:
081f7d87ba920572d329e5dd0da52e23a8c10239d6ca7feee427f7504258e194
Installation must use your service’s own stop/start procedure and library location, preserve a verified original-library recovery copy, and preserve existing host keys unless you deliberately choose to replace them. This is a package-owned file and upgrades can overwrite it. The workaround is not a drop-in replacement for arbitrary versions/builds and is not an audit of the whole old dependency stack.
Native FreeBSD fixed-vector reproducer (no private keys or running server required)
Save as ts3_freebsd_mpi_repro.c:
/* FreeBSD-only public diagnostic for the pinned TS3 3.13.8 vendor library.
* No keys, database, credentials, networking, or service changes.
* A complete executable-segment hash gates the internal self-test address.
*/
#include <sys/types.h>
#include <dlfcn.h>
#include <elf.h>
#include <sha256.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static void deadline(int sig) {
(void)sig;
(void)write(2, "BACKEND_PROBE_TIMEOUT\n", sizeof("BACKEND_PROBE_TIMEOUT\n") - 1);
_exit(124);
}
int main(int argc, char **argv) {
if (argc != 2) { fputs("Usage: mpi-repro /absolute/path/libts3_ssh.so\n", stderr); return 2; }
setbuf(stdout, NULL);
signal(SIGALRM, deadline);
alarm(10);
void *lib = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
if (!lib) { fprintf(stderr, "DLOPEN_ERROR: %s\n", dlerror()); return 2; }
void *rng_symbol = dlsym(lib, "ssh_get_random");
void *init_symbol = dlsym(lib, "ssh_init");
void *finish_symbol = dlsym(lib, "ssh_finalize");
if (!rng_symbol || !init_symbol || !finish_symbol) {
fputs("MISSING_SYMBOL\n", stderr); return 2;
}
Dl_info info;
if (!dladdr(rng_symbol, &info) || !info.dli_fbase) {
fputs("LIBRARY_BASE_ERROR\n", stderr); return 2;
}
uintptr_t base = (uintptr_t)info.dli_fbase;
const Elf64_Ehdr *header = (const Elf64_Ehdr *)base;
if (memcmp(header->e_ident, ELFMAG, SELFMAG) != 0 ||
header->e_ident[EI_CLASS] != ELFCLASS64 || header->e_machine != EM_X86_64 ||
header->e_phentsize != sizeof(Elf64_Phdr) || header->e_phnum > 64 ||
header->e_phoff > 0x1000 ||
header->e_phoff + header->e_phnum * sizeof(Elf64_Phdr) > 0x1000 ||
(uintptr_t)rng_symbol - base != 0x8c8b0) {
fputs("UNSUPPORTED_LIBRARY_LAYOUT\n", stderr); return 2;
}
const Elf64_Phdr *segments = (const Elf64_Phdr *)(base + header->e_phoff);
int matched = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
if (segments[i].p_type == PT_LOAD && segments[i].p_flags == (PF_R | PF_X) &&
segments[i].p_vaddr == 0x62410 && segments[i].p_filesz == 0x94fe0 &&
segments[i].p_memsz >= segments[i].p_filesz) matched = 1;
}
if (!matched) { fputs("UNSUPPORTED_EXECUTABLE_SEGMENT\n", stderr); return 2; }
char hash[65];
if (!SHA256_Data((const unsigned char *)(base + 0x62410), 0x94fe0, hash)) {
fputs("CODE_HASH_ERROR\n", stderr); return 2;
}
printf("VENDOR_EXECUTABLE_SHA256=%s\n", hash);
if (strcmp(hash, "a10e5c11186344d73c4be687487c6908fdd6e7d88eaaaa00e0bd722d2ba75c59") != 0 &&
strcmp(hash, "e654636a42fcf409df134d5a168a3a13e5336f2ecdc3f50af1414f75e80d1370") != 0) {
fputs("VENDOR_CODE_MISMATCH: internal test NOT called\n", stderr); return 2;
}
puts("VENDOR_CODE_VERIFIED=1");
int (*init)(void), (*finish)(void), (*mpi_test)(int);
_Static_assert(sizeof(init) == sizeof(init_symbol), "FreeBSD pointer size");
memcpy(&init, &init_symbol, sizeof(init));
memcpy(&finish, &finish_symbol, sizeof(finish));
void *test_address = (void *)(base + 0xbd3d0);
_Static_assert(sizeof(mpi_test) == sizeof(test_address), "FreeBSD pointer size");
memcpy(&mpi_test, &test_address, sizeof(mpi_test));
int rc = init();
printf("SSH_INIT=%d\n", rc);
if (rc != 0) return 1;
puts("MPI_SELF_TEST_BEGIN");
/* Identified from .eh_frame FDE + test-vector/string cross references;
* prototype confirmed in Mbed TLS 2.7.16 bignum.c. No source copied here.
*/
rc = mpi_test(1);
printf("MPI_SELF_TEST_RETURN=%d\n", rc);
finish();
dlclose(lib);
alarm(0);
return rc == 0 ? 0 : 1;
}
From a directory containing the original or corrected library:
cc -std=c11 -O2 -Wall -Wextra -o mpi-repro ts3_freebsd_mpi_repro.c -lmd
ulimit -c 0
./mpi-repro "$(realpath ./libts3_ssh.so)"
./mpi-repro "$(realpath ./libts3_ssh.corrected.so)"
The original should fail at exp_mod; the corrected copy should pass all five tests. The underlying native test call/hash gates were exercised on real FreeBSD. This public command-line wrapper was statically checked but has not separately been compiled/run there; please report any build/runtime difference rather than bypassing a hash guard.
Investigation and drafting provenance
This work was carried out collaboratively. Most of the binary analysis, diagnostic and patch tooling, local comparison experiments, and report drafting were completed by OpenAI Codex, an AI assistant, under my direction. I performed the FreeBSD checks and deployment and supplied the observed terminal output. The real-system observations, instruction-emulation results and unverified conclusions are distinguished above; the AI contribution does not replace those validation boundaries.
I am sharing code and artifact identifiers, not vendor binaries, private keys, account details, server addresses or database files. Please confirm whether TeamSpeak can provide a corrected official FreeBSD artifact or compatible library rebuild.