Go渗透测试学习笔记(七)–滥用数据库和文件系统 0x00 前言 在本节,我们学习如何安装和配置各种SQL
和NOSQL
数据库,并学习如何通过Go
来进行交互。
0x01 与Mongo交互 1. 安装MongoDB
数据库然后写入数据 MongoDB是NOSQL
数据库,这意味着,他与其他传统数据库不同,他是无架构的。
使用docker开启了之后,使用store
数据库
db.use strore
然后写入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 db.transactions.insert([ { "ccnum" : "4444333322221111" , "date" : "2019-01-05" , "amount" : 100.12 , "cvv" : "1234" , "exp" : "09/2020" }, { "ccnum" : "4444123456789012" , "date" : "2019-01-07" , "amount" : 2400.18 , "cvv" : "5544" , "exp" : "02/2021" }, { "ccnum" : "4465122334455667" , "date" : "2019-01-29" , "amount" : 1450.87 , "cvv" : "9876" , "exp" : "06/2020" } ]);
2. 下载包并写入代码 需要先下载包,因为官方没有与NoSQL
直接交互的包,
所以我们安装如下mongo的驱动:go get gopkg.in/mgo.v2
然后写入以下代码
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 package mainimport ( "fmt" "log" mgo "gopkg.in/mgo.v2" ) type Transaction struct { CCNum string `bson:"ccnum"` Date string `bson:"date"` Amount float32 `bson:"amount"` Cvv string `bson:"cvv"` Expiration string `bson:"exp"` } func main () { session, err := mgo.Dial("192.168.68.137" ) if err != nil { log.Panicln(err) } defer session.Close() results := make ([]Transaction, 0 ) if err := session.DB("store" ).C("transactions" ).Find(nil ).All(&results); err != nil { log.Panicln(err) } for _, txn := range results { fmt.Println(txn.CCNum, txn.Date, txn.Amount, txn.Cvv, txn.Expiration) } }
0x02与mysq交互 1.创建mysql 数据库,并且写入数据 1 2 3 4 5 6 7 mysql> create database store; mysql> create table transactions( -> ccnum varchar(32), -> date date, -> amount decimal(7,2), -> cvv char(4), -> exp date);
1 2 3 insert into transactions(ccnum, date , amount, cvv, exp) values ('4444333322221111' , '2019-01-05' , 100.12 , '1234' , '2020-09-01' );insert into transactions(ccnum, date , amount, cvv, exp) values ('4444123456789012' , '2019-01-07' , 2400.18 , '5544' , '2021-02-01' );insert into transactions(ccnum, date , amount, cvv, exp) values ('4465122334455667' , '2019-01-29' , 1450.87 , '9876' , '2020-06-01' );
2. 编写代码 Go
包包含了一个database/sql
的标准包
go get github.com/go-sql-driver/mysql
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 package mainimport ( "database/sql" "fmt" "log" _ "github.com/go-sql-driver/mysql" ) func main () { db,err := sql.Open("mysql" ,"root:root@tcp(127.0.0.1:3306)/store" ) if err != nil { log.Panicln(err) } defer db.Close() var ( ccnum,date,cvv,exp string amount float32 ) rows,err := db.Query("SELECT ccnum,date,amount,cvv,exp FROM transactions " ) if err != nil { log.Panicln(err) } defer rows.Close() for rows.Next() { err := rows.Scan(&ccnum,&date,&amount,&cvv,&exp) if err != nil { log.Panicln(err) } fmt.Println(ccnum,date,cvv,exp) } if rows.Err() != nil { log.Panicln(err) } }
这里需要引入匿名包,否则加载不了数据库的驱动
0x03 构建数据库矿工 在本节中,我们将创建一个工具来检查数据库模式(例如列明),以确定其中的数据是否值得窃取。
1. 首先实现一个接口 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 import ( "fmt" "regexp" ) type DatabaseMiner interface { GetSchema() (*Schema, error ) } type Schema struct { Databases []Database } type Database struct { Name string Tables []Table } type Table struct { Name string Columns []string } func Search (m DatabaseMiner) error { s, err := m.GetSchema() if err != nil { return err } re := getRegex() for _, database := range s.Databases { for _, table := range database.Tables { for _, field := range table.Columns { for _, r := range re { if r.MatchString(field) { fmt.Println(database) fmt.Printf("[+] HIT: %s\n" , field) } } } } } return nil } func getRegex () []*regexp.Regexp { return []*regexp.Regexp{ regexp.MustCompile(`(?i)social` ), regexp.MustCompile(`(?i)ssn` ), regexp.MustCompile(`(?i)pass(word)?` ), regexp.MustCompile(`(?i)hash` ), regexp.MustCompile(`(?i)ccnum` ), regexp.MustCompile(`(?i)card` ), regexp.MustCompile(`(?i)security` ), regexp.MustCompile(`(?i)key` ), } } func (s Schema) String() string { var ret string for _, database := range s.Databases { ret += fmt.Sprint(database.String() + "\n" ) } return ret } func (d Database) String() string { ret := fmt.Sprintf("[DB] = %+s\n" , d.Name) for _, table := range d.Tables { ret += table.String() } return ret } func (t Table) String() string { ret := fmt.Sprintf(" [TABLE] = %+s\n" , t.Name) for _, field := range t.Columns { ret += fmt.Sprintf(" [COL] = %+s\n" , field) } return ret }
该代码首先定义了一个名为DatabaseMiner
的接口,实现接口的任何数据类型都需要一个名为GetSchema
的方法。每个后端数据库都可能有特定的逻辑来检索数据库模式。
定义一个Schema
类型,该类型由也在此处定义的一些子类组成。我们使用Schema
类型在逻辑上表示数据库架构,即数据库,表和列。接口中定义的函数GetSchema()
希望返回一个 * Schema(Schema类型的指针)
现在定义一个名为Search()
的函数,函数Search()
希望在函数调用期间将DatabaseMiner
实例传递给它。它将miner的值储存在一个名为m
的变量中,然后使用m.GetSchema()
来检索。然后循环遍历整个模式。再根据正则表达式(regex)值列表搜索匹配到的列明。如果能找到,则将数据库模式和匹配字段打印到屏幕上。
最后,定义一个名为getRegex
的函数,此函数使用Go
的regex
包预编译正则表达式,并返回这些值的一部分。
2. 开始实现 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 package mainimport ( "database/sql" "fmt" "log" "os" "github.com/blackhat-go/bhg/ch-7/db/dbminer" _ "github.com/go-sql-driver/mysql" ) type MySQLMiner struct { Host string Db sql.DB } func New (host string ) (*MySQLMiner, error ) { m := MySQLMiner{Host: host} err := m.connect() if err != nil { return nil , err } return &m, nil } func (m *MySQLMiner) connect() error { db, err := sql.Open("mysql" , fmt.Sprintf("root:root@tcp(%s:3306)/information_schema" , m.Host)) if err != nil { log.Panicln(err) } m.Db = *db return nil } func (m *MySQLMiner) GetSchema() (*dbminer.Schema, error ) { var s = new (dbminer.Schema) sql := `SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME FROM columns WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') ORDER BY TABLE_SCHEMA, TABLE_NAME` schemarows, err := m.Db.Query(sql) if err != nil { return nil , err } defer schemarows.Close() var prevschema, prevtable string var db dbminer.Database var table dbminer.Table for schemarows.Next() { var currschema, currtable, currcol string if err := schemarows.Scan(&currschema, &currtable, &currcol); err != nil { return nil , err } if currschema != prevschema { if prevschema != "" { db.Tables = append (db.Tables, table) s.Databases = append (s.Databases, db) } db = dbminer.Database{Name: currschema, Tables: []dbminer.Table{}} prevschema = currschema prevtable = "" } if currtable != prevtable { if prevtable != "" { db.Tables = append (db.Tables, table) } table = dbminer.Table{Name: currtable, Columns: []string {}} prevtable = currtable } table.Columns = append (table.Columns, currcol) } db.Tables = append (db.Tables, table) s.Databases = append (s.Databases, db) if err := schemarows.Err(); err != nil { return nil , err } return s, nil } func main () { mm, err := New(os.Args[1 ]) if err != nil { panic (err) } defer mm.Db.Close() if err := dbminer.Search(mm); err != nil { panic (err) } }
0x04 文件掠夺系统 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 package mainimport ( "fmt" "log" "os" "path/filepath" "regexp" ) var regexes = []*regexp.Regexp{ regexp.MustCompile(`(?i)user`), regexp.MustCompile(`(?i)password`), regexp.MustCompile(`(?i)kdb`), regexp.MustCompile(`(?i)login`), } func walkFn (path string, f os.FileInfo, err error) error { for _, r := range regexes { if r.MatchString(path) { fmt.Printf("[+] HIT: %s\n" , path) } } return nil } func main () { root := os.Args[1 ] if err := filepath.Walk(root, walkFn); err != nil { log.Panicln(err) } }