Yakit单兵作战武器文档速查
最后更新时间:
写在前面
记录Yakit这个工具的使用学习以及Yakit语法脚本exp的编写,黑盒开搞!
Yak中的常见库使用教程
字符串工具库 str
渗透测试可能需要使用的函数
判断一个字符串密码强弱
1
2
3
4println(str.IsStrongPassword("abcdefghijk")) // false
println(str.IsStrongPassword("abc#52G")) // false
println(str.IsStrongPassword("abcdefgh.G1ijk")) // true
println(str.IsStrongPassword("abcdefghij.$t2Tk")) // true把域名或者IP+端口拼成一个网络地址
1
2
3println(str.HostPort("192.168.1.1", 80))
println(str.HostPort("192.168.1.1", "80"))
println(str.HostPort("example.com", 80))让IPV4退化成一个C段地址
1
printTwoResult(str.IPv4ToCClassNetwork("example.com"))
判断一个字符串是不是IPV4
1
println(str.IsIPv4("127.0.0.1"))
判断一个字符串是不是IPV6
1
println(str.IsIPv6("127.0.0.1"))
把字符串(URL/Addr)中的主机和端口解析出来
1
2
3
4
5
6
7
8
9
10
11
12
13printHostPortErr(str.ParseStringToHostPort("example.com:80"))
printHostPortErr(str.ParseStringToHostPort("127.0.0.1:80"))
printHostPortErr(str.ParseStringToHostPort("https://example.com"))
printHostPortErr(str.ParseStringToHostPort("http://example.com"))
printHostPortErr(str.ParseStringToHostPort("http://example.com:8082"))
printHostPortErr(str.ParseStringToHostPort("example.com"))
printHostPortErr(str.ParseStringToHostPort("example"))
printHostPortErr(str.ParseStringToHostPort("127.0.0.1"))
/*
OUTPUT:
Host: example.com Port: 80把以
,
分割的字符串解析成主机信息,可解析内容为网段、IP、域名等。一般用于解析扫描目标主机。1
2
3
4println(str.ParseStringToHosts("baidu.com,127.0.0.1,192.168.1.2/28"))
// OUTPUT
// [baidu.com 127.0.0.1 192.168.1.0 192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4 192.168.1.5 192.168.1.6 192.168.1.7 192.168.1.8 192.168.1.9 192.168.1.10 192.168.1.11 192.168.1.12 192.168.1.13 192.168.1.14 192.168.1.15]把字符串按行来分隔
1
dump(str.ParseStringToLines("123123123\nasdfasdf\nwfhiuqwe\nasdfasdf\r\nasdfasdf"))
把字符串(端口和端口的集合例如
22,3306,8080-8088
)解析成单独端口1
2
3
4
5
6
7
8
9println(str.ParseStringToPorts("22,3306,80-82,8080-8083"))
println(str.ParseStringToPorts("8080-8083"))
println(str.ParseStringToPorts("22,xx"))
println(str.ParseStringToPorts("127.0.0.1/28"))
/*
OUTPUT:
[22 80 81 82 3306 8080 8081 8082 8083]把字符串(网络地址)转变为可能的标准格式的Url
1
2
3
4
5
6
7
8
9
10
11
12println(str.ParseStringToUrls("www.baidu.com"))
println(str.ParseStringToUrls("example.com"))
println(str.ParseStringToUrls("example.com:80"))
println(str.ParseStringToUrls("sdfaasdgasd"))
println(str.ParseStringToUrls("127.0.1.1"))
println(str.ParseStringToUrls("192.168.1.3:443"))
println(str.ParseStringToUrls("192.168.1.3:80"))
/*
OUTPUT:
[https://www.baidu.com http://www.baidu.com]把字符串(网络地址)转变为可能的标准格式的Url(可能以
www
作为域名开头)1
2
3
4
5
6println(str.ParseStringToUrlsWith3W("example.com"))
println(str.ParseStringToUrlsWith3W("example.com:80"))
println(str.ParseStringToUrlsWith3W("sdfaasdgasd"))
[https://example.com https://www.example.com http://example.com http://www.example.com]
[http://example.com http://www.example.com]
[https://sdfaasdgasd https://www.sdfaasdgasd http://sdfaasdgasd http://www.sdfaasdgasd]随机生成一个指定长度的随机字符串,可作为密码
1
fn str.RandSecret(var_1: int): string
随机生成一个指定长度字符串
1
fn str.RandStr(var_1: int): string
编码解码库 codec
不可见字符打印编码(ASCII)
Base64编码解码
HTML实体编码
Url 编码
只编码需要编码的字符/字符串?
双URL编码,常用于XSS
十六进制编码
如果这个十六进制字符串需要在 mysql 中展示,记得加
0x
前缀AES/DES加密编解码
Hash计算与编码
文件操作与IO库 file
https://www.yaklang.io/docs/buildinlibs/lib_file
exp
创建文件
1
2
3
4
5
6
7
8
9
10
11
12// file.OpenFile(var1: string, mode: int, perm: int)
// 创建简单的文件
f, err := file.OpenFile("_testFile.txt", file.O_CREATE|file.O_RDWR, 0777)
die(err)
f.Write("we write some Message \n\n\n\n\nasdfahsdfasdf Now\n")
f.Close()
// 像 cat 一样,把文件打印出来到屏幕上
file.Cat("_testFile.txt")
// 移除文件
file.Rm("_testFile.txt")创建临时文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// file.TempFile(dir: string)
// 创建一个临时文件
f, err := file.TempFile("")
die(err)
println("我们创建了一个临时文件:", f.Name())
// 往临时文件中写一个字符串
println("写入一点随机字符串")
f.Write("asdfasdf")
// 记得脚本结束要关闭文件
defer f.Close()
// 我们验证我们的内容写成功了没?
raw, err := file.ReadFile(f.Name())
die(err)
println("我们写入的文件内容为:")
// 展示写的结果
dump(raw)文件删除
1
2
3
4// file.Remove 或者 file.Rm
fileName = "target-filename.txt"
file.Remove(fileName)
file.Rm(fileName)文件移动
1
2file.Rename(oldName, newName string)
file.Mv(oldName: string, newName)文件按行读写
1
2
3
4f, err := file.Open("12_fileio.yak")
die(err)
dump(f.ReadLines())
f.Close()
系统读写工具库 io
正则工具库 re
re.Match
检查字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 我们构建一个 match
pattern := `matchThis(.*?)txt`
// 我们随便写一个字符串
result := re.Match(pattern, `
asdfas sdfa sdfa
dsfasdfk;iopu34
matchMatchMatchmatchThisasdfnkaopjryqeryjklijklojkloptxt
`)
if !result {
die("failed to match re:")
}
printf("pattern: %v 匹配成功\n", pattern)
/*
OUTPUT:
pattern: matchThis(.*?)txt 匹配成功
*/检查字节流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16pattern := `matchThis(.*?)txt`
result := re.Match(pattern, []byte(`
asdfas sdfa sdfa
dsfasdfk;iopu34
matchMatchMatchmatchThisasdfnkaopjryqeryjklijklojkloptxt
`))
if !result {
die("failed to match re:")
}
printf("pattern: %v 匹配成功\n", pattern)
/*
OUTPUT:
pattern: matchThis(.*?)txt 匹配成功
*/
操作系统库 os
TCP 网络连接库 tcp
exp
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// 设置日志的级别,方便我们看到 TCP 库的一些信息输出
loglevel("info")
// 启动一个 TCP 服务器,在 Goroutine 中异步启动
go fn{
tcp.Serve("127.0.0.1", 8085, tcp.serverCallback(func(conn) {
println("真正的 TCP 服务器收到一个连接:", conn.RemoteAddr())
bytes, err := conn.RecvLen(4)
if err != nil {
conn.Close()
return
}
println("收到连接的前 4 个字符为", string(bytes))
println("发送一个 Hello World 给客户端")
conn.Send("你好,世界!Hello World from 127.0.0.1:8085")
println("发送成功了!")
conn.Close()
}))
}
// 启动一个 TCP 本地转发,把本地 9000 转发到 127.0.0.1:8085
go fn{
tcp.Forward(9000, "127.0.0.1", 8085)
}
// 启动一个 TCP 连接,直接访问本地 9000 端口
conn, err := tcp.Connect("127.0.0.1", 9000)
die(err)
// 发送一个消息给 9000 端口
conn.Send("abdasdf this message from client")
dump(conn.RecvStringTimeout(1))
// 等待3秒观察日志
sleep(3)模糊测试工具库 fuzz
模糊测试字符串
fn fuzz.Strings(origin: string) []string
这个函数会把核心的
{{ }}
标签转变为需要渲染的内容exp
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
26origin := "{{int(1,3,4,80-88)}}"
res := fuzz.Strings(origin)
println("需要模糊渲染的字符串为:", origin)
println("渲染结果为:")
dump(res)
/*
OUTPUT:
需要模糊渲染的字符串为: {{int(1,3,4,80-88)}}
渲染结果为:
([]string) (len=12 cap=12) {
(string) (len=1) "1",
(string) (len=1) "3",
(string) (len=1) "4",
(string) (len=2) "80",
(string) (len=2) "81",
(string) (len=2) "82",
(string) (len=2) "83",
(string) (len=2) "84",
(string) (len=2) "85",
(string) (len=2) "86",
(string) (len=2) "87",
(string) (len=2) "88"
}
*/fuzz 标签定义以及使用
在同一个渲染的字符串中,完全相同的标签不会做排列组合,而是被渲染成相同的元素。如果真的需要标签渲染内容完全相同,并且需要分别渲染,可以在标签函数功能后增加一个数字,例如
{{int1()}}
,{{int2()}}
这两个标签同{{int()}}
完全等效,但是可以分别渲染。基础标签
{{int}}
渲染整数/端口最常用的用户其实是渲染一个端口,端口组;如果我们想要爆破密码的时候,生成密码也可以使用这个标签。
{{net}}/{{host}}
渲染扫描目标本质上和
str.ParseStringToHosts
一样我们会把目标拆分成多个主机,支持网段,域名,IP 等。解析结果都是以
,
为分割的。经典配合
{{int}}
使用,批量生成URL1
2
3
4
5origin := "http://{{net(176.1.0.1/28,example.com)}}:{{port(8080)}}/admin.php"
res := fuzz.Strings(origin)
println("需要模糊渲染的字符串为:", origin)
println("渲染结果为:")
dump(res){{randstr}}
生成随机字符串生成一个随机字符串,只包含二十六个英文字母大小写
{{randint}}
生成随机数字{{char}}
指定生成单个字符1
2
3for _, r := range fuzz.Strings("生成一字符为:{{char(a-f)}}") {
println(r)
}{{punctuation}} / {{punc}}
Fuzz 所有可见标点符号将会被替换为
[< > ? , . / : " ; ' { } [ ] | \ _ + - = ) ( * & ^ % $ # @ !
]`{{rangechar}}
制定生成任意字符有时还需要生成一些不可见字符
【编码标签】
{{md5}} {{sha1}} {{sha256}} {{sha512}}
支持嵌套
【编码标签】
{{base64}} {{hex}} {{url}} {{durl}} {{html}} {{htmlhex}}
Fuzz Http 请求
通过
fuzz.HTTPRequest
这个接口可以创建一个 HTTP 请求,这个请求可以支持 Fuzz 相关操作。1
func fuzz.HTTPRequest(params: *http.Request|[]byte|string) (*FuzzHTTPRequest, error)
exp
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/*
构筑 FuzzHTTPRequest
*/
// 我们构建一个基础数据包,这个数据包是标准 http 请求构建的
req, err := http.NewRequest("GET", "http://127.0.0.1:8080")
die(err)
// 我们自定义个数据包的具体内容,虽然他不标准,但是仍然可以被解析和使用,这点非常棒
reqBody := `GET / HTTP/1.1
Host: 127.0.0.1:8082`
for _, params := range [
[]byte(reqBody), // 测试 []byte / []uint8 类型的数据包是否能被使用
reqBody, // 测试 string 数据包是否能被使用
req, // 测试 *http.Request 是否能被正常解析
] {
req, err := fuzz.HTTPRequest(params)
if err != nil {
/* 如果解析失败,将会直接在这里打印出失败的原因和原来参数 */
println("参数错误: ")
dump(params)
die(err)
} else {
println(str.f("接收 %v 数据包成功", type(params)))
}
}那么如何对一大批url进行批量测试?
利用
fuzz.UrlsToHTTPRequests
单个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// fuzz.UrlsToHTTPRequests 案例1
reqGroup, err = fuzz.UrlsToHTTPRequests("http://127.0.0.1:8082")
die(err)
// 获取 Fuzz 的结果
reqs, err := reqGroup.Results()
die(err)
// 展示构造后的数据包
for _, req := range reqs {
http.show(req)
}
/*
OUTPUT
GET / HTTP/1.1
Host: 127.0.0.1:8082
Content-Length: 0
*/多个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15println("----------")
// fuzz.UrlsToHTTPRequests 案例
urls = `http://127.0.0.1:8082
http://127.0.0.1:8083
http://127.0.0.1:8084
http://127.0.0.1:8085
`
reqGroup, err = fuzz.UrlsToHTTPRequests(str.ParseStringToLines(urls)...)
die(err)
reqs, err := reqGroup.Results()
die(err)
for _, req := range reqs {
http.show(req)
}优化版本
1
2
3
4
5
6
7
8
9
10
11
12
13println("----------")
// fuzz.UrlsToHTTPRequests 案例
urls = `http://127.0.0.1:808{{int(5-8)}}
http://127.0.0.1:8082`
reqGroup, err = fuzz.UrlsToHTTPRequests(str.ParseStringToLines(urls)...)
die(err)
reqs, err := reqGroup.Results()
die(err)
for _, req := range reqs {
http.show(req)
}链式调用 Fuzz一个HTTPRequest
批量发起Fuzz过的请求
httpool.Pool(i *FuzzHTTPRequest|[]*http.Request, opts...) (chan map[string]interface{}, error)
exp
1
2
3
4
5
6
7
8
9
10
11
12
13// 批量发起请求
req, err := fuzz.HTTPRequest(`GET /path-target HTTP/1.1
Host: 127.0.0.1:8082`)
die(err)
fReq := req.FuzzPath("/admin/admin{{int(1-4)}}.php")
ch, err := httpool.Pool(fReq)
die(err)
for result := range ch {
dump(result)
println("-------------------------------------")
}
HTTP库 http
API 如下 https://www.yaklang.io/docs/buildinlibs/lib_http#%E6%89%80%E6%9C%89-api
从命令行读参数 cli
cli.Args()
处理不同类型的参数值
cli.String() / cli.Bool() / cli.Int
servicescan 服务指纹扫描
网络空间引擎 spaceengine
配合servicescan 使用
1
2
3
4
5
6
7
8
9
10
11apiKey := cli.String("token")
res, err := spacengine.ShodanQuery(apiKey, "jenkins", spacengine.maxRecord(100))
die(err)
fpRes, err := servicescan.ScanFromSpaceEngine(res)
die(err)
for result := range fpRes {
println(result)
}子域名收集
1
2
3
4
5
6res, err := subdomain.Scan("b******u.com" , subdomain.recursive(true ) )
die(err)
for result := range res {
result.Show()
}赋予Yak漏扫能力
nuclei.Scan