The shore
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec molestie.
129 | Sed aliquam sem ut arcu.
painted by: r4w8173
├── main.go ├── .gitignore ├── database ├── models │ ├── dummy.go │ ├── configuration.go │ ├── collab.go │ ├── model.go │ ├── user.go │ ├── scan.go │ ├── signature.go │ ├── record.go │ └── oob.go ├── connect.go ├── scan.go ├── record.go ├── user.go ├── config.go ├── dnsbin.go ├── sign.go └── collaborator.go ├── libs ├── passive.go ├── version.go ├── signature.go ├── options.go ├── http.go └── banner.go ├── Dockerfile ├── .github └── FUNDING.yml ├── core ├── generator_test.go ├── sender_test.go ├── background.go ├── variables_test.go ├── update.go ├── analyze.go ├── config.go ├── passive_test.go ├── middleware.go ├── conclusions.go ├── signature.go ├── variables.go ├── detecter.go ├── parser_test.go ├── passive.go ├── parser.go └── generator.go ├── LICENSE ├── go.mod ├── cmd ├── server_test.go ├── server.go ├── root.go ├── config.go └── scan.go ├── utils ├── log.go └── helper.go ├── server ├── controllers.go ├── api.go └── routers.go ├── sender ├── chrome.go └── sender.go └── README.md /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/jaeles-project/jaeles/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .goreleaser.yml 3 | .idea 4 | .vscode 5 | Makefile 6 | dist 7 | out 8 | passive-* 9 | old-out 10 | http-out 11 | test-sign 12 | test-scripts -------------------------------------------------------------------------------- /database/models/dummy.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Dummy just testing table 4 | type Dummy struct { 5 | Model 6 | 7 | Name string `gorm:"type:varchar(30);"` 8 | Desc string `gorm:"type:varchar(30);"` 9 | } 10 | -------------------------------------------------------------------------------- /database/models/configuration.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Configuration used to store some config for entire tools 4 | type Configuration struct { 5 | Model 6 | Name string `gorm:"type:varchar(255);"` 7 | Value string `gorm:"type:varchar(255);"` 8 | } 9 | -------------------------------------------------------------------------------- /database/models/collab.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Collab store all collab server 4 | type Collab struct { 5 | Model 6 | Secret string `gorm:"type:varchar(255);"` 7 | InteractionString string `gorm:"type:varchar(255);"` 8 | Type string `gorm:"type:varchar(255);default:'burp'"` 9 | } 10 | -------------------------------------------------------------------------------- /libs/passive.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | // Passive struct for passive detection 4 | type Passive struct { 5 | Name string 6 | Desc string 7 | Rules []Rule 8 | } 9 | 10 | // Rule rule for run detections 11 | type Rule struct { 12 | ID string 13 | Reason string 14 | Detections []string 15 | } 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt-get update -y; apt-get install golang git -y; go get -u github.com/jaeles-project/jaeles; /root/go/bin/jaeles config -a init 4 | 5 | WORKDIR /root/go/bin/ 6 | 7 | VOLUME /root/go/bin/out 8 | VOLUME /root/.jaeles/ 9 | 10 | EXPOSE 5000 11 | 12 | CMD [ "/root/go/bin/jaeles", "server", "--host", "0.0.0.0" ] 13 | -------------------------------------------------------------------------------- /database/models/model.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | // Model is base table 6 | type Model struct { 7 | ID uint `gorm:"primary_key" json:"id"` 8 | CreatedAt time.Time `json:"created_at,omitempty"` 9 | UpdatedAt time.Time `json:"updated_at,omitempty"` 10 | DeletedAt *time.Time `sql:"index" json:"deleted_at,omitempty"` 11 | } 12 | -------------------------------------------------------------------------------- /database/models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // User define user table in db 4 | type User struct { 5 | Model 6 | Username string `gorm:"type:varchar(255);"` 7 | Password string `gorm:"type:varchar(255);"` 8 | Email string `gorm:"type:varchar(255);"` 9 | Secret string `gorm:"type:varchar(255);"` 10 | Token string `gorm:"type:varchar(255);"` 11 | } 12 | -------------------------------------------------------------------------------- /libs/version.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | const ( 4 | // VERSION current Jaeles version 5 | VERSION = "beta v0.4.4" 6 | // AUTHOR author of this 7 | AUTHOR = "@j3ssiejjj" 8 | // SIGNREPO default repo to get signature 9 | SIGNREPO = "https://github.com/jaeles-project/jaeles-signatures" 10 | // UIREPO default repo to get UI 11 | UIREPO = "https://github.com/jaeles-project/jaeles-plugins" 12 | ) 13 | -------------------------------------------------------------------------------- /libs/signature.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | // Signature base signature struct 4 | type Signature struct { 5 | ID string 6 | Type string 7 | Info struct { 8 | Name string 9 | Category string 10 | Risk string 11 | Tech string 12 | OS string 13 | } 14 | 15 | Origin Request 16 | Requests []Request 17 | RawRequest string 18 | Payloads []string 19 | Params []map[string]string 20 | Variables []map[string]string 21 | Target map[string]string 22 | } 23 | -------------------------------------------------------------------------------- /database/models/scan.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Scans store scan log 4 | type Scans struct { 5 | Model 6 | ScanID string `gorm:"type:varchar(255);"` 7 | ScanName string `gorm:"type:varchar(255);"` 8 | SignatureID string `gorm:"type:varchar(255);"` 9 | Input string `gorm:"type:longtext;default:''"` 10 | OutputDir string `gorm:"type:longtext;"` 11 | Mode string `gorm:"type:varchar(255);default:'scan'"` 12 | Level int `gorm:"type:int;default:'1'"` 13 | Source string `gorm:"type:longtext;default:''"` 14 | } 15 | -------------------------------------------------------------------------------- /database/models/signature.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Signature mapping signature to a db 4 | type Signature struct { 5 | Model 6 | SignID string `gorm:"type:varchar(100);unique_index"` 7 | Name string `gorm:"type:varchar(100);default:'single'"` 8 | Category string `gorm:"type:varchar(100);default:'general'"` 9 | Risk string `gorm:"type:varchar(100);default:'Info'"` 10 | Tech string `gorm:"type:varchar(100);default:'general'"` 11 | OS string `gorm:"type:varchar(100);default:'general'"` 12 | AsbPath string `gorm:"type:longtext;default:''"` 13 | 14 | Type string `gorm:"type:varchar(30);not null;default:'single'"` 15 | } 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: j3ssie 5 | open_collective: jaeles-project 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /core/generator_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jaeles-project/jaeles/libs" 7 | ) 8 | 9 | // func TestGeneratorPath(t *testing.T) { 10 | // var req libs.Request 11 | 12 | // req.URL = "http://example.com/rest/products/6/reviews" 13 | // reqs := RunGenerator(req, ".json", `Path("{{.payload}}", "*")`) 14 | // fmt.Println(reqs) 15 | // // for _, r := range reqs { 16 | // // if !strings.Contains(r.URL, ".json") { 17 | // // t.Errorf("Error generate Path") 18 | // // } 19 | // // } 20 | // } 21 | 22 | func TestGeneratorMethod(t *testing.T) { 23 | var req libs.Request 24 | req.Method = "GET" 25 | reqs := RunGenerator(req, `Method("PUT")`) 26 | for _, r := range reqs { 27 | if r.Method != "PUT" { 28 | t.Errorf("Error generate Path") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/models/record.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Record define record table in db 4 | type Record struct { 5 | Model 6 | ReqURL string `gorm:"type:longtext;"` 7 | ReqMethod string `gorm:"type:varchar(30);"` 8 | ReqBody string `gorm:"type:longtext;"` 9 | ReqRaw string `gorm:"type:longtext;"` 10 | 11 | StatusCode int `gorm:"type:int;"` 12 | ResBody string `gorm:"type:longtext;"` 13 | ResTime float64 `gorm:"type:float64;"` 14 | ResLength int `gorm:"type:int;"` 15 | ResRaw string `gorm:"type:longtext;"` 16 | Issues string `gorm:"type:varchar(100);"` 17 | Risk string `gorm:"type:varchar(100);"` 18 | ExtraOutput string `gorm:"type:longtext;"` 19 | ScanID string `gorm:"type:longtext;"` 20 | // Issues []string `gorm:"type:varchar(100);"` 21 | 22 | RawFile string `gorm:"type:longtext"` 23 | // ChechSum string `gorm:"type:varchar(30);unique_index"` 24 | } 25 | -------------------------------------------------------------------------------- /database/connect.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "github.com/jaeles-project/jaeles/database/models" 5 | "github.com/jinzhu/gorm" 6 | 7 | // load driver 8 | _ "github.com/jinzhu/gorm/dialects/sqlite" 9 | ) 10 | 11 | // DB global DB variable 12 | var DB *gorm.DB 13 | 14 | // InitDB init DB connection 15 | func InitDB(DBPath string) (*gorm.DB, error) { 16 | db, err := gorm.Open("sqlite3", DBPath) 17 | // turn this on when we go live 18 | // Disable Logger, don't show any log even errors 19 | db.LogMode(false) 20 | 21 | if err == nil { 22 | DB = db 23 | db.AutoMigrate(&models.Scans{}) 24 | db.AutoMigrate(&models.Record{}) 25 | db.AutoMigrate(&models.Signature{}) 26 | db.AutoMigrate(&models.User{}) 27 | db.AutoMigrate(&models.Configuration{}) 28 | db.AutoMigrate(&models.Dummy{}) 29 | // table for Out of band stuff 30 | db.AutoMigrate(&models.Collab{}) 31 | db.AutoMigrate(&models.OutOfBand{}) 32 | db.AutoMigrate(&models.ReqLog{}) 33 | return db, err 34 | } 35 | return nil, err 36 | } 37 | -------------------------------------------------------------------------------- /database/models/oob.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // OutOfBand table to store all OOB data 4 | type OutOfBand struct { 5 | Model 6 | Secret string `gorm:"type:varchar(255);"` 7 | InteractionString string `gorm:"type:varchar(255);"` 8 | Protocol string `gorm:"type:varchar(255);"` 9 | ClientIP string `gorm:"type:varchar(255);"` 10 | Time string `gorm:"type:varchar(255);"` 11 | Data string `gorm:"type:longtext;"` 12 | Type string `gorm:"type:varchar(255);default:'burp'"` 13 | } 14 | 15 | // ReqLog table to store request have OOB payload 16 | type ReqLog struct { 17 | Model 18 | Req string `gorm:"type:longtext;"` 19 | Res string `gorm:"type:longtext;"` 20 | ScanID string `gorm:"type:longtext;"` 21 | InteractionString string `gorm:"type:varchar(255);"` 22 | Secret string `gorm:"type:varchar(255);"` 23 | Data string `gorm:"type:longtext;"` 24 | Count int `gorm:"type:int;"` 25 | } 26 | -------------------------------------------------------------------------------- /database/scan.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strings" 7 | 8 | "github.com/google/uuid" 9 | "github.com/jaeles-project/jaeles/database/models" 10 | "github.com/jaeles-project/jaeles/libs" 11 | ) 12 | 13 | // CleanScans clean all scan 14 | func CleanScans() { 15 | var scans []models.Scans 16 | DB.Find(&scans) 17 | DB.Unscoped().Delete(&scans) 18 | } 19 | 20 | // NewScan select signature to gen request 21 | func NewScan(options libs.Options, mode string, signs []string) string { 22 | id, _ := uuid.NewUUID() 23 | rawScanID, _ := id.MarshalText() 24 | 25 | var shortSigns []string 26 | for _, signName := range signs { 27 | shortSigns = append(shortSigns, filepath.Base(signName)) 28 | } 29 | signatures := strings.Join(shortSigns[:], ",") 30 | 31 | signObj := models.Scans{ 32 | ScanID: fmt.Sprintf("%x", rawScanID), 33 | SignatureID: signatures, 34 | OutputDir: options.Output, 35 | Mode: mode, 36 | } 37 | DB.Create(&signObj) 38 | return fmt.Sprintf("%v", signObj.ScanID) 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 j3ssie 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 | 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jaeles-project/jaeles 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/Jeffail/gabs/v2 v2.4.0 7 | github.com/Shopify/yaml v2.1.0+incompatible 8 | github.com/appleboy/gin-jwt/v2 v2.6.3 9 | github.com/chromedp/cdproto v0.0.0-20200209033844-7e00b02ea7d2 10 | github.com/chromedp/chromedp v0.5.3 11 | github.com/fatih/color v1.9.0 12 | github.com/gin-contrib/cors v1.3.0 13 | github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2 14 | github.com/gin-gonic/gin v1.5.0 15 | github.com/go-resty/resty/v2 v2.1.0 16 | github.com/google/uuid v1.1.1 17 | github.com/gorilla/websocket v1.4.1 18 | github.com/jinzhu/gorm v1.9.12 19 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect 20 | github.com/mitchellh/go-homedir v1.1.0 21 | github.com/parnurzeal/gorequest v0.2.16 22 | github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff 23 | github.com/sirupsen/logrus v1.4.2 24 | github.com/spf13/cobra v0.0.5 25 | github.com/spf13/viper v1.6.2 26 | github.com/thoas/go-funk v0.5.0 27 | github.com/x-cray/logrus-prefixed-formatter v0.5.2 28 | gopkg.in/sourcemap.v1 v1.0.5 // indirect 29 | gopkg.in/src-d/go-git.v4 v4.13.1 30 | gopkg.in/yaml.v2 v2.2.8 31 | moul.io/http2curl v1.0.0 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /libs/options.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | // Options global options 4 | type Options struct { 5 | RootFolder string 6 | SignFolder string 7 | PassiveFolder string 8 | ResourcesFolder string 9 | ScanID string 10 | ConfigFile string 11 | SummaryOutput string 12 | Output string 13 | PassiveOutput string 14 | LogFile string 15 | Proxy string 16 | Params []string 17 | Signs []string 18 | Excludes []string 19 | SelectedSigns []string 20 | GlobalVar map[string]string 21 | 22 | Concurrency int 23 | Threads int 24 | Delay int 25 | SaveRaw bool 26 | Timeout int 27 | Refresh int 28 | Retry int 29 | Verbose bool 30 | Debug bool 31 | NoBackGround bool 32 | NoOutput bool 33 | EnablePassive bool 34 | Server Server 35 | } 36 | 37 | // Server options for api server 38 | type Server struct { 39 | DBPath string 40 | Bind string 41 | JWTSecret string 42 | Cors string 43 | DefaultSign string 44 | SecretCollab string 45 | Username string 46 | Password string 47 | } 48 | 49 | // Job define job for running routine 50 | type Job struct { 51 | URL string 52 | Sign Signature 53 | } 54 | -------------------------------------------------------------------------------- /core/sender_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/jaeles-project/jaeles/sender" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/jaeles-project/jaeles/libs" 9 | ) 10 | 11 | func TestReallySending(t *testing.T) { 12 | var headers []map[string]string 13 | var req libs.Request 14 | headers = append(headers, map[string]string{ 15 | "Content-Type": "application/json", 16 | }) 17 | 18 | req.Method = "POST" 19 | req.URL = "https://httpbin.org/post" 20 | req.Headers = headers 21 | 22 | var opt libs.Options 23 | // opt.Proxy = "http://127.0.0.1:8080" 24 | res, err := sender.JustSend(opt, req) 25 | if err != nil { 26 | t.Errorf("Error sending request") 27 | } 28 | 29 | status := res.StatusCode 30 | if status != 200 { 31 | t.Errorf("Error parsing result") 32 | } 33 | // sending with POST data 34 | req.Body = "example1=23" 35 | res, err = sender.JustSend(opt, req) 36 | if err != nil { 37 | t.Errorf("Error sending request") 38 | } 39 | 40 | if !strings.Contains(res.Body, "example1") { 41 | t.Errorf("Error parsing result") 42 | } 43 | 44 | req.Body = `{"example1": "3333"}` 45 | res, err = sender.JustSend(opt, req) 46 | if err != nil { 47 | t.Errorf("Error sending request") 48 | } 49 | 50 | if !strings.Contains(res.Body, "example1") { 51 | t.Errorf("Error parsing result") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cmd/server_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/jaeles-project/jaeles/core" 8 | "github.com/jaeles-project/jaeles/libs" 9 | ) 10 | 11 | func TestServerWithSign(t *testing.T) { 12 | raw := `GET /rest/sample/redirect?to=localhoost&example=123 HTTP/1.1 13 | Host: juice-shop.herokuapp.com 14 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3968.0 Safari/537.36 15 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 16 | Accept-Encoding: gzip, deflate 17 | Accept-Language: en-US,en;q=0.9 18 | Connection: close 19 | Cookie: language=en 20 | Upgrade-Insecure-Requests: 1 21 | ` 22 | var record libs.Record 23 | record.OriginReq = core.ParseBurpRequest(raw) 24 | signFile := "../test-sign/open-redirect.yaml" 25 | sign, err := core.ParseSign(signFile) 26 | if err != nil { 27 | t.Errorf("Error parsing signature") 28 | } 29 | for _, req := range sign.Requests { 30 | core.ParseRequestFromServer(&record, req, sign) 31 | // send origin request 32 | Reqs := core.ParseFuzzRequest(record, sign) 33 | 34 | if len(Reqs) == 0 { 35 | t.Errorf("Error generate Path") 36 | } 37 | for _, req := range Reqs { 38 | fmt.Println(req.Method, req.URL) 39 | // fmt.Println(req.URL) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /libs/http.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | // Record all information about request 4 | type Record struct { 5 | OriginReq Request 6 | OriginRes Response 7 | Request Request 8 | Response Response 9 | Sign Signature 10 | RawOutput string 11 | ExtraOutput string 12 | ScanID string 13 | } 14 | 15 | // Request all information about request 16 | type Request struct { 17 | Engine string 18 | Timeout int 19 | Repeat int 20 | Scheme string 21 | Host string 22 | Port string 23 | Path string 24 | URL string 25 | Proxy string 26 | Method string 27 | Redirect bool 28 | UseTemplateHeader bool 29 | Headers []map[string]string 30 | Values []map[string]string 31 | Body string 32 | Beautify string 33 | MiddlewareOutput string 34 | Raw string 35 | Conditions []string 36 | Middlewares []string 37 | Conclusions []string 38 | Detections []string 39 | Generators []string 40 | Encoding string 41 | Target map[string]string 42 | } 43 | 44 | // Response all information about response 45 | type Response struct { 46 | HasPopUp bool 47 | StatusCode int 48 | Status string 49 | Headers []map[string]string 50 | Body string 51 | ResponseTime float64 52 | Length int 53 | Beautify string 54 | } 55 | -------------------------------------------------------------------------------- /database/record.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "encoding/base64" 5 | "path/filepath" 6 | 7 | "github.com/jaeles-project/jaeles/database/models" 8 | "github.com/jaeles-project/jaeles/libs" 9 | ) 10 | 11 | // CleanRecords clean all record 12 | func CleanRecords() { 13 | var rec []models.Record 14 | DB.Find(&rec) 15 | DB.Unscoped().Delete(&rec) 16 | } 17 | 18 | // ImportRecord import record to db 19 | func ImportRecord(rec libs.Record) { 20 | rawOutput, _ := filepath.Abs(rec.RawOutput) 21 | ReqRaw := base64.StdEncoding.EncodeToString([]byte(rec.Request.Beautify)) 22 | ResRaw := base64.StdEncoding.EncodeToString([]byte(rec.Response.Beautify)) 23 | extraOutput := base64.StdEncoding.EncodeToString([]byte(rec.ExtraOutput)) 24 | 25 | if rec.Sign.Info.Name == "" { 26 | rec.Sign.Info.Name = rec.Sign.ID 27 | } 28 | if rec.Sign.Info.Risk == "" { 29 | rec.Sign.Info.Risk = "Potential" 30 | } 31 | 32 | recObj := models.Record{ 33 | ReqMethod: rec.Request.Method, 34 | ReqURL: rec.Request.URL, 35 | ReqRaw: ReqRaw, 36 | ReqBody: rec.Request.Body, 37 | ResLength: rec.Response.Length, 38 | StatusCode: rec.Response.StatusCode, 39 | ResTime: rec.Response.ResponseTime, 40 | ResRaw: ResRaw, 41 | RawFile: rawOutput, 42 | ExtraOutput: extraOutput, 43 | Issues: rec.Sign.ID, 44 | Risk: rec.Sign.Info.Risk, 45 | ScanID: rec.ScanID, 46 | } 47 | DB.Create(&recObj) 48 | 49 | // if dbObj := DB.Create(&recObj); dbObj.Error != nil != true { 50 | // fmt.Printf("[Error] something woringcreate record\n") 51 | // } 52 | } 53 | -------------------------------------------------------------------------------- /database/user.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "github.com/jaeles-project/jaeles/utils" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/jaeles-project/jaeles/database/models" 11 | ) 12 | 13 | // SelectUser get password of one user to compare 14 | func SelectUser(username string) string { 15 | var user models.User 16 | DB.Where("username = ?", username).First(&user) 17 | if user.Username == username { 18 | return user.Password 19 | } 20 | return "" 21 | } 22 | 23 | // CreateUser used to create new user 24 | func CreateUser(username string, password string) { 25 | oldpass := SelectUser(username) 26 | if oldpass != "" { 27 | UpdateUser(username, password) 28 | } else { 29 | rawToken := fmt.Sprintf("%v-%v", username, strconv.FormatInt(time.Now().Unix(), 10)) 30 | rawSecret := fmt.Sprintf("%v-%v-%v", username, password, strconv.FormatInt(time.Now().Unix(), 10)) 31 | 32 | userObj := models.User{ 33 | Username: username, 34 | Email: username, 35 | Password: GenHash(password), 36 | Secret: GenHash(rawSecret), 37 | Token: GenHash(rawToken), 38 | } 39 | utils.GoodF("Create new credentials %v:%v", username, password) 40 | 41 | DB.Create(&userObj) 42 | } 43 | } 44 | 45 | // UpdateUser update default sign 46 | func UpdateUser(username string, password string) { 47 | var userObj models.User 48 | DB.Where("username = ?", username).First(&userObj) 49 | userObj.Password = GenHash(password) 50 | DB.Save(&userObj) 51 | } 52 | 53 | // GenHash generate SHA1 hash 54 | func GenHash(text string) string { 55 | h := sha1.New() 56 | h.Write([]byte(text)) 57 | hashed := h.Sum(nil) 58 | return fmt.Sprintf("%x", hashed) 59 | } 60 | -------------------------------------------------------------------------------- /core/background.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jaeles-project/jaeles/utils" 6 | "net/url" 7 | "time" 8 | 9 | "github.com/Jeffail/gabs/v2" 10 | "github.com/jaeles-project/jaeles/database" 11 | "github.com/jaeles-project/jaeles/libs" 12 | "github.com/parnurzeal/gorequest" 13 | ) 14 | 15 | // Background main function to call other background task 16 | func Background(options libs.Options) { 17 | utils.DebugF("Checking backround task") 18 | time.Sleep(time.Duration(options.Refresh) * time.Second) 19 | PollingLog() 20 | PickupLog(options) 21 | // @TODO: Add passive signature for analyzer each request 22 | } 23 | 24 | // PollingLog polling all request with their 25 | func PollingLog() { 26 | objs := database.GetUnPollReq() 27 | for _, obj := range objs { 28 | // sending part 29 | secret := url.QueryEscape(database.GetSecretbyCollab(obj.Secret)) 30 | url := fmt.Sprintf("http://polling.burpcollaborator.net/burpresults?biid=%v", secret) 31 | request := gorequest.New() 32 | _, response, errs := request.Get(url).End() 33 | if len(errs) > 0 { 34 | continue 35 | } 36 | jsonParsed, _ := gabs.ParseJSON([]byte(response)) 37 | exists := jsonParsed.Exists("responses") 38 | if exists == false { 39 | continue 40 | } else { 41 | for _, element := range jsonParsed.Path("responses").Children() { 42 | // import this to DB so we don't miss in other detect 43 | database.ImportOutOfBand(fmt.Sprintf("%v", element)) 44 | } 45 | } 46 | } 47 | } 48 | 49 | // PickupLog pickup request that's have log coming back 50 | func PickupLog(options libs.Options) { 51 | objs := database.GetUnPollReq() 52 | for _, obj := range objs { 53 | interactString := obj.InteractionString 54 | data := database.GetOOB(interactString) 55 | if data != "" { 56 | var rec libs.Record 57 | rec.Request.Beautify = obj.Req 58 | rec.Response.Beautify = obj.Res 59 | rec.ExtraOutput = data 60 | 61 | if options.NoOutput == false { 62 | outputName := StoreOutput(rec, options) 63 | rec.RawOutput = outputName 64 | database.ImportRecord(rec) 65 | } 66 | 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /core/variables_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/jaeles-project/jaeles/libs" 8 | ) 9 | 10 | func TestVariables(t *testing.T) { 11 | varString := `RandomString("6")` 12 | data := RunVariables(varString) 13 | if len(data) <= 0 { 14 | t.Errorf("Error RandomString") 15 | } 16 | varString = `RandomString(3)` 17 | data = RunVariables(varString) 18 | 19 | if len(data) <= 0 { 20 | t.Errorf("Error RandomString") 21 | } 22 | varString = `Range(0,5)` 23 | data = RunVariables(varString) 24 | fmt.Println(varString, ":", data) 25 | if len(data) <= 0 { 26 | t.Errorf("Error RandomString") 27 | } 28 | varString = `File("~/suites/contents/quick.txt")` 29 | data = RunVariables(varString) 30 | fmt.Println(varString, ":", len(data)) 31 | if len(data) <= 0 { 32 | t.Errorf("Error RandomString") 33 | } 34 | varString = `InputCmd("echo 123")` 35 | data = RunVariables(varString) 36 | fmt.Println(varString, ":", data) 37 | if len(data) <= 0 { 38 | t.Errorf("Error RandomString") 39 | } 40 | } 41 | 42 | func TestMultipleVariables(t *testing.T) { 43 | var sign libs.Signature 44 | var vars []map[string]string 45 | 46 | varElement := make(map[string]string) 47 | varElement["param"] = `[1,2,3,4]` 48 | vars = append(vars, varElement) 49 | 50 | varElement2 := make(map[string]string) 51 | varElement2["dest"] = `[a,b,c]` 52 | vars = append(vars, varElement2) 53 | 54 | sign.Variables = vars 55 | 56 | realVaris := ParseVariable(sign) 57 | fmt.Println(realVaris) 58 | if len(realVaris) <= 0 { 59 | t.Errorf("Error RandomString") 60 | } 61 | } 62 | 63 | func TestEncoding(t *testing.T) { 64 | varString := `URLEncode(" das da")` 65 | data := RunVariables(varString) 66 | fmt.Println(data) 67 | if len(data) <= 0 { 68 | t.Errorf("Error RandomString") 69 | } 70 | varString = `Base64Encode("das da c")` 71 | data = RunVariables(varString) 72 | fmt.Println(data) 73 | if len(data) <= 0 { 74 | t.Errorf("Error RandomString") 75 | } 76 | 77 | varString = `Base64EncodeByLines('das\nda\nc')` 78 | data = RunVariables(varString) 79 | fmt.Println(data) 80 | if len(data) <= 0 { 81 | t.Errorf("Error RandomString") 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /database/config.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/jaeles-project/jaeles/database/models" 8 | "github.com/parnurzeal/gorequest" 9 | ) 10 | 11 | // ImportBurpCollab used to init some default config 12 | func ImportBurpCollab(burpcollab string) string { 13 | var conObj models.Configuration 14 | DB.Where(models.Configuration{Name: "BurpCollab"}).Assign(models.Configuration{Value: burpcollab}).FirstOrCreate(&conObj) 15 | ImportBurpCollabResponse(burpcollab, "") 16 | return burpcollab 17 | } 18 | 19 | // GetDefaultBurpCollab update default sign 20 | func GetDefaultBurpCollab() string { 21 | var conObj models.Configuration 22 | DB.Where("name = ?", "BurpCollab").First(&conObj) 23 | return conObj.Value 24 | } 25 | 26 | // ImportBurpCollabResponse used to init some default config 27 | func ImportBurpCollabResponse(burpcollab string, data string) string { 28 | burpcollabres := data 29 | if burpcollabres == "" { 30 | url := fmt.Sprintf("http://%v?original=true", burpcollab) 31 | _, burpcollabres, _ := gorequest.New().Get(url).End() 32 | burpcollabres = strings.Replace(burpcollabres, "
", "", -1) 33 | burpcollabres = strings.Replace(burpcollabres, "", "", -1) 34 | } 35 | 36 | var conObj models.Configuration 37 | DB.Where(models.Configuration{Name: "BurpCollabResponse"}).Assign(models.Configuration{Value: burpcollabres}).FirstOrCreate(&conObj) 38 | return burpcollabres 39 | } 40 | 41 | // GetDefaultBurpRes update default sign 42 | func GetDefaultBurpRes() string { 43 | var conObj models.Configuration 44 | DB.Where("name = ?", "BurpCollabResponse").First(&conObj) 45 | return conObj.Value 46 | } 47 | 48 | // InitConfigSign used to init some default config 49 | func InitConfigSign() { 50 | conObj := models.Configuration{ 51 | Name: "DefaultSign", 52 | Value: "*", 53 | } 54 | DB.Create(&conObj) 55 | } 56 | 57 | // GetDefaultSign update default sign 58 | func GetDefaultSign() string { 59 | var conObj models.Configuration 60 | DB.Where("name = ?", "DefaultSign").First(&conObj) 61 | return conObj.Value 62 | } 63 | 64 | // UpdateDefaultSign update default sign 65 | func UpdateDefaultSign(sign string) { 66 | var conObj models.Configuration 67 | DB.Where("name = ?", "DefaultSign").First(&conObj) 68 | conObj.Value = sign 69 | DB.Save(&conObj) 70 | } 71 | 72 | // UpdateDefaultBurpCollab update default burp collab 73 | func UpdateDefaultBurpCollab(collab string) { 74 | var conObj models.Configuration 75 | DB.Where("name = ?", "BurpCollab").First(&conObj) 76 | conObj.Value = collab 77 | DB.Save(&conObj) 78 | } 79 | -------------------------------------------------------------------------------- /core/update.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | 8 | "github.com/jaeles-project/jaeles/libs" 9 | "github.com/jaeles-project/jaeles/utils" 10 | "gopkg.in/src-d/go-git.v4" 11 | "gopkg.in/src-d/go-git.v4/plumbing/transport/http" 12 | ) 13 | 14 | // UpdatePlugins update latest UI and Plugins from default repo 15 | func UpdatePlugins(options libs.Options) { 16 | pluginPath := path.Join(options.RootFolder, "plugins") 17 | url := libs.UIREPO 18 | utils.GoodF("Cloning Plugins from: %v", url) 19 | if utils.FolderExists(pluginPath) { 20 | utils.InforF("Remove: %v", pluginPath) 21 | os.RemoveAll(pluginPath) 22 | } 23 | r, err := git.PlainClone(pluginPath, false, &git.CloneOptions{ 24 | URL: url, 25 | RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, 26 | Depth: 1, 27 | }) 28 | if err != nil { 29 | fmt.Println("Error to clone Plugins repo") 30 | } else { 31 | _, err = r.Head() 32 | if err != nil { 33 | fmt.Println("Error to clone Plugins repo") 34 | } 35 | } 36 | } 37 | 38 | // UpdateSignature update latest UI from UI repo 39 | func UpdateSignature(options libs.Options, customRepo string) { 40 | signPath := path.Join(options.RootFolder, "base-signatures") 41 | passivePath := path.Join(signPath, "passives") 42 | resourcesPath := path.Join(signPath, "resources") 43 | 44 | url := libs.SIGNREPO 45 | if customRepo != "" { 46 | url = customRepo 47 | } 48 | 49 | utils.GoodF("Cloning Signature from: %v", url) 50 | if utils.FolderExists(signPath) { 51 | utils.InforF("Remove: %v", signPath) 52 | os.RemoveAll(signPath) 53 | os.RemoveAll(options.PassiveFolder) 54 | os.RemoveAll(options.PassiveFolder) 55 | } 56 | _, err := git.PlainClone(signPath, false, &git.CloneOptions{ 57 | Auth: &http.BasicAuth{ 58 | Username: options.Server.Username, 59 | Password: options.Server.Password, 60 | }, 61 | URL: url, 62 | RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, 63 | Depth: 1, 64 | Progress: os.Stdout, 65 | }) 66 | 67 | if err != nil { 68 | utils.ErrorF("Error to clone Signature repo: %v", url) 69 | return 70 | } 71 | 72 | // move passive signatures to default passive 73 | if utils.FolderExists(passivePath) { 74 | utils.MoveFolder(passivePath, options.PassiveFolder) 75 | } 76 | if utils.FolderExists(resourcesPath) { 77 | utils.MoveFolder(resourcesPath, options.ResourcesFolder) 78 | } 79 | 80 | } 81 | 82 | // // UpdateOutOfBand renew things in Out of band check 83 | // func UpdateOutOfBand(options libs.Options) { 84 | // // http 85 | // // dns 86 | // } 87 | -------------------------------------------------------------------------------- /database/dnsbin.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | // Use to gen bunch of DNS on dns.requestbin.net 4 | 5 | import ( 6 | "crypto/tls" 7 | "fmt" 8 | "net/url" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/Jeffail/gabs/v2" 14 | "github.com/gorilla/websocket" 15 | "github.com/parnurzeal/gorequest" 16 | ) 17 | 18 | // NewDNSBin create new dnsbin 19 | func NewDNSBin() string { 20 | var dnsbin string 21 | // var .fbbbf336914aa6bd9b58.d.requestbin.net 22 | addr := "dns.requestbin.net:8080" 23 | u := url.URL{Scheme: "ws", Host: addr, Path: "/dns"} 24 | 25 | // init a connection 26 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) 27 | if err != nil { 28 | return "" 29 | } 30 | defer c.Close() 31 | done := make(chan struct{}) 32 | go func() { 33 | defer close(done) 34 | for { 35 | _, message, err := c.ReadMessage() 36 | if err != nil { 37 | return 38 | } 39 | jsonParsed, err := gabs.ParseJSON([]byte(message)) 40 | if err != nil { 41 | return 42 | } 43 | // jsonParsed.Path("master") 44 | prefix := strconv.FormatInt(time.Now().Unix(), 10) 45 | token := strings.Trim(fmt.Sprintf("%v", jsonParsed.Path("master")), `"`) 46 | dnsbin = fmt.Sprintf("%v.%v.d.requestbin.net", prefix, token) 47 | return 48 | } 49 | }() 50 | 51 | err = c.WriteMessage(websocket.TextMessage, []byte(``)) 52 | if err != nil { 53 | return dnsbin 54 | } 55 | time.Sleep(time.Second) 56 | c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) 57 | return dnsbin 58 | } 59 | 60 | // NewReqBin gen new http bin 61 | func NewReqBin() string { 62 | var reqbin string 63 | url := fmt.Sprintf("https://bin-api.pipedream.com/api/v2/http_endpoints") 64 | prefix := strconv.FormatInt(time.Now().Unix(), 10) 65 | // client := resty.New() 66 | client := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}) 67 | client.Post(url) 68 | body := fmt.Sprintf(`{"name":"%v","pvt":false}`, prefix) 69 | client.Send(body) 70 | _, resBody, _ := client.End() 71 | 72 | message := resBody 73 | // {"status":0,"message":"success","data":{"api_key":"enw9yvvawe47","name":"Untitled","pvt":false,"created_at":"2019-11-20T10:56:29.962Z"}} 74 | jsonParsed, err := gabs.ParseJSON([]byte(message)) 75 | if err != nil { 76 | return "" 77 | } 78 | // jsonParsed.Path("master") 79 | // prefix := strconv.FormatInt(time.Now().Unix(), 10) 80 | token := strings.Trim(fmt.Sprintf("%v", jsonParsed.Path("data.api_key")), `"`) 81 | reqbin = fmt.Sprintf("https://%v.x.pipedream.net/", token) 82 | if err != nil { 83 | return "" 84 | } 85 | 86 | return reqbin 87 | } 88 | 89 | // GetTS get current timestamp and return a string 90 | func GetTS() string { 91 | return strconv.FormatInt(time.Now().Unix(), 10) 92 | } 93 | -------------------------------------------------------------------------------- /database/sign.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jaeles-project/jaeles/utils" 6 | "io/ioutil" 7 | "regexp" 8 | "strings" 9 | 10 | "github.com/Shopify/yaml" 11 | "github.com/jaeles-project/jaeles/database/models" 12 | "github.com/jaeles-project/jaeles/libs" 13 | ) 14 | 15 | // CleanSigns clean all signature 16 | func CleanSigns() { 17 | var signs []models.Signature 18 | DB.Find(&signs) 19 | DB.Unscoped().Delete(&signs) 20 | } 21 | 22 | // SelectSign select signature to gen request 23 | func SelectSign(signName string) []string { 24 | var signs []models.Signature 25 | DB.Find(&signs) 26 | 27 | //if signName == "*" || signName == "" { 28 | // DB.Find(&signs) 29 | //} else { 30 | // DB.Where("sign_id LIKE ? OR name LIKE ?", fmt.Sprintf("%%%v%%", signName), fmt.Sprintf("%%%v%%", signName)).Find(&signs) 31 | //} 32 | 33 | var selectedSigns []string 34 | for _, sign := range signs { 35 | if signName == "*" || signName == "" { 36 | selectedSigns = append(selectedSigns, sign.AsbPath) 37 | continue 38 | } 39 | // grep info 40 | info := fmt.Sprintf("%v|%v|%v|tech:%v", sign.SignID, sign.Name, sign.AsbPath, sign.Tech) 41 | if strings.Contains(info, signName) { 42 | selectedSigns = append(selectedSigns, sign.AsbPath) 43 | continue 44 | } 45 | r, err := regexp.Compile(signName) 46 | if err == nil { 47 | if r.MatchString(info) { 48 | selectedSigns = append(selectedSigns, sign.AsbPath) 49 | } 50 | } 51 | } 52 | return selectedSigns 53 | } 54 | 55 | // ImportSign import signature to DB 56 | func ImportSign(signPath string) { 57 | sign, err := ParseSignature(signPath) 58 | if err != nil { 59 | return 60 | } 61 | 62 | if sign.Info.Category == "" { 63 | if strings.Contains(sign.ID, "-") { 64 | sign.Info.Category = strings.Split(sign.ID, "-")[0] 65 | } else { 66 | sign.Info.Category = sign.ID 67 | } 68 | } 69 | if sign.Info.Name == "" { 70 | sign.Info.Name = sign.ID 71 | } 72 | 73 | signObj := models.Signature{ 74 | Name: sign.Info.Name, 75 | Category: sign.Info.Category, 76 | Risk: sign.Info.Risk, 77 | Tech: sign.Info.Tech, 78 | OS: sign.Info.OS, 79 | SignID: sign.ID, 80 | AsbPath: signPath, 81 | Type: sign.Type, 82 | } 83 | DB.Create(&signObj) 84 | } 85 | 86 | // ParseSign parsing YAML signature file 87 | func ParseSignature(signFile string) (sign libs.Signature, err error) { 88 | yamlFile, err := ioutil.ReadFile(signFile) 89 | if err != nil { 90 | utils.ErrorF("yamlFile.Get err #%v - %v", err, signFile) 91 | } 92 | err = yaml.Unmarshal(yamlFile, &sign) 93 | if err != nil { 94 | utils.ErrorF("Error: %v - %v", err, signFile) 95 | } 96 | // set some default value 97 | if sign.Info.Category == "" { 98 | if strings.Contains(sign.ID, "-") { 99 | sign.Info.Category = strings.Split(sign.ID, "-")[0] 100 | } else { 101 | sign.Info.Category = sign.ID 102 | } 103 | } 104 | if sign.Info.Name == "" { 105 | sign.Info.Name = sign.ID 106 | } 107 | if sign.Info.Risk == "" { 108 | sign.Info.Risk = "Potential" 109 | } 110 | return sign, err 111 | } 112 | -------------------------------------------------------------------------------- /database/collaborator.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/jaeles-project/jaeles/libs" 9 | 10 | "github.com/Jeffail/gabs/v2" 11 | "github.com/jaeles-project/jaeles/database/models" 12 | ) 13 | 14 | // GetCollab get random collab to test 15 | func GetCollab() string { 16 | var collabs []models.Collab 17 | DB.Find(&collabs) 18 | if len(collabs) == 0 { 19 | // auto gen a new one using request bin 20 | // dnsbin := NewDNSBin() 21 | // if dnsbin != "" { 22 | // return dnsbin 23 | // } 24 | return "" 25 | } 26 | rand.Seed(time.Now().Unix()) 27 | n := rand.Int() % len(collabs) 28 | return collabs[n].InteractionString 29 | } 30 | 31 | // GetSecretbyCollab get secret by interactString 32 | func GetSecretbyCollab(InteractionString string) string { 33 | var collabs models.Collab 34 | // DB.Find(&collabs) 35 | DB.Where("interaction_string = ?", InteractionString).First(&collabs) 36 | return collabs.Secret 37 | } 38 | 39 | // CleanCollab clean all collab 40 | func CleanCollab() { 41 | var rec []models.Collab 42 | DB.Find(&rec) 43 | DB.Unscoped().Delete(&rec) 44 | } 45 | 46 | // ImportCollab import burp collab with it's secret 47 | func ImportCollab(secret string, InteractionString string) { 48 | recObj := models.Collab{ 49 | Secret: secret, 50 | InteractionString: InteractionString, 51 | } 52 | DB.Create(&recObj) 53 | } 54 | 55 | // GetOOB check oob log with interactString 56 | func GetOOB(InteractionString string) string { 57 | var oob models.OutOfBand 58 | DB.Where("interaction_string = ?", InteractionString).First(&oob) 59 | return oob.Data 60 | } 61 | 62 | // ImportOutOfBand import polling result to DB 63 | func ImportOutOfBand(data string) { 64 | jsonParsed, _ := gabs.ParseJSON([]byte(data)) 65 | clientIP := jsonParsed.Path("client").Data().(string) 66 | protocol := jsonParsed.Path("protocol").Data().(string) 67 | ts := jsonParsed.Path("time").Data().(string) 68 | rawData := fmt.Sprintf("%v", jsonParsed.Path("data")) 69 | 70 | interactionString := jsonParsed.Path("interactionString").Data().(string) 71 | secret := GetSecretbyCollab(interactionString) 72 | 73 | // interactionString 74 | recObj := models.OutOfBand{ 75 | InteractionString: interactionString, 76 | ClientIP: clientIP, 77 | Time: ts, 78 | Protocol: protocol, 79 | Data: rawData, 80 | Secret: secret, 81 | } 82 | DB.Create(&recObj) 83 | } 84 | 85 | // GetUnPollReq get request that unpoll 86 | func GetUnPollReq() []models.ReqLog { 87 | var reqLogs []models.ReqLog 88 | DB.Where("data = ?", "").Find(&reqLogs) 89 | return reqLogs 90 | } 91 | 92 | // ImportReqLog import polling result to DB 93 | func ImportReqLog(rec libs.Record, analyzeString string) { 94 | secret := GetSecretbyCollab(analyzeString) 95 | recObj := models.ReqLog{ 96 | Req: rec.Request.Beautify, 97 | Res: rec.Response.Beautify, 98 | InteractionString: analyzeString, 99 | ScanID: rec.ScanID, 100 | Secret: secret, 101 | } 102 | DB.Create(&recObj) 103 | } 104 | -------------------------------------------------------------------------------- /core/analyze.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/sha1" 5 | "fmt" 6 | "github.com/jaeles-project/jaeles/sender" 7 | "net/url" 8 | "os" 9 | "path" 10 | "strings" 11 | 12 | "github.com/fatih/color" 13 | "github.com/jaeles-project/jaeles/database" 14 | "github.com/jaeles-project/jaeles/libs" 15 | "github.com/jaeles-project/jaeles/utils" 16 | ) 17 | 18 | // Analyze run analyzer with each detections 19 | func Analyze(options libs.Options, record *libs.Record) { 20 | /* Analyze part */ 21 | if record.Request.Beautify == "" { 22 | record.Request.Beautify = sender.BeautifyRequest(record.Request) 23 | } 24 | if len(record.Request.Detections) <= 0 { 25 | return 26 | } 27 | 28 | for _, analyze := range record.Request.Detections { 29 | utils.DebugF("[Detection] %v", analyze) 30 | extra, result := RunDetector(*record, analyze) 31 | if extra != "" { 32 | record.ExtraOutput = extra 33 | } 34 | if result == true { 35 | if options.Verbose { 36 | color.Magenta("[Found] %v", analyze) 37 | } 38 | var outputName string 39 | if options.NoOutput == false { 40 | outputName = StoreOutput(*record, options) 41 | record.RawOutput = outputName 42 | database.ImportRecord(*record) 43 | } 44 | color.Green("[Vulnerable][%v] %v %v", record.Sign.Info.Risk, record.Request.URL, outputName) 45 | } 46 | } 47 | } 48 | 49 | // StoreOutput store vulnerable request to a file 50 | func StoreOutput(rec libs.Record, options libs.Options) string { 51 | // store output to a file 52 | if rec.Request.URL == "" { 53 | rec.Request.URL = rec.Request.Target["URL"] 54 | } 55 | head := fmt.Sprintf("[%v] - %v\n\n", rec.Sign.ID, rec.Request.URL) 56 | content := head 57 | if rec.Request.MiddlewareOutput != "" { 58 | content += strings.Join(rec.Request.Middlewares, "\n") 59 | content += fmt.Sprintf("\n%v\n", strings.Repeat("-", 50)) 60 | content += rec.Request.MiddlewareOutput 61 | } 62 | if rec.ExtraOutput != "" { 63 | content += strings.Join(rec.Request.Middlewares, "\n") 64 | content += fmt.Sprintf("\n%v\n", strings.Repeat("-", 50)) 65 | content += rec.ExtraOutput 66 | } 67 | if rec.ExtraOutput == "" && rec.Request.MiddlewareOutput == "" { 68 | content += rec.Request.Beautify 69 | content += fmt.Sprintf("\n%v\n", strings.Repeat("-", 50)) 70 | content += rec.Response.Beautify 71 | } 72 | 73 | // hash the content 74 | h := sha1.New() 75 | h.Write([]byte(content)) 76 | checksum := h.Sum(nil) 77 | 78 | parts := []string{options.Output} 79 | if rec.Request.URL == "" { 80 | parts = append(parts, rec.Request.Target["Domain"]) 81 | } else { 82 | u, _ := url.Parse(rec.Request.URL) 83 | parts = append(parts, u.Hostname()) 84 | } 85 | parts = append(parts, fmt.Sprintf("%v-%x", rec.Sign.ID, checksum)) 86 | 87 | p := path.Join(parts...) 88 | if _, err := os.Stat(path.Dir(p)); os.IsNotExist(err) { 89 | err = os.MkdirAll(path.Dir(p), 0750) 90 | if err != nil { 91 | utils.ErrorF("Error Write content to: %v", p) 92 | } 93 | } 94 | utils.WriteToFile(p, content) 95 | sum := fmt.Sprintf("%v - %v", strings.TrimSpace(head), p) 96 | utils.AppendToContent(options.SummaryOutput, sum) 97 | 98 | return p 99 | } 100 | -------------------------------------------------------------------------------- /core/config.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/Jeffail/gabs/v2" 7 | "github.com/jaeles-project/jaeles/libs" 8 | "github.com/jaeles-project/jaeles/utils" 9 | "github.com/spf13/viper" 10 | "io/ioutil" 11 | "os" 12 | "path" 13 | ) 14 | 15 | // InitConfig init config 16 | func InitConfig(options *libs.Options) { 17 | options.RootFolder = utils.NormalizePath(options.RootFolder) 18 | options.Server.DBPath = path.Join(options.RootFolder, "sqlite3.db") 19 | // init new root folder 20 | if !utils.FolderExists(options.RootFolder) { 21 | utils.InforF("Init new config at %v", options.RootFolder) 22 | os.MkdirAll(options.RootFolder, 0750) 23 | // cloning default repo 24 | UpdatePlugins(*options) 25 | UpdateSignature(*options, "") 26 | } 27 | 28 | configPath := path.Join(options.RootFolder, "config.yaml") 29 | v := viper.New() 30 | v.AddConfigPath(options.RootFolder) 31 | v.SetConfigName("config") 32 | v.SetConfigType("yaml") 33 | if !utils.FileExists(configPath) { 34 | utils.InforF("Write new config to: %v", configPath) 35 | // save default config if not exist 36 | bind := "http://127.0.0.1:5000" 37 | v.SetDefault("defaultSign", "*") 38 | v.SetDefault("cors", "*") 39 | // default credential 40 | v.SetDefault("username", "jaeles") 41 | v.SetDefault("password", utils.GenHash(utils.GetTS())[:10]) 42 | v.SetDefault("secret", utils.GenHash(utils.GetTS())) 43 | v.SetDefault("bind", bind) 44 | v.WriteConfigAs(configPath) 45 | 46 | } else { 47 | if options.Debug { 48 | utils.InforF("Load config from: %v", configPath) 49 | } 50 | b, _ := ioutil.ReadFile(configPath) 51 | v.ReadConfig(bytes.NewBuffer(b)) 52 | } 53 | // config.defaultSign = fmt.Sprintf("%v", v.Get("defaultSign")) 54 | 55 | // WARNING: change me if you really want to deploy on remote server 56 | // allow all origin 57 | options.Server.Cors = v.GetString("cors") 58 | options.Server.JWTSecret = v.GetString("secret") 59 | options.Server.Username = v.GetString("username") 60 | options.Server.Password = v.GetString("password") 61 | 62 | // store default credentials for Burp plugin 63 | burpConfigPath := path.Join(options.RootFolder, "burp.json") 64 | if !utils.FileExists(burpConfigPath) { 65 | jsonObj := gabs.New() 66 | jsonObj.Set("", "JWT") 67 | jsonObj.Set(v.GetString("username"), "username") 68 | jsonObj.Set(v.GetString("password"), "password") 69 | bind := v.GetString("bind") 70 | if bind == "" { 71 | bind = "http://127.0.0.1:5000" 72 | } 73 | jsonObj.Set(fmt.Sprintf("http://%v/api/parse", bind), "endpoint") 74 | utils.WriteToFile(burpConfigPath, jsonObj.String()) 75 | if options.Verbose { 76 | utils.InforF("Store default credentials for client at: %v", burpConfigPath) 77 | } 78 | } 79 | 80 | // set some default config 81 | options.PassiveFolder = path.Join(utils.NormalizePath(options.RootFolder), "passives") 82 | options.ResourcesFolder = path.Join(utils.NormalizePath(options.RootFolder), "resources") 83 | 84 | // create output folder 85 | var err error 86 | err = os.MkdirAll(options.Output, 0750) 87 | if err != nil && options.NoOutput == false { 88 | fmt.Fprintf(os.Stderr, "failed to create output directory: %s\n", err) 89 | os.Exit(1) 90 | } 91 | if options.SummaryOutput == "" { 92 | options.SummaryOutput = path.Join(options.Output, "jaeles-summary.txt") 93 | } 94 | utils.InforF("Summary output: %v", options.SummaryOutput) 95 | } 96 | -------------------------------------------------------------------------------- /utils/log.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/fatih/color" 13 | "github.com/jaeles-project/jaeles/libs" 14 | "github.com/sirupsen/logrus" 15 | prefixed "github.com/x-cray/logrus-prefixed-formatter" 16 | ) 17 | 18 | var logger = logrus.New() 19 | 20 | // InitLog init log 21 | func InitLog(options *libs.Options) { 22 | logger = &logrus.Logger{ 23 | Out: os.Stdout, 24 | Level: logrus.InfoLevel, 25 | Formatter: &prefixed.TextFormatter{ 26 | ForceColors: true, 27 | ForceFormatting: true, 28 | }, 29 | } 30 | 31 | if options.LogFile != "" { 32 | options.LogFile = NormalizePath(options.LogFile) 33 | dir := path.Dir(options.LogFile) 34 | tmpFile, _ := ioutil.TempFile(dir, "jaeles-*.log") 35 | options.LogFile = tmpFile.Name() 36 | dir = filepath.Dir(options.LogFile) 37 | if !FolderExists(dir) { 38 | os.MkdirAll(dir, 0755) 39 | } 40 | f, err := os.OpenFile(options.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 41 | if err != nil { 42 | logger.Errorf("error opening file: %v", err) 43 | } 44 | 45 | mwr := io.MultiWriter(os.Stdout, f) 46 | logger = &logrus.Logger{ 47 | Out: mwr, 48 | Level: logrus.InfoLevel, 49 | Formatter: &prefixed.TextFormatter{ 50 | ForceColors: true, 51 | ForceFormatting: true, 52 | }, 53 | } 54 | } 55 | 56 | if options.Debug == true { 57 | logger.SetLevel(logrus.DebugLevel) 58 | } else if options.Verbose == true { 59 | logger.SetOutput(os.Stdout) 60 | logger.SetLevel(logrus.InfoLevel) 61 | } else { 62 | logger.SetLevel(logrus.PanicLevel) 63 | logger.SetOutput(ioutil.Discard) 64 | } 65 | if options.LogFile != "" { 66 | logger.Info(fmt.Sprintf("Store log file to: %v", options.LogFile)) 67 | } 68 | } 69 | 70 | // PrintLine print seperate line 71 | func PrintLine() { 72 | dash := color.HiWhiteString("-") 73 | fmt.Println(strings.Repeat(dash, 40)) 74 | } 75 | 76 | // GoodF print good message 77 | func GoodF(format string, args ...interface{}) { 78 | good := color.HiGreenString("[+]") 79 | fmt.Printf("%s %s\n", good, fmt.Sprintf(format, args...)) 80 | } 81 | 82 | // BannerF print info message 83 | func BannerF(format string, data string) { 84 | banner := fmt.Sprintf("%v%v%v ", color.WhiteString("["), color.BlueString(format), color.WhiteString("]")) 85 | fmt.Printf("%v%v\n", banner, color.HiGreenString(data)) 86 | } 87 | 88 | // BlockF print info message 89 | func BlockF(name string, data string) { 90 | banner := fmt.Sprintf("%v%v%v ", color.WhiteString("["), color.GreenString(name), color.WhiteString("]")) 91 | fmt.Printf(fmt.Sprintf("%v%v\n", banner, data)) 92 | } 93 | 94 | // InforF print info message 95 | func InforF(format string, args ...interface{}) { 96 | logger.Info(fmt.Sprintf(format, args...)) 97 | } 98 | 99 | // ErrorF print good message 100 | func ErrorF(format string, args ...interface{}) { 101 | logger.Error(fmt.Sprintf(format, args...)) 102 | } 103 | 104 | // WarningF print good message 105 | func WarningF(format string, args ...interface{}) { 106 | good := color.YellowString("[!]") 107 | fmt.Printf("%s %s\n", good, fmt.Sprintf(format, args...)) 108 | } 109 | 110 | // DebugF print debug message 111 | func DebugF(format string, args ...interface{}) { 112 | logger.Debug(fmt.Sprintf(format, args...)) 113 | } 114 | -------------------------------------------------------------------------------- /cmd/server.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "path/filepath" 7 | "sync" 8 | 9 | "github.com/jaeles-project/jaeles/core" 10 | "github.com/jaeles-project/jaeles/database" 11 | "github.com/jaeles-project/jaeles/libs" 12 | "github.com/jaeles-project/jaeles/server" 13 | "github.com/jaeles-project/jaeles/utils" 14 | 15 | "github.com/spf13/cobra" 16 | ) 17 | 18 | var serverCmd *cobra.Command 19 | 20 | func init() { 21 | // byeCmd represents the bye command 22 | var serverCmd = &cobra.Command{ 23 | Use: "server", 24 | Short: "Start API server", 25 | Long: libs.Banner(), RunE: runServer, 26 | } 27 | //serverCmd.Flags().StringSliceP("sign", "s", []string{}, "Signature selector (Multiple -s flags are accepted)") 28 | serverCmd.Flags().String("host", "127.0.0.1", "IP address to bind the server") 29 | serverCmd.Flags().String("port", "5000", "Port") 30 | RootCmd.AddCommand(serverCmd) 31 | 32 | } 33 | 34 | func runServer(cmd *cobra.Command, args []string) error { 35 | SelectSign() 36 | // prepare DB stuff 37 | if options.Server.Username != "" { 38 | database.CreateUser(options.Server.Username, options.Server.Password) 39 | } 40 | // reload signature 41 | SignFolder, _ := filepath.Abs(path.Join(options.RootFolder, "base-signatures")) 42 | allSigns := utils.GetFileNames(SignFolder, ".yaml") 43 | if allSigns != nil { 44 | for _, signFile := range allSigns { 45 | database.ImportSign(signFile) 46 | } 47 | } 48 | database.InitConfigSign() 49 | 50 | result := make(chan libs.Record) 51 | jobs := make(chan libs.Job) 52 | 53 | go func() { 54 | for { 55 | record := <-result 56 | utils.InforF("[Receive] %v %v \n", record.OriginReq.Method, record.OriginReq.URL) 57 | for _, signFile := range options.SelectedSigns { 58 | sign, err := core.ParseSign(signFile) 59 | if err != nil { 60 | utils.ErrorF("Error loading sign: %v\n", signFile) 61 | continue 62 | } 63 | // parse sign as list or single 64 | if sign.Type != "fuzz" { 65 | url := record.OriginReq.URL 66 | jobs <- libs.Job{URL: url, Sign: sign} 67 | 68 | } else { 69 | 70 | fuzzSign := sign 71 | fuzzSign.Requests = []libs.Request{} 72 | for _, req := range sign.Requests { 73 | core.ParseRequestFromServer(&record, req, sign) 74 | // append all requests in sign with request from api 75 | req.Method = record.Request.Method 76 | req.URL = record.Request.URL 77 | req.Headers = record.Request.Headers 78 | req.Body = record.Request.Body 79 | fuzzSign.Requests = append(fuzzSign.Requests, req) 80 | 81 | } 82 | url := record.OriginReq.URL 83 | jobs <- libs.Job{URL: url, Sign: fuzzSign} 84 | 85 | } 86 | } 87 | 88 | } 89 | }() 90 | 91 | /* Start sending request here */ 92 | var wg sync.WaitGroup 93 | for i := 0; i < options.Concurrency; i++ { 94 | wg.Add(1) 95 | go func() { 96 | defer wg.Done() 97 | for job := range jobs { 98 | sign := job.Sign 99 | url := job.URL 100 | RunJob(url, sign, options) 101 | } 102 | }() 103 | } 104 | 105 | host, _ := cmd.Flags().GetString("host") 106 | port, _ := cmd.Flags().GetString("port") 107 | bind := fmt.Sprintf("%v:%v", host, port) 108 | options.Server.Bind = bind 109 | utils.InforF("Start API server at %v", fmt.Sprintf("http://%v/#/", bind)) 110 | 111 | server.InitRouter(options, result) 112 | // wg.Wait() 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /core/passive_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jaeles-project/jaeles/libs" 6 | "testing" 7 | ) 8 | 9 | func TestPassiveCheck(t *testing.T) { 10 | var record libs.Record 11 | //record.Response.Beautify = "SQLite3::SQLException foo" 12 | record.Response.Beautify = "Warning: file_exists(a" 13 | var passive libs.Passive 14 | passive = defaultPassive() 15 | 16 | for _, rule := range passive.Rules { 17 | for _, detectionString := range rule.Detections { 18 | fmt.Println(detectionString) 19 | _, result := RunDetector(record, detectionString) 20 | fmt.Println(result) 21 | if !result { 22 | t.Errorf("Error resolve variable") 23 | } 24 | } 25 | } 26 | } 27 | 28 | func TestRegexSearch(t *testing.T) { 29 | raw := "SQLite3::SQLException foo" 30 | regex := "(Exception (condition )?\\d+\\. Transaction rollback|com\\.frontbase\\.jdbc|org\\.h2\\.jdbc|Unexpected end of command in statement \\[\"|Unexpected token.*?in statement \\[|org\\.hsqldb\\.jdbc|CLI Driver.*?DB2|DB2 SQL error|\\bdb2_\\w+\\(|SQLSTATE.+SQLCODE|com\\.ibm\\.db2\\.jcc|Zend_Db_(Adapter|Statement)_Db2_Exception|Pdo[./_\\\\]Ibm|DB2Exception|Warning.*?\\Wifx_|Exception.*?Informix|Informix ODBC Driver|ODBC Informix driver|com\\.informix\\.jdbc|weblogic\\.jdbc\\.informix|Pdo[./_\\\\]Informix|IfxException|Warning.*?\\Wingres_|Ingres SQLSTATE|Ingres\\W.*?Driver|com\\.ingres\\.gcf\\.jdbc|Dynamic SQL Error|Warning.*?\\Wibase_|org\\.firebirdsql\\.jdbc|Pdo[./_\\\\]Firebird|Microsoft Access (\\d+ )?Driver|JET Database Engine|Access Database Engine|ODBC Microsoft Access|Syntax error \\(missing operator\\) in query expression|Driver.*? SQL[\\-\\_\\ ]*Server|OLE DB.*? SQL Server|\\bSQL Server[^<"]+Driver|Warning.*?\\W(mssql|sqlsrv)_|\\bSQL Server[^<"]+[0-9a-fA-F]{8}|System\\.Data\\.SqlClient\\.SqlException|(?s)Exception.*?\\bRoadhouse\\.Cms\\.|Microsoft SQL Native Client error '[0-9a-fA-F]{8}|\\[SQL Server\\]|ODBC SQL Server Driver|ODBC Driver \\d+ for SQL Server|SQLServer JDBC Driver|com\\.jnetdirect\\.jsql|macromedia\\.jdbc\\.sqlserver|Zend_Db_(Adapter|Statement)_Sqlsrv_Exception|com\\.microsoft\\.sqlserver\\.jdbc|Pdo[./_\\\\](Mssql|SqlSrv)|SQL(Srv|Server)Exception|SQL syntax.*?MySQL|Warning.*?\\Wmysqli?_|MySQLSyntaxErrorException|valid MySQL result|check the manual that corresponds to your (MySQL|MariaDB) server version|Unknown column '[^ ]+' in 'field list'|MySqlClient\\.|com\\.mysql\\.jdbc|Zend_Db_(Adapter|Statement)_Mysqli_Exception|Pdo[./_\\\\]Mysql|MySqlException|\\bORA-\\d{5}|Oracle error|Oracle.*?Driver|Warning.*?\\W(oci|ora)_|quoted string not properly terminated|SQL command not properly ended|macromedia\\.jdbc\\.oracle|oracle\\.jdbc|Zend_Db_(Adapter|Statement)_Oracle_Exception|Pdo[./_\\\\](Oracle|OCI)|OracleException|PostgreSQL.*?ERROR|Warning.*?\\Wpg_|valid PostgreSQL result|Npgsql\\.|PG::SyntaxError:|org\\.postgresql\\.util\\.PSQLException|ERROR:\\s\\ssyntax error at or near|ERROR: parser: parse error at or near|PostgreSQL query failed|org\\.postgresql\\.jdbc|Pdo[./_\\\\]Pgsql|PSQLException|SQL error.*?POS([0-9]+)|Warning.*?\\Wmaxdb_|DriverSapDB|com\\.sap\\.dbtech\\.jdbc|SQLite/JDBCDriver|SQLite\\.Exception|(Microsoft|System)\\.Data\\.SQLite\\.SQLiteException|Warning.*?\\W(sqlite_|SQLite3::)|\\[SQLITE_ERROR\\]|SQLite error \\d+:|sqlite3.OperationalError:|SQLite3::SQLException|org\\.sqlite\\.JDBC|Pdo[./_\\\\]Sqlite|SQLiteException|Warning.*?\\Wsybase_|Sybase message|Sybase.*?Server message|SybSQLException|Sybase\\.Data\\.AseClient|com\\.sybase\\.jdbc)" 31 | 32 | result := RegexSearch(raw, regex) 33 | if !result { 34 | t.Errorf("Error resolve variable") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/controllers.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/jaeles-project/jaeles/libs" 9 | 10 | "github.com/fatih/color" 11 | "github.com/gin-gonic/gin" 12 | "github.com/jaeles-project/jaeles/core" 13 | ) 14 | 15 | // RequestData struct for recive request from burp 16 | type RequestData struct { 17 | RawReq string `json:"req"` 18 | RawRes string `json:"res"` 19 | URL string `json:"url"` 20 | } 21 | 22 | // SetBurpCollab setup Burp 23 | func SetBurpCollab(c *gin.Context) { 24 | c.JSON(200, gin.H{ 25 | "status": "200", 26 | "message": "Got it", 27 | }) 28 | } 29 | 30 | // ParseRaw Get Raw Burp Request in base64 encode 31 | func ParseRaw(c *gin.Context) { 32 | // result <- record 33 | // core data 34 | var reqData RequestData 35 | // c.BindJSON(&reqData) 36 | err := c.ShouldBindJSON(&reqData) 37 | if err != nil { 38 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 39 | } 40 | 41 | go func() { 42 | var record libs.Record 43 | // core data 44 | rawReq := reqData.RawReq 45 | rawRes := reqData.RawRes 46 | 47 | req, err := base64.StdEncoding.DecodeString(rawReq) 48 | if err != nil { 49 | c.JSON(500, gin.H{ 50 | "message": "error decode request", 51 | "status": "Error", 52 | }) 53 | } 54 | record.OriginReq = core.ParseBurpRequest(string(req)) 55 | /* Response part */ 56 | if rawRes != "" { 57 | // response stuff 58 | res, err := base64.StdEncoding.DecodeString(rawRes) 59 | if err != nil { 60 | c.JSON(500, gin.H{ 61 | "message": "error decode response", 62 | "status": "Error", 63 | }) 64 | } 65 | record.OriginRes = core.ParseBurpResponse(string(req), string(res)) 66 | } 67 | 68 | color.Green("-- got from gin") 69 | fmt.Println(record.OriginReq.URL) 70 | color.Green("-- done from gin") 71 | // result <- record 72 | }() 73 | 74 | c.JSON(200, gin.H{ 75 | "status": "200", 76 | "message": "Got it", 77 | }) 78 | } 79 | 80 | // ReceiveRequest is handler to got request from Burp 81 | func ReceiveRequest(result chan libs.Record) gin.HandlerFunc { 82 | return func(c *gin.Context) { 83 | cCp := c.Copy() 84 | var reqData RequestData 85 | err := cCp.ShouldBindJSON(&reqData) 86 | if err != nil { 87 | c.JSON(200, gin.H{ 88 | "status": "500", 89 | "message": "Error parsing JSON data", 90 | }) 91 | return 92 | } 93 | var record libs.Record 94 | // core data 95 | rawReq := reqData.RawReq 96 | rawRes := reqData.RawRes 97 | URL := reqData.URL 98 | 99 | // var record libs.Record 100 | req, err := base64.StdEncoding.DecodeString(rawReq) 101 | if err != nil { 102 | c.JSON(200, gin.H{ 103 | "status": "500", 104 | "message": "Error parsing request", 105 | }) 106 | return 107 | } 108 | record.OriginReq = core.ParseBurpRequest(string(req)) 109 | if URL != "" { 110 | record.OriginReq.URL = URL 111 | } 112 | //fmt.Printf("[Recive] %v %v \n", record.OriginReq.Method, record.OriginReq.URL) 113 | 114 | /* Response part */ 115 | if rawRes != "" { 116 | // response stuff 117 | res, err := base64.StdEncoding.DecodeString(rawRes) 118 | if err != nil { 119 | c.JSON(200, gin.H{ 120 | "status": "500", 121 | "message": "Error parsing response", 122 | }) 123 | } 124 | record.OriginRes = core.ParseBurpResponse(string(req), string(res)) 125 | } 126 | result <- record 127 | 128 | c.JSON(200, gin.H{ 129 | "status": "200", 130 | "message": "Got it", 131 | }) 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /server/api.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/jaeles-project/jaeles/database" 8 | "github.com/jaeles-project/jaeles/database/models" 9 | ) 10 | 11 | // Ping testing authenticated connection 12 | func Ping(c *gin.Context) { 13 | c.JSON(200, gin.H{ 14 | "status": "200", 15 | "message": "pong", 16 | }) 17 | } 18 | 19 | // GetStats return stat data 20 | func GetStats(c *gin.Context) { 21 | var info []models.Record 22 | database.DB.Where("risk = ?", "Info").Find(&info) 23 | var potential []models.Record 24 | database.DB.Where("risk = ?", "Potential").Find(&potential) 25 | var low []models.Record 26 | database.DB.Where("risk = ?", "Low").Find(&low) 27 | var medium []models.Record 28 | database.DB.Where("risk = ?", "Medium").Find(&medium) 29 | var high []models.Record 30 | database.DB.Where("risk = ?", "High").Find(&high) 31 | var critical []models.Record 32 | database.DB.Where("risk = ?", "Critical").Find(&critical) 33 | 34 | stats := []int{ 35 | len(info), 36 | len(potential), 37 | len(low), 38 | len(medium), 39 | len(high), 40 | len(critical), 41 | } 42 | 43 | c.JSON(200, gin.H{ 44 | "status": "200", 45 | "message": "Success", 46 | "stats": stats, 47 | }) 48 | } 49 | 50 | // GetSignSummary return signature stat 51 | func GetSignSummary(c *gin.Context) { 52 | var signs []models.Signature 53 | var categories []string 54 | var data []int 55 | database.DB.Find(&signs).Pluck("DISTINCT category", &categories) 56 | // stats := make(map[string]int) 57 | for _, category := range categories { 58 | var signatures []models.Signature 59 | database.DB.Where("category = ?", category).Find(&signatures) 60 | data = append(data, len(signatures)) 61 | } 62 | 63 | c.JSON(200, gin.H{ 64 | "status": "200", 65 | "message": "Success", 66 | "categories": categories, 67 | "data": data, 68 | }) 69 | } 70 | 71 | // GetSigns return signature record 72 | func GetSigns(c *gin.Context) { 73 | var signs []models.Signature 74 | database.DB.Find(&signs) 75 | 76 | c.JSON(200, gin.H{ 77 | "status": "200", 78 | "message": "Success", 79 | "signatures": signs, 80 | }) 81 | } 82 | 83 | // GetAllScan return all scans 84 | func GetAllScan(c *gin.Context) { 85 | var scans []models.Scans 86 | database.DB.Find(&scans) 87 | 88 | // remove empty scan 89 | var realScans []models.Scans 90 | for _, scan := range scans { 91 | var rec models.Record 92 | database.DB.First(&rec, "scan_id = ?", scan.ScanID) 93 | if rec.ScanID != "" { 94 | realScans = append(realScans, scan) 95 | } 96 | } 97 | 98 | c.JSON(200, gin.H{ 99 | "status": "200", 100 | "message": "Success", 101 | "scans": realScans, 102 | }) 103 | } 104 | 105 | // GetRecords get record by scan ID 106 | func GetRecords(c *gin.Context) { 107 | sid := c.Param("sid") 108 | var records []models.Record 109 | database.DB.Where("scan_id = ?", sid).Find(&records) 110 | 111 | c.JSON(200, gin.H{ 112 | "status": "200", 113 | "message": "Success", 114 | "records": records, 115 | }) 116 | } 117 | 118 | // GetRecord get record detail by record ID 119 | func GetRecord(c *gin.Context) { 120 | rid := c.Param("rid") 121 | var record models.Record 122 | database.DB.Where("id = ?", rid).First(&record) 123 | 124 | c.JSON(200, gin.H{ 125 | "status": "200", 126 | "message": "Success", 127 | "record": record, 128 | }) 129 | } 130 | 131 | // SignConfig config 132 | type SignConfig struct { 133 | Value string `json:"sign"` 134 | } 135 | 136 | // UpdateDefaultSign geet record by scan 137 | func UpdateDefaultSign(c *gin.Context) { 138 | var signConfig SignConfig 139 | err := c.ShouldBindJSON(&signConfig) 140 | if err != nil { 141 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) 142 | } 143 | 144 | database.UpdateDefaultSign(signConfig.Value) 145 | c.JSON(200, gin.H{ 146 | "status": "200", 147 | "message": "Update Defeult sign success", 148 | }) 149 | } 150 | -------------------------------------------------------------------------------- /server/routers.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "path" 7 | "time" 8 | 9 | jwt "github.com/appleboy/gin-jwt/v2" 10 | "github.com/jaeles-project/jaeles/database" 11 | "github.com/jaeles-project/jaeles/libs" 12 | "github.com/jaeles-project/jaeles/utils" 13 | 14 | "github.com/gin-contrib/cors" 15 | "github.com/gin-contrib/static" 16 | "github.com/gin-gonic/gin" 17 | ) 18 | 19 | type login struct { 20 | Username string `form:"username" json:"username" binding:"required"` 21 | Password string `form:"password" json:"password" binding:"required"` 22 | } 23 | 24 | var identityKey = "id" 25 | 26 | // User struct 27 | type User struct { 28 | UserName string 29 | Role string 30 | Email string 31 | IsAdmin bool 32 | } 33 | 34 | // InitRouter start point of api server 35 | func InitRouter(options libs.Options, result chan libs.Record) { 36 | // turn off Gin debug mode 37 | if !options.Debug { 38 | gin.SetMode(gin.ReleaseMode) 39 | } 40 | r := gin.New() 41 | r.Use(gin.Logger()) 42 | r.Use(gin.Recovery()) 43 | 44 | // default is ~/.jaeles/ui/ 45 | uiPath := path.Join(options.RootFolder, "/plugins/ui") 46 | r.Use(static.Serve("/", static.LocalFile(uiPath, true))) 47 | 48 | allowOrigin := "*" 49 | secret := "something you have to change" 50 | if options.Server.JWTSecret != "" { 51 | secret = options.Server.JWTSecret 52 | } 53 | if options.Server.Cors != "" { 54 | allowOrigin = options.Server.Cors 55 | } 56 | 57 | r.Use(cors.New(cors.Config{ 58 | AllowOrigins: []string{allowOrigin}, 59 | AllowMethods: []string{"POST", "GET", "OPTIONS"}, 60 | AllowHeaders: []string{"Authorization"}, 61 | AllowCredentials: true, 62 | MaxAge: 24 * time.Hour, 63 | })) 64 | 65 | // the jwt middleware 66 | authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{ 67 | Realm: "jaeles server", 68 | Key: []byte(secret), 69 | Timeout: time.Hour * 360, 70 | MaxRefresh: time.Hour * 720, 71 | IdentityKey: identityKey, 72 | PayloadFunc: func(data interface{}) jwt.MapClaims { 73 | if v, ok := data.(*User); ok { 74 | return jwt.MapClaims{ 75 | identityKey: v.Role, 76 | } 77 | } 78 | return jwt.MapClaims{} 79 | }, 80 | IdentityHandler: func(c *gin.Context) interface{} { 81 | claims := jwt.ExtractClaims(c) 82 | return &User{ 83 | Role: claims[identityKey].(string), 84 | } 85 | }, 86 | Authenticator: func(c *gin.Context) (interface{}, error) { 87 | var loginVals login 88 | err := c.ShouldBindJSON(&loginVals) 89 | if err != nil { 90 | return "", jwt.ErrMissingLoginValues 91 | } 92 | username := loginVals.Username 93 | password := loginVals.Password 94 | 95 | // compare hashed password 96 | realPassword := database.SelectUser(username) 97 | if utils.GenHash(password) == realPassword { 98 | return &User{ 99 | UserName: username, 100 | // only have one role for now 101 | Role: "admin", 102 | Email: username, 103 | IsAdmin: true, 104 | }, nil 105 | } 106 | 107 | return nil, jwt.ErrFailedAuthentication 108 | }, 109 | Authorizator: func(data interface{}, c *gin.Context) bool { 110 | // @TODO: Disable authorization for now 111 | if v, ok := data.(*User); ok && v.Role == "admin" { 112 | return true 113 | } 114 | return false 115 | // return true 116 | }, 117 | Unauthorized: func(c *gin.Context, code int, message string) { 118 | c.JSON(code, gin.H{ 119 | "code": code, 120 | "message": message, 121 | }) 122 | }, 123 | TokenLookup: "header: Authorization, query: token, cookie: jwt", 124 | TokenHeadName: "Jaeles", 125 | TimeFunc: time.Now, 126 | }) 127 | 128 | if err != nil { 129 | log.Fatal("JWT Error:" + err.Error()) 130 | } 131 | 132 | r.POST("/auth/login", authMiddleware.LoginHandler) 133 | 134 | r.NoRoute(authMiddleware.MiddlewareFunc(), func(c *gin.Context) { 135 | claims := jwt.ExtractClaims(c) 136 | log.Printf("NoRoute claims: %#v\n", claims) 137 | c.JSON(404, gin.H{"code": "404", "message": "Page not found"}) 138 | }) 139 | 140 | auth := r.Group("/api") 141 | // Refresh time can be longer than token timeout 142 | auth.GET("/refresh_token", authMiddleware.RefreshHandler) 143 | auth.Use(authMiddleware.MiddlewareFunc()) 144 | { 145 | auth.GET("/ping", Ping) 146 | auth.POST("/parse", ReceiveRequest(result)) 147 | auth.POST("/config/sign", UpdateDefaultSign) 148 | auth.GET("/stats/vuln", GetStats) 149 | auth.GET("/stats/sign", GetSignSummary) 150 | auth.GET("/signatures", GetSigns) 151 | auth.GET("/scans", GetAllScan) 152 | auth.GET("/scan/:sid/", GetRecords) 153 | auth.GET("/record/:rid/", GetRecord) 154 | } 155 | 156 | if err := http.ListenAndServe(options.Server.Bind, r); err != nil { 157 | log.Fatal(err) 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /sender/chrome.go: -------------------------------------------------------------------------------- 1 | package sender 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/jaeles-project/jaeles/utils" 7 | "log" 8 | "time" 9 | 10 | "github.com/chromedp/cdproto/dom" 11 | "github.com/chromedp/cdproto/network" 12 | "github.com/chromedp/cdproto/page" 13 | "github.com/chromedp/chromedp" 14 | "github.com/jaeles-project/jaeles/libs" 15 | ) 16 | 17 | // SendWithChrome send request with real browser 18 | func SendWithChrome(options libs.Options, req libs.Request) (libs.Response, error) { 19 | // parsing some stuff 20 | url := req.URL 21 | // @TODO: parse more request component later 22 | // method := req.Method 23 | // body := req.Body 24 | // headers := GetHeaders(req) 25 | fmt.Printf("[Sent][Chrome] %v \n", url) 26 | var res libs.Response 27 | 28 | // prepare the chrome options 29 | opts := append(chromedp.DefaultExecAllocatorOptions[:], 30 | chromedp.Flag("headless", true), 31 | chromedp.Flag("ignore-certificate-errors", true), 32 | chromedp.Flag("disable-gpu", true), 33 | chromedp.Flag("enable-automation", true), 34 | chromedp.Flag("disable-extensions", false), 35 | chromedp.Flag("disable-setuid-sandbox", true), 36 | ) 37 | 38 | if options.Debug { 39 | // show the chrome page in debug mode 40 | opts = append(chromedp.DefaultExecAllocatorOptions[:], 41 | chromedp.Flag("headless", false), 42 | chromedp.Flag("ignore-certificate-errors", true), 43 | chromedp.Flag("disable-gpu", true), 44 | chromedp.Flag("enable-automation", true), 45 | chromedp.Flag("disable-extensions", false), 46 | chromedp.Flag("disable-setuid-sandbox", true), 47 | ) 48 | } 49 | 50 | allocCtx, bcancel := chromedp.NewExecAllocator(context.Background(), opts...) 51 | allocCtx, bcancel = context.WithTimeout(allocCtx, time.Duration(options.Timeout)*time.Second) 52 | defer bcancel() 53 | chromeContext, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf)) 54 | defer cancel() 55 | 56 | // catch the pop up 57 | chromedp.ListenTarget(chromeContext, func(event interface{}) { 58 | if _, ok := event.(*page.EventJavascriptDialogOpening); ok { 59 | // fmt.Println("closing alert:", ev.Message) 60 | utils.DebugF("Detecting Pop-up: %v", url) 61 | res.HasPopUp = true 62 | go func() { 63 | if err := chromedp.Run(chromeContext, 64 | page.HandleJavaScriptDialog(true), 65 | ); err != nil { 66 | res.HasPopUp = false 67 | } 68 | }() 69 | } 70 | }) 71 | timeStart := time.Now() 72 | // waiting time for the page to load 73 | waiting := time.Duration(1) 74 | if req.Timeout != 0 { 75 | waiting = time.Duration(req.Timeout) 76 | } 77 | // start Chrome and run given tasks 78 | err := chromedp.Run( 79 | chromeContext, 80 | chromeTask(chromeContext, url, 81 | // @TODO: add header here 82 | map[string]interface{}{}, 83 | &res), 84 | // wait for the page to load 85 | chromedp.Sleep(waiting*time.Second), 86 | chromedp.ActionFunc(func(ctx context.Context) error { 87 | node, err := dom.GetDocument().Do(ctx) 88 | if err != nil { 89 | return err 90 | } 91 | res.Body, err = dom.GetOuterHTML().WithNodeID(node.NodeID).Do(ctx) 92 | return err 93 | }), 94 | ) 95 | res.ResponseTime = time.Since(timeStart).Seconds() 96 | if err != nil { 97 | utils.ErrorF("%v", err) 98 | return res, err 99 | } 100 | 101 | res.Beautify = fmt.Sprintf("%v\n%v\n", res.StatusCode, res.Body) 102 | return res, err 103 | } 104 | 105 | // chrome debug protocol tasks to run 106 | func chromeTask(chromeContext context.Context, url string, requestHeaders map[string]interface{}, res *libs.Response) chromedp.Tasks { 107 | // setup a listener for events 108 | chromedp.ListenTarget(chromeContext, func(event interface{}) { 109 | // get which type of event it is 110 | switch msg := event.(type) { 111 | // just before request sent 112 | case *network.EventRequestWillBeSent: 113 | request := msg.Request 114 | // see if we have been redirected 115 | // if so, change the URL that we are tracking 116 | if msg.RedirectResponse != nil { 117 | url = request.URL 118 | } 119 | 120 | // once we have the full response 121 | case *network.EventResponseReceived: 122 | response := msg.Response 123 | // is the request we want the status/headers on? 124 | if response.URL == url { 125 | res.StatusCode = int(response.Status) 126 | // fmt.Printf(" url: %s\n", response.URL) 127 | // fmt.Printf(" status code: %d\n", res.StatusCode) 128 | for k, v := range response.Headers { 129 | header := make(map[string]string) 130 | // fmt.Println(k, v) 131 | header[k] = v.(string) 132 | res.Headers = append(res.Headers, header) 133 | } 134 | } 135 | } 136 | 137 | }) 138 | 139 | return chromedp.Tasks{ 140 | network.Enable(), 141 | network.SetExtraHTTPHeaders(network.Headers(requestHeaders)), 142 | chromedp.Navigate(url), 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /core/middleware.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/url" 7 | "os" 8 | "os/exec" 9 | "path" 10 | "strconv" 11 | "strings" 12 | 13 | "github.com/jaeles-project/jaeles/libs" 14 | "github.com/jaeles-project/jaeles/utils" 15 | "github.com/thoas/go-funk" 16 | 17 | "github.com/robertkrimen/otto" 18 | ) 19 | 20 | // @NOTE: Middleware allow execute command on your machine 21 | // So make sure you read the signature before you run it 22 | 23 | // MiddleWare is main function for middleware 24 | func MiddleWare(rec *libs.Record, options libs.Options) { 25 | // func MiddleWare(req *libs.Request) { 26 | vm := otto.New() 27 | var middlewareOutput string 28 | 29 | vm.Set("Host2IP", func(call otto.FunctionCall) otto.Value { 30 | var realHeaders []map[string]string 31 | for _, head := range rec.Request.Headers { 32 | containHost := funk.Contains(head, "Host") 33 | if containHost == false { 34 | realHeaders = append(realHeaders, head) 35 | } 36 | } 37 | HostHeader := Host2IP(rec.Request.URL) 38 | if !funk.IsEmpty(HostHeader) { 39 | realHeaders = append(realHeaders, HostHeader) 40 | } 41 | rec.Request.Headers = realHeaders 42 | return otto.Value{} 43 | }) 44 | 45 | vm.Set("InvokeCmd", func(call otto.FunctionCall) otto.Value { 46 | rawCmd := call.Argument(0).String() 47 | result := InvokeCmd(rec, rawCmd) 48 | middlewareOutput += result 49 | utils.DebugF(result) 50 | return otto.Value{} 51 | }) 52 | 53 | vm.Set("TurboIntruder", func(call otto.FunctionCall) otto.Value { 54 | if rec.Request.Raw != "" { 55 | result := TurboIntruder(rec) 56 | utils.DebugF(result) 57 | } 58 | return otto.Value{} 59 | }) 60 | 61 | for _, middleString := range rec.Request.Middlewares { 62 | utils.DebugF(middleString) 63 | vm.Run(middleString) 64 | } 65 | 66 | if middlewareOutput != "" { 67 | rec.Request.MiddlewareOutput = middlewareOutput 68 | } 69 | } 70 | 71 | // Host2IP replace Host header with IP address 72 | func Host2IP(rawURL string) map[string]string { 73 | // HostHeader = 74 | u, err := url.Parse(rawURL) 75 | if err != nil { 76 | return map[string]string{} 77 | } 78 | hostname := u.Hostname() 79 | resolved, err := net.LookupHost(hostname) 80 | if err != nil { 81 | return map[string]string{} 82 | } 83 | 84 | ip := resolved[0] 85 | if u.Port() == "443" || u.Port() == "80" || u.Port() == "" { 86 | hostname = ip 87 | } else { 88 | hostname = ip + ":" + u.Port() 89 | } 90 | 91 | return map[string]string{"Host": hostname} 92 | } 93 | 94 | // InvokeCmd execute external command 95 | func InvokeCmd(rec *libs.Record, rawCmd string) string { 96 | target := ParseTarget(rec.Request.URL) 97 | realCommand := Encoder(rec.Request.Encoding, ResolveVariable(rawCmd, target)) 98 | utils.DebugF("Execute Command: %v", realCommand) 99 | command := []string{ 100 | "bash", 101 | "-c", 102 | realCommand, 103 | } 104 | out, _ := exec.Command(command[0], command[1:]...).CombinedOutput() 105 | rec.Request.MiddlewareOutput = string(out) 106 | return string(out) 107 | } 108 | 109 | // TurboIntruder execute Turbo Intruder CLI 110 | func TurboIntruder(rec *libs.Record) string { 111 | req := rec.Request 112 | turboPath := ResolveVariable("{{.homePath}}/plugins/turbo-intruder/turbo-intruder-all.jar", req.Target) 113 | scriptPath := ResolveVariable("{{.homePath}}/plugins/turbo-intruder/basic.py", req.Target) 114 | 115 | // create a folder in case it didn't exist 116 | logReqPath := ResolveVariable("{{.homePath}}/log/req/", req.Target) 117 | url := ResolveVariable("{{.URL}}", req.Target) 118 | rec.Request.URL = url 119 | if _, err := os.Stat(logReqPath); os.IsNotExist(err) { 120 | os.MkdirAll(logReqPath, 0750) 121 | } 122 | // write request to a file 123 | rawReq := ResolveVariable(req.Raw, req.Target) 124 | reqPath := path.Join(logReqPath, utils.GenHash(rawReq)) 125 | utils.WriteToFile(reqPath, rawReq) 126 | 127 | // call the command and parse some info 128 | turboCmd := fmt.Sprintf(`java -jar %v %v %v %v foo`, turboPath, scriptPath, reqPath, url) 129 | 130 | command := []string{ 131 | "bash", 132 | "-c", 133 | turboCmd, 134 | } 135 | out, _ := exec.Command(command[0], command[1:]...).CombinedOutput() 136 | 137 | // parse output 138 | rawOutput := string(out) 139 | if strings.Contains(rawOutput, "=-+-================") { 140 | // split the prefix 141 | resp := strings.Split(rawOutput, "=-+-================")[1] 142 | result := strings.Split(resp, "------------------+=") 143 | 144 | // [Info] 403 11585 0.272 145 | info := result[0] 146 | statusCode, _ := strconv.Atoi(strings.Split(info, " ")[1]) 147 | rec.Response.StatusCode = statusCode 148 | 149 | length, _ := strconv.Atoi(strings.Split(info, " ")[2]) 150 | rec.Response.Length = length 151 | 152 | resTime, _ := strconv.ParseFloat(strings.TrimSpace(strings.Split(info, " ")[2]), 64) 153 | rec.Response.ResponseTime = resTime 154 | 155 | rec.Request.Beautify = result[1] 156 | rec.Response.Beautify = result[2] 157 | verbose := fmt.Sprintf("[TurboIntruder] %v %v %v %v", rec.Request.URL, reqPath, rec.Response.StatusCode, rec.Response.ResponseTime) 158 | return verbose 159 | } 160 | 161 | verbose := fmt.Sprintf("[TurboIntruder] Error sending request from: %v", reqPath) 162 | return verbose 163 | } 164 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jaeles-project/jaeles/core" 6 | "github.com/jaeles-project/jaeles/database" 7 | "github.com/jaeles-project/jaeles/libs" 8 | "github.com/jaeles-project/jaeles/utils" 9 | "github.com/jinzhu/gorm" 10 | "github.com/spf13/cobra" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strings" 15 | ) 16 | 17 | var options = libs.Options{} 18 | 19 | // DB database variables 20 | var DB *gorm.DB 21 | 22 | // RootCmd represents the base command when called without any subcommands 23 | var RootCmd = &cobra.Command{ 24 | Use: "jaeles", 25 | Short: "Jaeles Scanner", 26 | Long: libs.Banner(), 27 | } 28 | 29 | // Execute main function 30 | func Execute() { 31 | if err := RootCmd.Execute(); err != nil { 32 | fmt.Println(err) 33 | os.Exit(1) 34 | } 35 | } 36 | 37 | func init() { 38 | cobra.OnInitialize(initConfig) 39 | RootCmd.PersistentFlags().StringVar(&options.ConfigFile, "config", "", "config file (default is $HOME/.jaeles/config.yaml)") 40 | RootCmd.PersistentFlags().StringVar(&options.RootFolder, "rootDir", "~/.jaeles/", "root Project") 41 | RootCmd.PersistentFlags().StringVar(&options.SignFolder, "signDir", "~/.jaeles/signatures-base/", "Folder contain default signatures") 42 | RootCmd.PersistentFlags().StringVar(&options.ScanID, "scanID", "", "Scan ID") 43 | 44 | RootCmd.PersistentFlags().StringVar(&options.Proxy, "proxy", "", "proxy") 45 | RootCmd.PersistentFlags().IntVar(&options.Timeout, "timeout", 20, "HTTP timeout") 46 | RootCmd.PersistentFlags().IntVar(&options.Delay, "delay", 100, "Milliseconds delay for polling new job") 47 | RootCmd.PersistentFlags().IntVar(&options.Retry, "retry", 0, "retry") 48 | 49 | RootCmd.PersistentFlags().BoolVar(&options.SaveRaw, "save-raw", false, "save raw request") 50 | RootCmd.PersistentFlags().BoolVar(&options.NoOutput, "no-output", false, "Do not store raw output") 51 | RootCmd.PersistentFlags().BoolVar(&options.EnablePassive, "passive", false, "Do not run passive detections") 52 | RootCmd.PersistentFlags().BoolVar(&options.NoBackGround, "no-background", false, "Do not run background task") 53 | RootCmd.PersistentFlags().BoolVarP(&options.Verbose, "verbose", "v", false, "Verbose") 54 | RootCmd.PersistentFlags().BoolVar(&options.Debug, "debug", false, "Debug") 55 | RootCmd.PersistentFlags().IntVar(&options.Refresh, "refresh", 10, "Refresh") 56 | 57 | RootCmd.PersistentFlags().IntVarP(&options.Concurrency, "concurrency", "c", 20, "concurrency") 58 | RootCmd.PersistentFlags().IntVarP(&options.Threads, "threads", "t", 1, "Enable parallel in single signature") 59 | RootCmd.PersistentFlags().StringVarP(&options.Output, "output", "o", "out", "output folder name") 60 | RootCmd.PersistentFlags().StringVarP(&options.SummaryOutput, "summaryOutput", "O", "", "Summary output file") 61 | RootCmd.PersistentFlags().StringVarP(&options.LogFile, "log", "l", "", "log file") 62 | // custom params from cli 63 | RootCmd.PersistentFlags().StringSliceVarP(&options.Signs, "signs", "s", []string{}, "Signature selector (Multiple -s flags are accepted)") 64 | RootCmd.PersistentFlags().StringSliceVarP(&options.Excludes, "exclude", "x", []string{}, "Exclude Signature selector (Multiple -x flags are accepted)") 65 | RootCmd.PersistentFlags().StringSliceVarP(&options.Params, "params", "p", []string{}, "Custom params --params='foo=bar'") 66 | } 67 | 68 | // initConfig reads in config file and ENV variables if set. 69 | func initConfig() { 70 | fmt.Printf("Jaeles %v by %v\n", libs.VERSION, libs.AUTHOR) 71 | if options.Debug { 72 | options.Verbose = true 73 | } 74 | utils.InitLog(&options) 75 | core.InitConfig(&options) 76 | 77 | // Init DB 78 | var err error 79 | DB, err = database.InitDB(utils.NormalizePath(options.Server.DBPath)) 80 | if err != nil { 81 | fmt.Printf("Can't connect to DB at %v\n", options.Server.DBPath) 82 | os.Exit(-1) 83 | } 84 | } 85 | 86 | // SelectSign select signature 87 | func SelectSign() { 88 | //options.SelectedSigns 89 | var selectedSigns []string 90 | 91 | // default is all signature 92 | if len(options.Signs) == 0 { 93 | selectedSigns = core.SelectSign("**") 94 | } 95 | 96 | // search signature through Signatures table 97 | for _, signName := range options.Signs { 98 | selectedSigns = append(selectedSigns, core.SelectSign(signName)...) 99 | Signs := database.SelectSign(signName) 100 | selectedSigns = append(selectedSigns, Signs...) 101 | } 102 | 103 | // exclude some signature 104 | if len(options.Excludes) > 0 { 105 | for _, exclude := range options.Excludes { 106 | for index, sign := range selectedSigns { 107 | if strings.Contains(sign, exclude) { 108 | selectedSigns = append(selectedSigns[:index], selectedSigns[index+1:]...) 109 | } 110 | r, err := regexp.Compile(exclude) 111 | if err != nil { 112 | continue 113 | } 114 | if r.MatchString(sign) { 115 | selectedSigns = append(selectedSigns[:index], selectedSigns[index+1:]...) 116 | } 117 | 118 | } 119 | } 120 | } 121 | options.SelectedSigns = selectedSigns 122 | 123 | if len(selectedSigns) == 0 { 124 | fmt.Println("[Error] No signature loaded") 125 | os.Exit(1) 126 | } 127 | utils.InforF("Signatures Loaded: %v", len(selectedSigns)) 128 | signInfo := fmt.Sprintf("Signature Loaded: ") 129 | for _, signName := range selectedSigns { 130 | signInfo += fmt.Sprintf("%v ", filepath.Base(signName)) 131 | } 132 | utils.InforF(signInfo) 133 | 134 | // create new scan or group with old one 135 | var scanID string 136 | if options.ScanID == "" { 137 | scanID = database.NewScan(options, "scan", selectedSigns) 138 | } else { 139 | scanID = options.ScanID 140 | } 141 | utils.InforF("Start Scan with ID: %v", scanID) 142 | options.ScanID = scanID 143 | } 144 | -------------------------------------------------------------------------------- /libs/banner.go: -------------------------------------------------------------------------------- 1 | package libs 2 | 3 | import ( 4 | "github.com/fatih/color" 5 | ) 6 | 7 | // Banner print ascii banner 8 | func Banner() string { 9 | version := color.HiWhiteString(VERSION) 10 | author := color.MagentaString(AUTHOR) 11 | b := color.GreenString(``) 12 | 13 | b += "\n" + color.HiGreenString(``) 14 | b += "\n" + color.GreenString(` ,+izzir, `) 15 | b += "\n" + color.GreenString(` '*K@@Q8&Q@@8t' `) 16 | b += "\n" + color.GreenString(` !Q@N;''`) + color.HiWhiteString(`,~~;`) + color.GreenString(`\D@@t' `) 17 | b += "\n" + color.GreenString(` ,Q@q. `) + color.HiWhiteString(`'~~~~~~;`) + color.GreenString(`5@@L `) 18 | b += "\n" + color.GreenString(` L@@+ `) + color.HiWhiteString(`'~~~~~~~`) + color.GreenString(`^Q@X `) 19 | b += "\n" + color.GreenString(` ^@@z `) + color.HiWhiteString(`'~~~~~~~`) + color.GreenString(`|Q@y `) 20 | b += "\n" + color.GreenString(` 'Z@@7 `) + color.HiWhiteString(`'~~~~;`) + color.GreenString(`TQ@N, `) 21 | b += "\n" + color.GreenString(` ^%@QhJ7fmDQ@Q7' ~}DQ@@@Qqv, `) 22 | b += "\n" + color.GreenString(` ~jdQ@@Qdjr' ,U@@qv=|tm#@QY `) 23 | b += "\n" + color.GreenString(` *@@= D@&;`) + color.HiWhiteString(` ,~~~`) + color.GreenString(`;f@@^ `) 24 | b += "\n" + color.GreenString(` <@@+ .@@L`) + color.HiWhiteString(` '~~~~~~`) + color.GreenString(`K@P `) 25 | b += "\n" + color.GreenString(` ,
3 |
4 |
52 | Detect Jira SSRF CVE-2019-8451 53 |
54 | 55 | ### Burp Integration 56 | 57 |  58 | 59 | Plugin can be found [here](https://github.com/jaeles-project/jaeles-plugins/blob/master/jaeles-burp.py) and Video Guide [here](https://youtu.be/1lxsYhfTq3M) 60 | 61 | ## Mentions 62 | 63 | [My introduction slide about Jaeles](https://speakerdeck.com/j3ssie/jaeles-the-swiss-army-knife-for-automated-web-application-testing) 64 | 65 | 66 | ### Planned Features 67 | 68 | * Adding more signatures. 69 | * Adding more input sources. 70 | * Adding more APIs to get access to more properties of the request. 71 | * Adding proxy plugins to directly receive input from browser of http client. 72 | * ~~Adding passive signature for passive checking each request.~~ 73 | * Adding more action on Web UI. 74 | * Integrate with many other tools. 75 | 76 | ## Contribute 77 | 78 | If you have some new idea about this project, issue, feedback or found some valuable tool feel free to open an issue for just DM me via @j3ssiejjj. 79 | Feel free to submit new signature to this [repo](https://github.com/jaeles-project/jaeles-signatures). 80 | 81 | ### Credits 82 | 83 | * Special thanks to [chaitin](https://github.com/chaitin/xray) team for sharing ideas to me for build the architecture. 84 | 85 | * React components is powered by [Carbon](https://www.carbondesignsystem.com/) and [carbon-tutorial](https://github.com/carbon-design-system/carbon-tutorial). 86 | 87 | * Awesomes artworks are powered by [Freepik](http://freepik.com) at [flaticon.com](http://flaticon.com). 88 | 89 | ## In distributions 90 | 91 | [](https://repology.org/project/jaeles/versions) 92 | 93 | ## Contributors 94 | 95 | ### Code Contributors 96 | 97 | This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. 98 |
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec molestie.
129 | Sed aliquam sem ut arcu.
painted by: r4w8173
Lorem ipsum dolor sit amet. Donec molestie.
131 | Sed aliquam sem ut arcu.
painted by: r4w8173
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec molestie.
132 | Sed aliquam sem ut arcu. Phasellus sollicitudin.
133 |
painted by: r4w8173
Warning: This is not a real shop. This is an example PHP application, which is intentionally vulnerable to web attacks. It is intended to help you test Acunetix. It also helps you understand how developer errors and bad configuration may let someone break into your website. You can use it to test other tools and your manual hacking skills as well. Tip: Look for potential SQL Injections, Cross-site Scripting (XSS), and Cross-site Request Forgery (CSRF), and more.
184 |msxml4\\.dll<\\/font>|
msxml3\\.dll<\\/font>|4005 Notes error: Query is not understandable|SimpleXMLElement::xpath|xmlXPathEval:|simplexml_load_string|parser error :|An error occured!|xmlParseEntityDecl|simplexml_load_string|xmlParseInternalSubset|DOCTYPE improperly terminated|Start tag expected|No declaration for attribute|No declaration for element|failed to load external entity|Start tag expected|Invalid URI: file:\\/\\/\\/|Malformed declaration expecting version|Unicode strings with encoding|must be well-formed|Content is not allowed in prolog|org.xml.sax|SAXParseException|com.sun.org.apache.xerces|ParseError|nokogiri|REXML|XML syntax error on line|Error unmarshaling XML|conflicts with field|illegal character code|XML Parsing Error|SyntaxError|no root element|not well-formed\n")`}, 132 | }, 133 | ////// 134 | libs.Rule{ 135 | ID: "default-error-03", 136 | Reason: "PHP Error", 137 | Detections: []string{ 138 | `RegexSearch("response", "Warning: include\(|Warning: unlink\(|for inclusion \(include_path=|fread\(|Failed opening required|Warning: file_get_contents\(|Fatal error: require_once\(|Warning: file_exists\(")`}, 139 | }, 140 | 141 | libs.Rule{ 142 | ID: "default-error-04", 143 | Reason: "Java Error", 144 | Detections: []string{ 145 | `RegexSearch("response", "java\.io\.FileNotFoundException|java\.lang\.Exception|java\.lang\.IllegalArgumentException|java\.net\.MalformedURLException"`}, 146 | }, 147 | 148 | libs.Rule{ 149 | ID: "default-error-05", 150 | Reason: "XML Error", 151 | Detections: []string{ 152 | `RegexSearch("response", "simplexml_load_string|parser error :|An error occured!|xmlParseEntityDecl|simplexml_load_string|xmlParseInternalSubset|DOCTYPE improperly terminated|Start tag expected|No declaration for attribute|No declaration for element|failed to load external entity|Start tag expected|Invalid URI: file:\/\/\/|Malformed declaration expecting version|Unicode strings with encoding|must be well-formed|Content is not allowed in prolog|org.xml.sax|SAXParseException|com.sun.org.apache.xerces|ParseError|nokogiri|REXML|XML syntax error on line|Error unmarshaling XML|conflicts with field|illegal character code|XML Parsing Error|SyntaxError|no root element|not well-formed"`}, 153 | }, 154 | 155 | libs.Rule{ 156 | ID: "default-error-06", 157 | Reason: "RCE Error", 158 | Detections: []string{ 159 | `RegexSearch("response", "root:|(uid|gid|groups)=\d+|bytes from \b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b|Configuration File \(php\.ini\) Path |vulnerable 10|Trying \b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b|\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b\s+localhost|BROADCAST,MULTICAST|drwxr-xr|Active Internet connections|Syntax error|sh:|Average Speed Time|dir: cannot access||drwxrwxr|GNU/Linux"`}, 160 | }, 161 | 162 | //libs.Rule{ 163 | // ID: "default-error-02", 164 | // Reason: "General Error", 165 | // Detections: []string{ 166 | // `RegexSearch("response", "regexhere"`}, 167 | //}, 168 | 169 | } 170 | return libs.Passive{ 171 | Name: "Default", 172 | Desc: "Default Rule for catching common Error", 173 | Rules: rules, 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /core/parser.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/jaeles-project/jaeles/utils" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/jaeles-project/jaeles/database" 14 | "github.com/jaeles-project/jaeles/libs" 15 | "github.com/thoas/go-funk" 16 | "gopkg.in/yaml.v2" 17 | ) 18 | 19 | // ParseSign parsing YAML signature file 20 | func ParseSign(signFile string) (sign libs.Signature, err error) { 21 | yamlFile, err := ioutil.ReadFile(signFile) 22 | if err != nil { 23 | utils.ErrorF("yamlFile.Get err #%v - %v", err, signFile) 24 | } 25 | err = yaml.Unmarshal(yamlFile, &sign) 26 | if err != nil { 27 | utils.ErrorF("Error: %v - %v", err, signFile) 28 | } 29 | // set some default value 30 | if sign.Info.Category == "" { 31 | if strings.Contains(sign.ID, "-") { 32 | sign.Info.Category = strings.Split(sign.ID, "-")[0] 33 | } else { 34 | sign.Info.Category = sign.ID 35 | } 36 | } 37 | if sign.Info.Name == "" { 38 | sign.Info.Name = sign.ID 39 | } 40 | if sign.Info.Risk == "" { 41 | sign.Info.Risk = "Potential" 42 | } 43 | return sign, err 44 | } 45 | 46 | // ParsePassive parsing YAML passive file 47 | func ParsePassive(passiveFile string) (passive libs.Passive, err error) { 48 | yamlFile, err := ioutil.ReadFile(passiveFile) 49 | if err != nil { 50 | utils.ErrorF("yamlFile.Get err #%v - %v", err, passiveFile) 51 | } 52 | err = yaml.Unmarshal(yamlFile, &passive) 53 | if err != nil { 54 | utils.ErrorF("Error: %v - %v", err, passiveFile) 55 | } 56 | return passive, err 57 | } 58 | 59 | // ParseTarget parsing target and some variable for template 60 | func ParseTarget(raw string) map[string]string { 61 | target := make(map[string]string) 62 | target["Raw"] = raw 63 | if raw == "" { 64 | return target 65 | } 66 | u, err := url.Parse(raw) 67 | 68 | // something wrong so parsing it again 69 | if err != nil || u.Scheme == "" || strings.Contains(u.Scheme, ".") { 70 | raw = fmt.Sprintf("https://%v", raw) 71 | u, err = url.Parse(raw) 72 | if err != nil { 73 | return target 74 | } 75 | // fmt.Println("parse again") 76 | } 77 | var hostname string 78 | var query string 79 | port := u.Port() 80 | // var domain string 81 | domain := u.Hostname() 82 | 83 | query = u.RawQuery 84 | if u.Port() == "" { 85 | if strings.Contains(u.Scheme, "https") { 86 | port = "443" 87 | } else { 88 | port = "80" 89 | } 90 | 91 | hostname = u.Hostname() 92 | } else { 93 | // ignore common port in Host 94 | if u.Port() == "443" || u.Port() == "80" { 95 | hostname = u.Hostname() 96 | } else { 97 | hostname = u.Hostname() + ":" + u.Port() 98 | } 99 | } 100 | 101 | target["Scheme"] = u.Scheme 102 | target["Path"] = u.Path 103 | target["Domain"] = domain 104 | target["Host"] = hostname 105 | target["Port"] = port 106 | target["RawQuery"] = query 107 | 108 | if (target["RawQuery"] != "") && (port == "80" || port == "443") { 109 | target["URL"] = fmt.Sprintf("%v://%v%v?%v", target["Scheme"], target["Host"], target["Path"], target["RawQuery"]) 110 | } else if port != "80" && port != "443" { 111 | target["URL"] = fmt.Sprintf("%v://%v:%v%v?%v", target["Scheme"], target["Domain"], target["Port"], target["Path"], target["RawQuery"]) 112 | } else { 113 | target["URL"] = fmt.Sprintf("%v://%v%v", target["Scheme"], target["Host"], target["Path"]) 114 | } 115 | 116 | uu, _ := url.Parse(raw) 117 | target["BaseURL"] = fmt.Sprintf("%v://%v", uu.Scheme, uu.Host) 118 | target["Extension"] = filepath.Ext(target["BaseURL"]) 119 | return target 120 | } 121 | 122 | // MoreVariables get more options to render in sign template 123 | func MoreVariables(target map[string]string, sign libs.Signature, options libs.Options) map[string]string { 124 | realTarget := target 125 | 126 | ssrf := database.GetDefaultBurpCollab() 127 | if ssrf != "" { 128 | target["oob"] = ssrf 129 | } else { 130 | target["oob"] = database.GetCollab() 131 | } 132 | 133 | // more options 134 | realTarget["rootPath"] = options.RootFolder 135 | realTarget["resourcePath"] = options.ResourcesFolder 136 | realTarget["proxy"] = options.Proxy 137 | realTarget["output"] = options.Output 138 | 139 | // default params in signature 140 | signParams := sign.Params 141 | if len(signParams) > 0 { 142 | for _, param := range signParams { 143 | for k, v := range param { 144 | // variable as a script 145 | if strings.Contains(v, "(") && strings.Contains(v, ")") { 146 | newValue := RunVariables(v) 147 | if len(newValue) > 0 { 148 | realTarget[k] = newValue[0] 149 | } 150 | } else { 151 | realTarget[k] = v 152 | } 153 | } 154 | } 155 | } 156 | 157 | // more params 158 | if len(options.Params) > 0 { 159 | params := ParseParams(options.Params) 160 | if len(params) > 0 { 161 | for k, v := range params { 162 | realTarget[k] = v 163 | } 164 | } 165 | } 166 | return realTarget 167 | } 168 | 169 | // ParseParams parse more params from cli 170 | func ParseParams(rawParams []string) map[string]string { 171 | params := make(map[string]string) 172 | 173 | for _, item := range rawParams { 174 | if strings.Contains(item, "=") { 175 | data := strings.Split(item, "=") 176 | params[data[0]] = strings.Replace(item, data[0]+"=", "", -1) 177 | } 178 | } 179 | return params 180 | } 181 | 182 | // ParseOrigin parse origin request 183 | func ParseOrigin(req libs.Request, sign libs.Signature, options libs.Options) libs.Request { 184 | target := sign.Target 185 | // resolve some parts with global variables first 186 | req.Target = target 187 | req.URL = ResolveVariable(req.URL, target) 188 | // @NOTE: backward compatible 189 | if req.URL == "" && req.Path != "" { 190 | req.URL = ResolveVariable(req.Path, target) 191 | } 192 | req.Body = ResolveVariable(req.Body, target) 193 | req.Headers = ResolveHeader(req.Headers, target) 194 | req.Middlewares = ResolveDetection(req.Middlewares, target) 195 | req.Conclusions = ResolveDetection(req.Conclusions, target) 196 | 197 | // parse raw request 198 | if req.Raw != "" { 199 | rawReq := ResolveVariable(req.Raw, target) 200 | burpReq := ParseBurpRequest(rawReq) 201 | burpReq.Detections = ResolveDetection(req.Detections, target) 202 | burpReq.Middlewares = ResolveDetection(req.Middlewares, target) 203 | burpReq.Conclusions = ResolveDetection(req.Conclusions, target) 204 | return burpReq 205 | } 206 | return req 207 | } 208 | 209 | // ParseRequest parse request part in YAML signature file 210 | func ParseRequest(req libs.Request, sign libs.Signature, options libs.Options) []libs.Request { 211 | var Reqs []libs.Request 212 | target := sign.Target 213 | 214 | // resolve some parts with global variables first 215 | req.Target = target 216 | req.URL = ResolveVariable(req.URL, target) 217 | // @NOTE: backward compatible 218 | if req.URL == "" && req.Path != "" { 219 | req.URL = ResolveVariable(req.Path, target) 220 | } 221 | req.Body = ResolveVariable(req.Body, target) 222 | req.Headers = ResolveHeader(req.Headers, target) 223 | req.Middlewares = ResolveDetection(req.Middlewares, target) 224 | req.Conditions = ResolveDetection(req.Conditions, target) 225 | 226 | if sign.Type != "fuzz" { 227 | // in case we only want to run a middleware alone 228 | if req.Raw != "" { 229 | rawReq := ResolveVariable(req.Raw, target) 230 | burpReq := ParseBurpRequest(rawReq) 231 | burpReq.Detections = ResolveDetection(req.Detections, target) 232 | burpReq.Middlewares = ResolveDetection(req.Middlewares, target) 233 | Reqs = append(Reqs, burpReq) 234 | } 235 | 236 | // if req.path is blank 237 | if req.URL == "" && funk.IsEmpty(req.Middlewares) { 238 | return Reqs 239 | } else if !funk.IsEmpty(req.Middlewares) { 240 | Req := req 241 | Req.Middlewares = ResolveDetection(req.Middlewares, target) 242 | Reqs = append(Reqs, Req) 243 | return Reqs 244 | } 245 | req.Detections = ResolveDetection(req.Detections, target) 246 | // normal requests here 247 | Req := req 248 | Req.Redirect = req.Redirect 249 | if Req.URL != "" { 250 | Reqs = append(Reqs, Req) 251 | } 252 | return Reqs 253 | } 254 | 255 | // start parse fuzz req 256 | // only take URL as a input from cli 257 | var record libs.Record 258 | 259 | // parse raw request in case we have -r options as a origin request 260 | if req.Raw != "" { 261 | rawReq := ResolveVariable(req.Raw, target) 262 | burpReq := ParseBurpRequest(rawReq) 263 | // resolve again with custom delimiter generator 264 | burpReq.Generators = req.Generators 265 | burpReq.Detections = req.Detections 266 | burpReq.Middlewares = req.Middlewares 267 | record.OriginReq = burpReq 268 | } else { 269 | record.OriginReq.URL = target["URL"] 270 | } 271 | 272 | record.Request = req 273 | reqs := ParseFuzzRequest(record, sign) 274 | if len(reqs) > 0 { 275 | Reqs = append(Reqs, reqs...) 276 | } 277 | utils.DebugF("[New Parsed Reuqest] %v", len(Reqs)) 278 | 279 | // repeat section 280 | if req.Repeat == 0 { 281 | return Reqs 282 | } 283 | realReqsWithRepeat := Reqs 284 | for i := 0; i < req.Repeat-1; i++ { 285 | realReqsWithRepeat = append(realReqsWithRepeat, Reqs...) 286 | } 287 | return realReqsWithRepeat 288 | } 289 | 290 | // ParseFuzzRequest parse request recive in API server 291 | func ParseFuzzRequest(record libs.Record, sign libs.Signature) []libs.Request { 292 | req := record.Request 293 | 294 | var Reqs []libs.Request 295 | // color.Green("-- Start do Injecting") 296 | if req.URL == "" { 297 | req.URL = record.OriginReq.URL 298 | } 299 | Reqs = Generators(req, sign) 300 | return Reqs 301 | } 302 | 303 | // ParsePayloads parse payload to replace 304 | func ParsePayloads(sign libs.Signature) []string { 305 | rawPayloads := []string{} 306 | rawPayloads = append(rawPayloads, sign.Payloads...) 307 | // strip out blank line 308 | for index, value := range rawPayloads { 309 | if strings.Trim(value, " ") == "" { 310 | rawPayloads = append(rawPayloads[:index], rawPayloads[index+1:]...) 311 | } 312 | } 313 | 314 | var realPayloads []string 315 | // check if use variables or not 316 | if len(sign.Variables) > 0 { 317 | realVariables := ParseVariable(sign) 318 | // replace payload with variables 319 | if len(realVariables) > 0 { 320 | for _, variable := range realVariables { 321 | for _, payload := range rawPayloads { 322 | target := make(map[string]string) 323 | // replace here 324 | for k, v := range variable { 325 | target[k] = v 326 | } 327 | payload := ResolveVariable(payload, target) 328 | realPayloads = append(realPayloads, payload) 329 | } 330 | } 331 | } 332 | } else { 333 | // just append the payload 334 | for _, payload := range rawPayloads { 335 | realPayloads = append(realPayloads, ResolveVariable(payload, sign.Target)) 336 | } 337 | } 338 | return realPayloads 339 | } 340 | 341 | // ParseBurpRequest parse burp style request 342 | func ParseBurpRequest(raw string) (req libs.Request) { 343 | var realReq libs.Request 344 | realReq.Raw = raw 345 | reader := bufio.NewReader(strings.NewReader(raw)) 346 | parsedReq, err := http.ReadRequest(reader) 347 | if err != nil { 348 | return realReq 349 | } 350 | realReq.Method = parsedReq.Method 351 | // URL part 352 | if parsedReq.URL.Host == "" { 353 | realReq.Host = parsedReq.Host 354 | parsedReq.URL.Host = parsedReq.Host 355 | } 356 | if parsedReq.URL.Scheme == "" { 357 | if parsedReq.Referer() == "" { 358 | realReq.Scheme = "https" 359 | parsedReq.URL.Scheme = "https" 360 | } else { 361 | u, err := url.Parse(parsedReq.Referer()) 362 | if err == nil { 363 | realReq.Scheme = u.Scheme 364 | parsedReq.URL.Scheme = u.Scheme 365 | } 366 | } 367 | } 368 | // fmt.Println(parsedReq.URL) 369 | // parsedReq.URL.RequestURI = parsedReq.RequestURI 370 | realReq.URL = parsedReq.URL.String() 371 | realReq.Path = parsedReq.RequestURI 372 | realReq.Headers = ParseHeaders(parsedReq.Header) 373 | 374 | body, _ := ioutil.ReadAll(parsedReq.Body) 375 | realReq.Body = string(body) 376 | 377 | return realReq 378 | } 379 | 380 | // ParseHeaders parse header for sending method 381 | func ParseHeaders(rawHeaders map[string][]string) []map[string]string { 382 | var headers []map[string]string 383 | for name, value := range rawHeaders { 384 | header := map[string]string{ 385 | name: strings.Join(value[:], ""), 386 | } 387 | headers = append(headers, header) 388 | } 389 | return headers 390 | } 391 | 392 | // ParseBurpResponse parse burp style response 393 | func ParseBurpResponse(rawReq string, rawRes string) (res libs.Response) { 394 | // var res libs.Response 395 | readerr := bufio.NewReader(strings.NewReader(rawReq)) 396 | parsedReq, _ := http.ReadRequest(readerr) 397 | 398 | reader := bufio.NewReader(strings.NewReader(rawRes)) 399 | parsedRes, err := http.ReadResponse(reader, parsedReq) 400 | if err != nil { 401 | return res 402 | } 403 | 404 | res.Status = fmt.Sprintf("%v %v", parsedRes.Status, parsedRes.Proto) 405 | res.StatusCode = parsedRes.StatusCode 406 | 407 | var headers []map[string]string 408 | for name, value := range parsedReq.Header { 409 | header := map[string]string{ 410 | name: strings.Join(value[:], ""), 411 | } 412 | headers = append(headers, header) 413 | } 414 | res.Headers = headers 415 | 416 | body, _ := ioutil.ReadAll(parsedRes.Body) 417 | res.Body = string(body) 418 | 419 | return res 420 | } 421 | 422 | // ParseRequestFromServer parse request receive from API server 423 | func ParseRequestFromServer(record *libs.Record, req libs.Request, sign libs.Signature) { 424 | if req.Raw != "" { 425 | parsedReq := ParseBurpRequest(req.Raw) 426 | // check if parse request ok 427 | if parsedReq.Method != "ParseRequest" { 428 | record.Request = parsedReq 429 | } else { 430 | record.Request = record.OriginReq 431 | } 432 | } else { 433 | record.Request = record.OriginReq 434 | } 435 | 436 | // get some component from sign 437 | if req.Method != "" { 438 | record.Request.Method = req.Method 439 | } 440 | if req.Path != "" { 441 | record.Request.Path = req.Path 442 | } else { 443 | record.Request.Path = record.Request.URL 444 | } 445 | if req.Body != "" { 446 | record.Request.Body = req.Body 447 | } 448 | 449 | // header stuff 450 | if len(req.Headers) > 0 { 451 | realHeaders := req.Headers 452 | keys := []string{} 453 | for _, realHeader := range req.Headers { 454 | for key := range realHeader { 455 | keys = append(keys, key) 456 | } 457 | } 458 | for _, rawHeader := range record.Request.Headers { 459 | for key := range rawHeader { 460 | // only add header we didn't define 461 | if !funk.Contains(keys, key) { 462 | realHeaders = append(realHeaders, rawHeader) 463 | } 464 | } 465 | } 466 | record.Request.Headers = realHeaders 467 | } 468 | record.Request.Generators = req.Generators 469 | record.Request.Encoding = req.Encoding 470 | record.Request.Middlewares = req.Middlewares 471 | record.Request.Redirect = req.Redirect 472 | 473 | // resolve template 474 | target := ParseTarget(record.Request.URL) 475 | record.Request.URL = ResolveVariable(record.Request.Path, target) 476 | record.Request.Body = ResolveVariable(record.Request.Body, target) 477 | record.Request.Headers = ResolveHeader(record.Request.Headers, target) 478 | record.Request.Detections = ResolveDetection(req.Detections, target) 479 | } 480 | -------------------------------------------------------------------------------- /core/generator.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "path/filepath" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/Jeffail/gabs/v2" 11 | //"github.com/davecgh/go-spew/spew" 12 | "github.com/jaeles-project/jaeles/libs" 13 | "github.com/jaeles-project/jaeles/utils" 14 | "github.com/robertkrimen/otto" 15 | "github.com/thoas/go-funk" 16 | ) 17 | 18 | // Generators run multiple generator 19 | func Generators(req libs.Request, sign libs.Signature) []libs.Request { 20 | var reqs []libs.Request 21 | realPayloads := funk.UniqString(ParsePayloads(sign)) 22 | for _, payload := range realPayloads { 23 | fuzzReq := req 24 | // prepare something so we can access variable in generator string too 25 | fuzzReq.Target["payload"] = payload 26 | // set original to blank first 27 | fuzzReq.Target["original"] = "" 28 | fuzzReq.Detections = ResolveDetection(fuzzReq.Detections, fuzzReq.Target) 29 | fuzzReq.Generators = funk.UniqString(ResolveDetection(fuzzReq.Generators, fuzzReq.Target)) 30 | 31 | for _, genString := range fuzzReq.Generators { 32 | // just copy exactly request again 33 | if genString == "Null()" { 34 | reqs = append(reqs, fuzzReq) 35 | continue 36 | } 37 | if fuzzReq.Method == "" { 38 | fuzzReq.Method = "GET" 39 | } 40 | 41 | utils.DebugF("[Generator] %v", genString) 42 | injectedReqs := RunGenerator(fuzzReq, genString) 43 | if len(injectedReqs) <= 0 { 44 | utils.DebugF("No request generated by: %v", genString) 45 | continue 46 | } 47 | 48 | for _, injectedReq := range injectedReqs { 49 | // resolve detection this time because we may need parse something in the variable and original 50 | injectedReq.Detections = AltResolveDetection(fuzzReq.Detections, injectedReq.Target) 51 | injectedReq.Conclusions = AltResolveDetection(fuzzReq.Conclusions, injectedReq.Target) 52 | reqs = append(reqs, injectedReq) 53 | } 54 | } 55 | } 56 | 57 | return reqs 58 | } 59 | 60 | // RunGenerator is main function for generator 61 | func RunGenerator(req libs.Request, genString string) []libs.Request { 62 | var reqs []libs.Request 63 | vm := otto.New() 64 | 65 | vm.Set("Query", func(call otto.FunctionCall) otto.Value { 66 | var injectedReq []libs.Request 67 | if len(reqs) > 0 { 68 | for _, req := range reqs { 69 | injectedReq = Query(req, call.ArgumentList) 70 | } 71 | } else { 72 | injectedReq = Query(req, call.ArgumentList) 73 | } 74 | 75 | if len(injectedReq) > 0 { 76 | reqs = append(reqs, injectedReq...) 77 | } 78 | return otto.Value{} 79 | }) 80 | 81 | vm.Set("Body", func(call otto.FunctionCall) otto.Value { 82 | var injectedReq []libs.Request 83 | if len(reqs) > 0 { 84 | for _, req := range reqs { 85 | injectedReq = Body(req, call.ArgumentList) 86 | } 87 | } else { 88 | injectedReq = Body(req, call.ArgumentList) 89 | } 90 | 91 | if len(injectedReq) > 0 { 92 | reqs = append(reqs, injectedReq...) 93 | } 94 | return otto.Value{} 95 | }) 96 | 97 | vm.Set("Path", func(call otto.FunctionCall) otto.Value { 98 | var injectedReq []libs.Request 99 | if len(reqs) > 0 { 100 | for _, req := range reqs { 101 | injectedReq = Path(req, call.ArgumentList) 102 | } 103 | } else { 104 | injectedReq = Path(req, call.ArgumentList) 105 | } 106 | 107 | if len(injectedReq) > 0 { 108 | reqs = append(reqs, injectedReq...) 109 | } 110 | return otto.Value{} 111 | }) 112 | 113 | vm.Set("Header", func(call otto.FunctionCall) otto.Value { 114 | var injectedReq []libs.Request 115 | if len(reqs) > 0 { 116 | for _, req := range reqs { 117 | injectedReq = Header(req, call.ArgumentList) 118 | } 119 | } else { 120 | injectedReq = Header(req, call.ArgumentList) 121 | } 122 | 123 | if len(injectedReq) > 0 { 124 | reqs = append(reqs, injectedReq...) 125 | } 126 | return otto.Value{} 127 | }) 128 | 129 | vm.Set("Cookie", func(call otto.FunctionCall) otto.Value { 130 | var injectedReq []libs.Request 131 | if len(reqs) > 0 { 132 | for _, req := range reqs { 133 | injectedReq = Cookie(req, call.ArgumentList) 134 | reqs = append(reqs, injectedReq...) 135 | } 136 | } else { 137 | injectedReq = Cookie(req, call.ArgumentList) 138 | reqs = append(reqs, injectedReq...) 139 | } 140 | if len(injectedReq) > 0 { 141 | reqs = append(reqs, injectedReq...) 142 | } 143 | return otto.Value{} 144 | }) 145 | 146 | vm.Set("Method", func(call otto.FunctionCall) otto.Value { 147 | if len(reqs) > 0 { 148 | for _, req := range reqs { 149 | injectedReq := Method(req, call.ArgumentList) 150 | reqs = append(reqs, injectedReq...) 151 | } 152 | } else { 153 | injectedReq := Method(req, call.ArgumentList) 154 | reqs = append(reqs, injectedReq...) 155 | } 156 | return otto.Value{} 157 | }) 158 | 159 | vm.Run(genString) 160 | return reqs 161 | } 162 | 163 | // Encoder encoding part after resolve 164 | func Encoder(encodeString string, data string) string { 165 | if encodeString == "" { 166 | return data 167 | } 168 | var result string 169 | vm := otto.New() 170 | 171 | // Encode part 172 | vm.Set("URL", func(call otto.FunctionCall) otto.Value { 173 | result = url.QueryEscape(data) 174 | return otto.Value{} 175 | }) 176 | 177 | vm.Run(encodeString) 178 | return result 179 | } 180 | 181 | // Method gen request with multiple method 182 | func Method(req libs.Request, arguments []otto.Value) []libs.Request { 183 | methods := []string{"GET", "POST", "PUT", "HEAD", "PATCH"} 184 | if len(arguments) > 0 { 185 | methods = []string{strings.ToUpper(arguments[0].String())} 186 | } 187 | var reqs []libs.Request 188 | for _, method := range methods { 189 | injectedReq := req 190 | injectedReq.Method = method 191 | injectedReq.Target["original"] = req.Method 192 | reqs = append(reqs, injectedReq) 193 | } 194 | 195 | return reqs 196 | } 197 | 198 | // Query gen request with query string 199 | func Query(req libs.Request, arguments []otto.Value) []libs.Request { 200 | injectedString := arguments[0].String() 201 | paramName := "undefined" 202 | if len(arguments) > 1 { 203 | paramName = arguments[1].String() 204 | } 205 | 206 | var reqs []libs.Request 207 | rawURL := req.URL 208 | target := req.Target 209 | u, _ := url.Parse(rawURL) 210 | 211 | // replace one or create a new one if they're not exist 212 | if paramName != "undefined" { 213 | injectedReq := req 214 | uu, _ := url.Parse(injectedReq.URL) 215 | target["original"] = uu.Query().Get(paramName) 216 | // only replace value for now 217 | newValue := AltResolveVariable(injectedString, target) 218 | query := uu.Query() 219 | query.Set(paramName, newValue) 220 | uu.RawQuery = query.Encode() 221 | 222 | injectedReq.URL = uu.String() 223 | injectedReq.Target = target 224 | reqs = append(reqs, injectedReq) 225 | return reqs 226 | } 227 | 228 | for key, value := range u.Query() { 229 | injectedReq := req 230 | uu, _ := url.Parse(injectedReq.URL) 231 | if len(value) == 1 { 232 | target["original"] = strings.Join(value[:], "") 233 | } 234 | // only replace value for now 235 | newValue := AltResolveVariable(injectedString, target) 236 | 237 | query := uu.Query() 238 | query.Set(key, newValue) 239 | uu.RawQuery = query.Encode() 240 | 241 | injectedReq.URL = uu.String() 242 | injectedReq.Target = target 243 | reqs = append(reqs, injectedReq) 244 | } 245 | // return rawURL 246 | return reqs 247 | } 248 | 249 | // Body gen request with body 250 | func Body(req libs.Request, arguments []otto.Value) []libs.Request { 251 | injectedString := arguments[0].String() 252 | paramName := "undefined" 253 | if len(arguments) > 1 { 254 | paramName = arguments[1].String() 255 | } 256 | 257 | var reqs []libs.Request 258 | target := req.Target 259 | 260 | rawBody := req.Body 261 | // @TODO: deal with XML body later 262 | // @TODO: deal with multipart form later 263 | if paramName == "undefined" { 264 | // var newBody []string 265 | if rawBody != "" { 266 | // @TODO: inject for all child node, only 3 depth for now 267 | if utils.IsJSON(rawBody) { 268 | jsonParsed, _ := gabs.ParseJSON([]byte(rawBody)) 269 | for key, child := range jsonParsed.ChildrenMap() { 270 | injectedReq := req 271 | if len(child.Children()) == 0 { 272 | str := fmt.Sprint(child) 273 | target["original"] = str 274 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 275 | jsonBody, _ := gabs.ParseJSON([]byte(rawBody)) 276 | jsonBody.Set(newValue, key) 277 | injectedReq.Body = jsonBody.String() 278 | injectedReq.Target = target 279 | reqs = append(reqs, injectedReq) 280 | 281 | } else { 282 | // depth 2 283 | for _, ch := range child.Children() { 284 | if len(ch.Children()) == 0 { 285 | str := fmt.Sprint(child) 286 | target["original"] = str 287 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 288 | jsonBody, _ := gabs.ParseJSON([]byte(rawBody)) 289 | jsonBody.Set(newValue, key) 290 | injectedReq.Body = jsonBody.String() 291 | injectedReq.Target = target 292 | reqs = append(reqs, injectedReq) 293 | } else { 294 | // depth 3 295 | for _, ch := range child.Children() { 296 | if len(ch.Children()) == 0 { 297 | str := fmt.Sprint(child) 298 | target["original"] = str 299 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 300 | jsonBody, _ := gabs.ParseJSON([]byte(rawBody)) 301 | jsonBody.Set(newValue, key) 302 | injectedReq.Body = jsonBody.String() 303 | injectedReq.Target = target 304 | reqs = append(reqs, injectedReq) 305 | } 306 | } 307 | } 308 | } 309 | } 310 | // dd, ok := nn[1].Data().(int) 311 | } 312 | 313 | } else { 314 | // normal form body 315 | params := strings.SplitN(rawBody, "&", -1) 316 | for index, param := range params { 317 | newParams := strings.SplitN(rawBody, "&", -1) 318 | injectedReq := req 319 | k := strings.SplitN(param, "=", -1) 320 | if len(k) > 1 { 321 | target["original"] = k[1] 322 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 323 | newParams[index] = fmt.Sprintf("%v=%v", k[0], newValue) 324 | injectedReq.Body = strings.Join(newParams[:], "&") 325 | injectedReq.Target = target 326 | reqs = append(reqs, injectedReq) 327 | } else if len(k) == 1 { 328 | target["original"] = k[0] 329 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 330 | newParams[index] = fmt.Sprintf("%v=%v", k[0], newValue) 331 | injectedReq.Body = strings.Join(newParams[:], "&") 332 | injectedReq.Target = target 333 | reqs = append(reqs, injectedReq) 334 | } 335 | } 336 | 337 | } 338 | 339 | } 340 | } 341 | // return rawURL 342 | return reqs 343 | } 344 | 345 | // Path gen request with path 346 | func Path(req libs.Request, arguments []otto.Value) []libs.Request { 347 | injectedString := arguments[0].String() 348 | paramName := "last" 349 | if len(arguments) > 1 { 350 | paramName = arguments[1].String() 351 | } 352 | 353 | var reqs []libs.Request 354 | target := req.Target 355 | 356 | u, _ := url.Parse(req.URL) 357 | rawPath := u.Path 358 | rawQuery := u.RawQuery 359 | Paths := strings.Split(rawPath, "/") 360 | ext := filepath.Ext(Paths[len(Paths)-1]) 361 | 362 | // only replace extension file 363 | if paramName == "ext" && ext != "" { 364 | injectedReq := req 365 | target["original"] = filepath.Ext(Paths[len(Paths)-1]) 366 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 367 | newPaths := Paths 368 | newPaths[len(newPaths)-1] = strings.Replace(Paths[len(Paths)-1], target["original"], newValue, -1) 369 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") 370 | injectedReq.Target = target 371 | reqs = append(reqs, injectedReq) 372 | // only replace the last path 373 | } else if paramName == "last" || (paramName == "-1" && ext == "") { 374 | injectedReq := req 375 | target["original"] = Paths[len(Paths)-1] 376 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 377 | 378 | newPaths := Paths 379 | // if the path have query before append with it 380 | newPaths[len(newPaths)-1] = newValue 381 | if rawQuery != "" { 382 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") 383 | if strings.Contains(injectedReq.URL, "?") { 384 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") + "&" + rawQuery 385 | } else { 386 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") + "?" + rawQuery 387 | } 388 | 389 | // newPaths[len(newPaths)-1] = newValue + "&" + rawQuery 390 | } else { 391 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") 392 | } 393 | injectedReq.Target = target 394 | reqs = append(reqs, injectedReq) 395 | // specific position 396 | } else if paramName != "*" && len(paramName) == 1 { 397 | position, err := strconv.ParseInt(paramName, 10, 64) 398 | // select reverse 399 | if strings.HasPrefix(paramName, "-") { 400 | v := int(position) * -1 401 | if len(Paths) > v { 402 | position = int64(len(Paths) - v) 403 | } 404 | } 405 | 406 | if err == nil { 407 | 408 | injectedReq := req 409 | target["original"] = Paths[position] 410 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 411 | 412 | newPaths := Paths 413 | newPaths[position] = newValue 414 | 415 | injectedReq.URL = target["BaseURL"] + strings.Join(newPaths[:], "/") 416 | injectedReq.Target = target 417 | reqs = append(reqs, injectedReq) 418 | } 419 | } else if paramName == "*" || strings.Contains(paramName, ",") { 420 | // select path 421 | var injectPositions []int 422 | if strings.Contains(paramName, ",") { 423 | positions := strings.Split(paramName, ",") 424 | for _, pos := range positions { 425 | index, err := strconv.Atoi(strings.TrimSpace(pos)) 426 | if err == nil { 427 | injectPositions = append(injectPositions, index) 428 | } 429 | } 430 | } else { 431 | // all paths 432 | for index := range Paths { 433 | injectPositions = append(injectPositions, index) 434 | } 435 | } 436 | 437 | for _, injectPos := range injectPositions { 438 | Paths := strings.Split(rawPath, "/") 439 | 440 | injectedReq := req 441 | target["original"] = Paths[injectPos] 442 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 443 | 444 | newPaths := Paths 445 | newPaths[injectPos] = newValue 446 | reallyNewPaths := strings.Join(newPaths[:], "/") 447 | //// in case we miss the first / 448 | //if !strings.HasPrefix(reallyNewPaths, "/") { 449 | // reallyNewPaths = "/" + reallyNewPaths 450 | //} 451 | injectedReq.URL = target["BaseURL"] + reallyNewPaths 452 | injectedReq.Target = target 453 | reqs = append(reqs, injectedReq) 454 | } 455 | 456 | } 457 | return reqs 458 | } 459 | 460 | // Cookie gen request with Cookie 461 | func Cookie(req libs.Request, arguments []otto.Value) []libs.Request { 462 | var reqs []libs.Request 463 | injectedString := arguments[0].String() 464 | cookieName := "undefined" 465 | if len(arguments) > 1 { 466 | cookieName = arguments[1].String() 467 | } 468 | 469 | target := req.Target 470 | 471 | var haveCookie bool 472 | var cookieExist bool 473 | var originalCookies string 474 | originCookies := make(map[string]string) 475 | // check if request have cookie or not 476 | for _, header := range req.Headers { 477 | haveCookie = funk.Contains(header, "Cookie") 478 | if haveCookie == true { 479 | // got a cookie 480 | for _, v := range header { 481 | originalCookies = v 482 | rawCookies := strings.Split(v, ";") 483 | for _, rawCookie := range rawCookies { 484 | 485 | name := strings.Split(strings.TrimSpace(rawCookie), "=")[0] 486 | // just in case some weird part after '=' 487 | value := strings.Join(strings.Split(strings.TrimSpace(rawCookie), "=")[1:], "") 488 | originCookies[name] = value 489 | } 490 | } 491 | break 492 | } else { 493 | haveCookie = false 494 | } 495 | 496 | } 497 | if haveCookie == true && funk.Contains(originCookies, cookieName) { 498 | cookieExist = true 499 | } 500 | 501 | // start gen request 502 | if haveCookie == true { 503 | // replace entire old cookie if you don't define cookie name 504 | if cookieName == "undefined" { 505 | newHeaders := req.Headers 506 | target["original"] = originalCookies 507 | newCookie := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 508 | 509 | for _, header := range req.Headers { 510 | for k := range header { 511 | if k == "Cookie" { 512 | head := map[string]string{ 513 | "Cookie": newCookie, 514 | } 515 | newHeaders = append(newHeaders, head) 516 | } else { 517 | newHeaders = append(newHeaders, header) 518 | } 519 | 520 | } 521 | } 522 | injectedReq := req 523 | injectedReq.Headers = newHeaders 524 | injectedReq.Target = target 525 | reqs = append(reqs, injectedReq) 526 | return reqs 527 | } 528 | 529 | var newHeaders []map[string]string 530 | // replace old header 531 | for _, header := range req.Headers { 532 | for k := range header { 533 | // do things with Cookie header 534 | if k == "Cookie" { 535 | if cookieExist == true { 536 | target["original"] = originCookies[cookieName] 537 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 538 | originCookies[cookieName] = newValue 539 | 540 | } else { 541 | target["original"] = "" 542 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 543 | originCookies[cookieName] = newValue 544 | } 545 | 546 | // join it again to append to the rest of header 547 | var realCookies string 548 | for name, value := range originCookies { 549 | realCookies += fmt.Sprintf("%v=%v; ", name, value) 550 | } 551 | newHead := map[string]string{ 552 | "Cookie": realCookies, 553 | } 554 | 555 | // replace cookie 556 | newHeaders = append(newHeaders, newHead) 557 | } else { 558 | newHeaders = append(newHeaders, header) 559 | } 560 | } 561 | } 562 | injectedReq := req 563 | injectedReq.Headers = newHeaders 564 | injectedReq.Target = target 565 | reqs = append(reqs, injectedReq) 566 | 567 | } else { 568 | target["original"] = "" 569 | var realCookies string 570 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 571 | if cookieName == "undefined" { 572 | realCookies = fmt.Sprintf("%v; ", newValue) 573 | 574 | } else { 575 | realCookies = fmt.Sprintf("%v=%v; ", cookieName, newValue) 576 | } 577 | head := map[string]string{ 578 | "Cookie": realCookies, 579 | } 580 | injectedReq := req 581 | newHeaders := req.Headers 582 | newHeaders = append(newHeaders, head) 583 | injectedReq.Headers = newHeaders 584 | injectedReq.Target = target 585 | reqs = append(reqs, injectedReq) 586 | } 587 | 588 | return reqs 589 | } 590 | 591 | // Header gen request with header 592 | func Header(req libs.Request, arguments []otto.Value) []libs.Request { 593 | var reqs []libs.Request 594 | injectedString := arguments[0].String() 595 | headerName := arguments[1].String() 596 | 597 | target := req.Target 598 | 599 | injectedReq := req 600 | var isExistHeader bool 601 | // check if inject header is new or not 602 | for _, header := range req.Headers { 603 | isExistHeader = funk.Contains(header, headerName) 604 | if isExistHeader == true { 605 | break 606 | } else { 607 | isExistHeader = false 608 | } 609 | } 610 | if isExistHeader == false { 611 | newHeaders := req.Headers 612 | target["original"] = "" 613 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 614 | head := map[string]string{ 615 | headerName: newValue, 616 | } 617 | newHeaders = append(newHeaders, head) 618 | injectedReq.Headers = newHeaders 619 | injectedReq.Target = target 620 | reqs = append(reqs, injectedReq) 621 | } else { 622 | var newHeaders []map[string]string 623 | // replace old header 624 | for _, header := range req.Headers { 625 | for k, v := range header { 626 | if k == headerName { 627 | target["original"] = v 628 | newValue := Encoder(req.Encoding, AltResolveVariable(injectedString, target)) 629 | newHead := map[string]string{ 630 | headerName: newValue, 631 | } 632 | newHeaders = append(newHeaders, newHead) 633 | } else { 634 | newHeaders = append(newHeaders, header) 635 | } 636 | } 637 | } 638 | injectedReq.Target = target 639 | injectedReq.Headers = newHeaders 640 | reqs = append(reqs, injectedReq) 641 | } 642 | 643 | return reqs 644 | } 645 | --------------------------------------------------------------------------------