在实际渗透中,我一直坚信一个思想,如果目标能够渗透进去,而我却没有成功,一定是信息收集的时候,没有收集到关键点。
本文只是一个引子,希望各位师傅们可以有所收获
我们平常少不了的收集手段是端口和目录。而端口我们可以用fofa或者nmap等这些已经灰常好的工具,让我们进行探测。
本文用于一个demo测试,表哥们可以自行改编
本文demo包括:
命令行下工具以及GUI工具,最后介绍一下B/S架构的思考
本人还在学习,所以代码有点菜比,思路最重要,嘤嘤嘤~
让我们早点摆脱脚本小子和ctrl+c ctrl+v工程师的名衔
首先,编写一个工具或者程序首先要有一个思想。
什么是目录探测呢?
第一,要发送请求是不是。
第二,我们要判断响应码是不是200。
第三,可能由于waf的存在,目录不存在响应码也是200,那我们是不是可以在目标站随便输入一个目录,故意让他报错,看他存在的报错信息,是什么,是"页面不存在,跳转"这种字符提示信息。还是提示404。那样是不是我们可以直接在源代码中匹配这些关键词,如果匹配成功我们就判断这个目录不存在。(简单的绕过技巧思路)
那下面我们开始一步一步分析:
import requests a="https://www.baidu.com" ret=requests.get(a) if ret.status_code==200: print("页面存在"+"响应码:"+str(ret.status_code)) else: print("不存在")
在Python中我们可以用requests包去发送请求判断响应吗是不是200,如果是我们就判断它页面存在
package main import ( "fmt" "io/ioutil" "net/http" "os" ) func main() { url := "https://www.baidu.com" resp, err := http.Get(url) /*http.Get 是创建HTTP请求的函数, 如果请求没有出错,resp这个结构体中会得到访问的请求 结果 */ if err != nil { fmt.Fprintf(os.Stderr, "error:%v\n", err) //简单一点,可以直接Println("错误") //把格式字符串输出到指定文件设备中,所以参数比printf多一个文件指针FILE*。 //os.Stderr用于显示错误信息,大多情况下等用于os.Stdout } b, err := ioutil.ReadAll(resp.Body) cunhuo := resp.StatusCode defer resp.Body.Close() /* 关键字 defer 用于注册延迟调用。 这些些调用直到 return 前才被执。因此,可以用来做资源清理。 resp.Body字段 包括一个可读的服务器响应流 ioutil.ReadAll函数 从response中读取到全部内容 resp.Body.Close() 关闭resp Boby流 */ if err != nil { fmt.Println("Error!") os.Exit(1) } if cunhuo == 200 { fmt.Println(cunhuo) fmt.Println("存在此页面") } else { fmt.Println("不存在此页面") } fmt.Printf("%s", b) }
同样在Go语言内置包中"io/ioutil". "net/http"也提供了相应的方法来进行判断
提醒:根据Go包中规则,只有大写字母才能被引用,所以我们平常会一直看到大写字母开头的引用
if err != nil相当于Python中的try语句(异常处理机制)
我们现在可以得到一个响应码了,但我们正常的思路是在一个..../x(随机变量)这种方式,去循环请求服务器,不断的改变x的值,去查看什么情况下能够得到200的响应码
这时候我们便需要一个打开一个文件(这个文件作为我们的x的值)
在Python我们只需要写一个循环去请求字典中的值,然后写一个判断便可以了
import requests a="http://220.249.52.134:49872/" with open("1.txt","r") as f : b=f.read().splitlines() for i in b: rep=requests.get(a+str(i)) if rep.status_code == 200: print("存在此页面:"+a+str(i)) else: print(a+str(i)+"不存在")
而在Go语言中我们同样使用此种思路,读取文件去循环发送请求
package main import ( "bufio" "fmt" "net/http" "os" ) func main() { //author yanmu url := "http://220.249.52.134:49872/" file, err := os.Open("1.txt") //os包是系统标准库里面有操作系统相关的函数和变量,打开一个文件可以使用os.open if err != nil { fmt.Printf("文件读书失败") } defer func() { file.Close() }() //定义一个立即执行的匿名函数 n := bufio.NewScanner(file) /* 函数NewScanner返回一个Scanner结构体,这个返回值来自于函数参数file */ //fmt.Printf("%T-%d\n"m, n, n) for n.Scan() { //n.Scan()相当于其他语言的迭代器iterator,并把迭代器指向的数据存放到新的缓冲区里。新的缓冲区可以用n.Text()得到 //n.Scan()每一次调用都会调入一个新行,并且会自动将其行末的换行符去掉 //n.Scan()是一个bool类型,在这里为true data := n.Text() resp, err := http.Get(url + data) if err != nil { fmt.Fprintf(os.Stderr, "error:%v\n", err) } //b, err := ioutil.ReadAll(resp.Body) cunhuo := resp.StatusCode defer resp.Body.Close() if err != nil { fmt.Println("Error!") os.Exit(1) } if cunhuo == 200 { fmt.Println("存在此页面:" + url + data) } else { fmt.Println("不存在此页面:" + url + data) } //fmt.Printf("%s", b) } }
做为一个工具我们希望它有其的灵活性,就像大多数工具一样可以给定相对应的参数
在Python中我们可以使用optparse模块来指定我们的参数
import requests import optparse import sys def main(): parser =optparse.OptionParser('- u <url>') parser.add_option('-u',dest='a',type='string',help="url") options,args=parser.parse_args() if options.a==None: print(parser.usage) sys.exit() else: url=options.a a=url with open("1.txt","r") as f : b=f.read().splitlines() for i in b: rep=requests.get(a+str(i)) if rep.status_code == 200: print("存在此页面:"+a+str(i)) else: print(a+str(i)+"不存在") if __name__ == '__main__': main()
在Go语言中我们同样可以运用flag包去接受我们的参数
package main import ( "bufio" "flag" "fmt" "net/http" "os" ) func main() { var url string flag.StringVar(&url, "url", "", "请输入一下你的URL") flag.Parse() if url == "" { fmt.Println(` ▍ ★∴ ....▍▍....█▍ ☆ ★∵ ..../ ◥█▅▅██▅▅██▅▅▅▅▅███◤ .◥███████████████◤ ~~~~◥█████████████◤~~~~ 雷石安全实验室出品 -h 查看相应步骤 版本 0.0001 `) os.Exit(1) } file, err := os.Open("1.txt") if err != nil { fmt.Printf("文件读取失败") } defer func() { file.Close() }() n := bufio.NewScanner(file) //fmt.Printf("%T-%d\n"m, n, n) //fmt.Printf("%T--\n", n.Scan()) //fmt.Println(n.Scan()) for n.Scan() { data := n.Text() resp, err := http.Get(url + data) if err != nil { fmt.Fprintf(os.Stderr, "error:%v\n", err) } //b, err := ioutil.ReadAll(resp.Body) cunhuo := resp.StatusCode defer resp.Body.Close() if err != nil { fmt.Println("Error!") os.Exit(1) } if cunhuo == 200 { fmt.Println("存在此页面:" + url + data) } else { fmt.Println("不存在此页面:" + url + data) } //fmt.Printf("%s", b) } }
我们有了灵活性以后,我们可以希望速度上快一点,不然我们扫完以后可能,就没有以后了(=,=)
在Python中我们可以threading模块去创建多线程
import requests import threading import time import optparse import sys def test01(url): try: resp = requests.get(url, headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0'}) if resp.status_code == 200: print('[**' + str(resp.status_code) + '**]' + ":" + url) else: print(url+str(resp.status_code)) except: pass threadm.release() def test02(file,url): for i in file.readlines(): urls = url+i.strip() threadm.acquire() t = threading.Thread(target=test01, args=(urls,)) threads.append(t) t.start() for t in threads: t.join() if __name__ == '__main__': start = time.time() threads = [] threadm = threading.BoundedSemaphore(500) parser = optparse.OptionParser('- u <url>') parser.add_option('-u', dest='a', type='string', help="url") options, args = parser.parse_args() if options.a == None: print(parser.usage) sys.exit() else: url = options.a f= open('1.txt',encoding='utf-8') test02(f,url) end = time.time() print(end-start) f.close()
package main import ( "bufio" "flag" "fmt" "net/http" "os" "sync" "time" ) //channel 通道是一个同步处理的过程 var wg sync.WaitGroup //time.Time是一个结构体时间点 func testtime(start time.Time) { fmt.Printf("耗时:%v", time.Since(start)) //func Since(t Time)Duration 表示自从t时刻以后过了多长时间,是一个时间段 } //开启一个goroutines func test01(file *os.File, url string, urls chan string) { n := bufio.NewScanner(file) for n.Scan() { var okurl = fmt.Sprintf("%s%s", url, n.Text()) // Sprintf() 是把格式化字符串输出到指定的字符串中,可以用一个变量来接受,然后在打印 urls <- okurl //向urls通道中写入完整的url } err := n.Err() if err != nil { fmt.Println("Error!") } close(urls) fmt.Println("读取完毕") } func main() { var url string flag.StringVar(&url, "url", "", "请输入一下你的URL") flag.Parse() if url == "" { fmt.Println(` ▍ ★∴ ....▍▍....█▍ ☆ ★∵ ..../ ◥█▅▅██▅▅██▅▅▅▅▅███◤ .◥███████████████◤ ~~~~◥█████████████◤~~~~ 雷石安全实验室出品 -h 查看相应步骤 版本 0.0001test `) os.Exit(1) } times := time.Now() urls := make(chan string) file, err := os.Open("1.txt") if err != nil { fmt.Println("字典打开失败: ", err) return } defer testtime(times) defer file.Close() go test01(file, url, urls) //开启多个goroutines for i := 0; i < 200; i++ { wg.Add(1) go gourl(urls) } wg.Wait() } func gourl(urls chan string) { for { /* Go里面提供了一个关键字select,通过select可以监听channel上的数据流动。 select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。 select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。 */ select { case url, ok := <-urls: if !ok { wg.Done() return } //Golang通过http.NewRequest实现模拟请求,添加请求头和请求参数: client := &http.Client{} request, err := http.NewRequest("HEAD", url, nil) if err != nil { continue } request.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0") resp, err := client.Do(request) if err != nil { fmt.Println(err) continue } if resp.StatusCode == 200 { fmt.Printf("%s 状态码: %v\n", url, resp.StatusCode) } else { fmt.Printf("%s 状态码: %v\n", url, resp.StatusCode) } resp.Body.Close() /*超时控制 通常我们可以通过select + time.After 来进行超时检查*/ case <-time.After(time.Duration(2) * time.Second): wg.Done() return } } }