{"record":{"id":"3390a7ba9cf6ec59","repo":"txthinking/brook","slug":"expired-request-3390a7","errorCode":null,"errorMessage":"Expired request","messagePattern":"Expired request","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"simplestreamserver.go","lineNumber":69,"sourceCode":"\tif bytes.Compare(password, b[:32]) != 0 {\n\t\tx.BP2048.Put(b)\n\t\tWaitReadErr(s.Client)\n\t\treturn nil, errors.New(\"Password is wrong\")\n\t}\n\tl := int(binary.BigEndian.Uint16(b[32:34]))\n\tif l > 2048 {\n\t\tx.BP2048.Put(b)\n\t\treturn nil, errors.New(\"data too long\")\n\t}\n\tif _, err := io.ReadFull(s.Client, b[:l]); err != nil {\n\t\tx.BP2048.Put(b)\n\t\treturn nil, err\n\t}\n\ti := int64(binary.BigEndian.Uint32(b[:4]))\n\tif time.Now().Unix()-i > 60 {\n\t\tx.BP2048.Put(b)\n\t\tWaitReadErr(s.Client)\n\t\treturn nil, errors.New(\"Expired request\")\n\t}\n\tif i%2 == 0 {\n\t\ts.network = \"tcp\"\n\t\ts.RB = b\n\t\ts.WB = x.BP2048.Get().([]byte)\n\t}\n\tif i%2 == 1 {\n\t\ts.network = \"udp\"\n\t\ts.Timeout = udptimeout\n\t\ts.RB = x.BP65507.Get().([]byte)\n\t\tcopy(s.RB[:l], b[:l])\n\t\tx.BP2048.Put(b)\n\t\ts.WB = x.BP65507.Get().([]byte)\n\t}\n\ts.dst = socks5.ToAddress(s.RB[4], s.RB[4+1:l-2], s.RB[l-2:])\n\treturn ServerGate(s)\n}\n","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/txthinking/brook/blob/5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8/simplestreamserver.go#L51-L87","documentation":"NewSimpleStreamServer reads a 4-byte BigEndian Unix timestamp at the start of the payload (b[:4]) and rejects the request if it is more than 60 seconds in the past, returning 'Expired request'. This prevents replay of captured handshakes.","triggerScenarios":"The timestamp decoded from b[:4] satisfies time.Now().Unix()-i > 60 — replayed handshake capture, client clock more than a minute behind the server, or delayed delivery of the request.","commonSituations":"Clock skew between client and server (NTP off, VM suspended); resending a recorded handshake (replay attack); long-lived queued/proxied connections exceeding the 60s window; client caching the timestamp across reconnects.","solutions":["Enable NTP on the client so clocks agree within 60 seconds","Regenerate the timestamp (time.Now().Unix()) for every handshake, honoring the tcp odd/even parity scheme","Do not reuse cached handshake buffers containing old timestamps on reconnect","Investigate and eliminate network delays exceeding 60s (offline queuing, buffering proxies)"],"exampleFix":"// before (client reuses old handshake buffer)\nconn.Write(cachedHandshake)\n// after\ni := time.Now().Unix()\nbinary.BigEndian.PutUint32(b[36:40], uint32(i))\nconn.Write(b[:36+4+len(dst)])","handlingStrategy":"validation","validationCode":"i := time.Now().Unix()\nif binary.BigEndian.Uint32(handshake[36:40]) != uint32(i) || time.Now().Unix()-int64(i) > 55 {\n    return errors.New(\"handshake timestamp stale; rebuild before sending\")\n}","typeGuard":null,"tryCatchPattern":"s, err := NewSimpleStreamServer(password, conn, ...)\nif err != nil && err.Error() == \"Expired request\" {\n    log.Printf(\"expired handshake from %s (clock skew or replay?)\", conn.RemoteAddr())\n    conn.Close()\n}","preventionTips":["Run NTP on every client host","Build the handshake immediately before sending; never reuse buffers","Account for the tcp odd/even timestamp parity when constructing the timestamp","Alert on spikes of expired handshakes — they indicate clock drift or replay attempts"],"tags":["replay-protection","timestamp","clock-skew","handshake"],"backgroundTag":"request-timestamp-expired","analyzedSha":"5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8","analyzedAt":"2026-09-06T04:35:00.432Z","contentChangedAt":"2026-09-06T04:35:00.432Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}