{"record":{"id":"a8ef0c580d0b5ffa","repo":"shadow1ng/fscan","slug":"s-w-command-read-failed","errorCode":null,"errorMessage":"%s: %w [command_read_failed]","messagePattern":"(.+?): %w \\[command_read_failed\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/local/reverseshell.go","lineNumber":144,"sourceCode":"\t\t// 发送提示符\n\t\tprompt := fmt.Sprintf(\"%s> \", getCurrentDir())\n\t\t_, _ = conn.Write([]byte(prompt))\n\n\t\t// 设置读取超时，以便能响应 ctx 取消\n\t\t_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))\n\n\t\t// 读取命令\n\t\tcmdLine, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\t// 超时继续循环检查 ctx\n\t\t\tvar netErr net.Error\n\t\t\tif errors.As(err, &netErr) && netErr.Timeout() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"command_read_failed\"), err)\n\t\t}\n\n\t\t// 清理命令\n\t\tcmdLine = strings.TrimSpace(cmdLine)\n\t\tif cmdLine == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// 检查退出命令\n\t\tif cmdLine == \"exit\" {\n\t\t\t_, _ = conn.Write([]byte(\"Goodbye!\\n\"))\n\t\t\treturn nil\n\t\t}\n\n\t\t// 执行命令\n\t\tresult := p.executeCommand(cmdLine)\n\n\t\t// 发送结果","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/local/reverseshell.go#L126-L162","documentation":"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.","triggerScenarios":"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.","commonSituations":"C2 closed the connection mid-session; NAT/firewall dropped the idle connection; peer sent RST after a hung session exceeded idle timeout.","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))."],"exampleFix":"// before\nreturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"command_read_failed\"), err)\n// after — handle EOF/reset as session end, not a hard error\nif errors.Is(err, io.EOF) {\n\treturn nil // peer closed cleanly\n}\nreturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"command_read_failed\"), err)","handlingStrategy":"try-catch","validationCode":"// ensure liveness before looping\nif tc, ok := conn.(*net.TCPConn); ok {\n\t_ = tc.SetKeepAlive(true)\n\t_ = tc.SetKeepAlivePeriod(30 * time.Second)\n}","typeGuard":"func isTimeout(err error) bool {\n\tvar nerr net.Error\n\treturn errors.As(err, &nerr) && nerr.Timeout()\n}","tryCatchPattern":"_, err := reader.ReadString('\\n')\nswitch {\ncase errors.Is(err, io.EOF):\n\treturn nil // clean close: restart session\ncase isTimeout(err):\n\tcontinue\ndefault:\n\treturn fmt.Errorf(\"%s: %w\", i18n.GetText(\"command_read_failed\"), err)\n}","preventionTips":["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"],"tags":["network","io","reverseshell"],"backgroundTag":"broken-pipe","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}