shadow1ng/fscan · error
%s: %w [command_read_failed]
Error message
%s: %w [command_read_failed]
What it means
Reading a command line from the reverse-shell connection failed with a non-timeout error, so the command loop cannot continue. Timeout errors are explicitly filtered (continue); any other read failure is fatal and wrapped here.
Source
Thrown at plugins/local/reverseshell.go:144
// 发送提示符
prompt := fmt.Sprintf("%s> ", getCurrentDir())
_, _ = conn.Write([]byte(prompt))
// 设置读取超时,以便能响应 ctx 取消
_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))
// 读取命令
cmdLine, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
return nil
}
// 超时继续循环检查 ctx
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
continue
}
return fmt.Errorf("%s: %w", i18n.GetText("command_read_failed"), err)
}
// 清理命令
cmdLine = strings.TrimSpace(cmdLine)
if cmdLine == "" {
continue
}
// 检查退出命令
if cmdLine == "exit" {
_, _ = conn.Write([]byte("Goodbye!\n"))
return nil
}
// 执行命令
result := p.executeCommand(cmdLine)
// 发送结果View on GitHub (pinned to 95cc12e753)
Solutions
- Check whether the peer connection was reset (net.OpError / ECONNRESET) and reconnect the reverse shell.
- Add periodic keepalive traffic or shorter SetReadDeadline cycles so dead connections are detected as timeouts, not fatal errors.
- Treat EOF distinctly from resets: EOF usually means clean peer close — restart the session instead of reporting an error.
- Configure TCP keepalives on the conn (conn.(*net.TCPConn).SetKeepAlive(true)).
Example fix
// before
return fmt.Errorf("%s: %w", i18n.GetText("command_read_failed"), err)
// after — handle EOF/reset as session end, not a hard error
if errors.Is(err, io.EOF) {
return nil // peer closed cleanly
}
return fmt.Errorf("%s: %w", i18n.GetText("command_read_failed"), err) Defensive patterns
Strategy: try-catch
Validate before calling
// ensure liveness before looping
if tc, ok := conn.(*net.TCPConn); ok {
_ = tc.SetKeepAlive(true)
_ = tc.SetKeepAlivePeriod(30 * time.Second)
} Type guard
func isTimeout(err error) bool {
var nerr net.Error
return errors.As(err, &nerr) && nerr.Timeout()
} Try / catch
_, err := reader.ReadString('\n')
switch {
case errors.Is(err, io.EOF):
return nil // clean close: restart session
case isTimeout(err):
continue
default:
return fmt.Errorf("%s: %w", i18n.GetText("command_read_failed"), err)
} Prevention
- Enable TCP keepalives on the shell connection
- Distinguish EOF from resets in the read loop
- Set explicit read deadlines so hangs surface as timeouts
- Wrap the whole session in reconnect-with-backoff logic
When it happens
Trigger: The read call on the command stream returns an error that is neither EOF-expected shutdown nor a net.Error timeout — e.g. connection reset, broken pipe after the peer dropped, TLS termination.
Common situations: C2 closed the connection mid-session; NAT/firewall dropped the idle connection; peer sent RST after a hung session exceeded idle timeout.
Related errors
- %s: %w [connection_failed_plain]
- %s: %w [socks5_handshake_read_failed]
- short fragment header: %w
- mcs sendConnectInitial write error %v
- ReadConnectResponse %v
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/a8ef0c580d0b5ffa.
Report an issue: GitHub.