这次参加了软件供应链以及域名两个方向,都是第10名
本篇主要记录一下域名方向的做题记录
域名方向主要分两个题目,第一个题目是给你一堆黑产域名,需要判断域名所属的黑产家族,以及涉黄涉赌的情况。第一题主要考验的是爬虫的构造,以及信息的获取方式。
我暂时发现了可以通过以下几种方式来识别黑产网页:
Js特征:在静态请求网页的时候,能在网页源码里看到形如下图的百度的站点统计代码,通过正则表达式筛选出js?后面的长段字符串,相同的网页的这种字符串也是相同的,通过这种方式识别恶意网页家族速度较快,效果很好。
图片特征:
通过pyppter爬取网页内容,并保存相应的图片,然后通过函数计算并比较两张图片的RMS值,rms值<1000值是表明两张图片极其相似,rms值>10000表示两张图片毫无关联。
通过这种方式进行分类精度高,但是后续需要人为对各个图片类别进行合并,因为比如亚博这样的大平台会有多个不同的黑产网页。
网页结构内容特征:
以静态的方式请求网页,会发现同样黑产的网页有部分会存在特殊的内容,可以直接通过这些内容来对网页进行判读,如下图bet365的网页里会含有‘www.y666.net’以及’点我木有用’等词语,通过爬虫收集网页源码并保存,并通过浏览网页找出网页的内容结构特征,这种方法识别分类的精度较高,缺点是需要耗费大量精力去筛选,寻找这些特征。从经验来看,这些特征主要出现在源码开头或者结尾的位置,且多出现在和域名不一致的src链接信息里
标题特征
某些网站可以直接通过标题识别,但是实际发现很多黑产网页会滥用其他黑产网站的标题,因此如果想通过标题来识别分类恶意软件家族,依然需要花费一定的精力来筛选这些标题特征,不过如果能准确提取标题特征,那么使用这种特征识别的正确率会很高。
一些标题特征举例:如发彩网在标题里会直接有‘-发彩网‘的字段,尊龙黑产网页在标题里会有‘就是博’的字段,bet365的网页里会有◆~,√等字段,球盟会的网页里有很多使用【】的字段,如【老虎机】【官网】【官网入口】【老品牌】【平台入口】【体育首选】【手机登录】【手机入口】【手机下载】【点击登录】【送彩金】【体育平台】【正网】【真人平台】【注册送彩金】
试了python request 和selenium爬虫,发现最好用的爬虫还是pyppeteer
import asyncio
from pyppeteer import launch
import re
import csv
import codecs
import pickle
import os
csv.field_size_limit(500 * 1024 * 1024)
def read_csv(filename="A_test_data.csv"):
# 读取csv文件
full_data = []
with codecs.open(filename, 'r', encoding='gb2312', errors='ignore') as f:
reader = csv.reader(f)
for row in reader:
full_data.append(row)
return full_data[1:] # 删去抬头
def load_picklefile(filename):
# 读取pickle文件
pickfile = open(filename, 'rb')
listfile = pickle.load(pickfile)
return listfile
# -*-coding:utf-8 -*-
def write_to_pickle(dictdata, filename):
# 把dict_data以pickle的形式保存
pick_file = open(filename + '.pkl', 'wb')
pickle.dump(dictdata, pick_file)
pick_file.close()
def write_csv(answer_data=[(0, 0)], data_head=[
("域名", "域名排名", "title", "类型", "违规项目")
], filename='failname'):
# 生成答案的csv文件
data = data_head + answer_data
f = codecs.open(filename, 'w', 'utf_8_sig', errors='ignore')
writer = csv.writer(f)
for i in data:
writer.writerow(i)
f.close()
def pil_image_similarity(filepath1, filepath2):
# 输入两个图片
from PIL import Image
import math
import operator
from functools import reduce
image1 = Image.open(filepath1)
image2 = Image.open(filepath2)
h1 = image1.histogram()
h2 = image2.histogram()
rms = math.sqrt(reduce(operator.add, list(map(lambda a, b: (a - b) ** 2, h1, h2))) / len(h1))
print(rms)
return rms
async def main(filecsv):
data = read_csv(filecsv)
domain_list = []
for each_row in data:
domain_list.append(each_row[0])
save_folder_path = filecsv.replace('.csv', "")
pic_save_path = save_folder_path + '/pic/'
pickle_save_path = save_folder_path + '/pickfile/'
count = 0
try:
if not os.path.exists(save_folder_path):
os.makedirs(save_folder_path)
except Exception:
pass
if not os.path.exists(pic_save_path):
os.makedirs(pic_save_path)
if not os.path.exists(pickle_save_path):
os.makedirs(pickle_save_path)
try:
page_dict = load_picklefile(pickle_save_path + 'page_dict.pkl')
except Exception:
page_dict = {}
try:
js_dict = load_picklefile(pickle_save_path + 'js_dict.pkl')
print(js_dict)
except Exception:
js_dict = {}
try:
title_dict = load_picklefile(pickle_save_path + 'title_dict.pkl')
print(title_dict)
except Exception:
title_dict = {}
browser = await launch(args=['--no-sandbox'])
page = await browser.newPage()
compile_rule = r'''.js\?(.*)['|"];'''
await page.setJavaScriptEnabled(enabled=True)
for url in domain_list:
count = count + 1
if count%20==0:
write_to_pickle(js_dict,pickle_save_path+'js_dict')
write_to_pickle(page_dict,pickle_save_path+'page_dict')
write_to_pickle(title_dict,pickle_save_path+'title_dict')
if not url.startswith('http:'):
url = 'http://'+url
if url in page_dict.keys():
continue
print(url)
await page.goto('http://www.sjzlongxiang.com/')
content = await page.evaluate('document.body.textContent', force_expr=True)
title = await page.title()
print(title)
js_feature = re.findall(compile_rule, content)
try:
title_dict.update({url: title})
except Exception:
pass
try:
js_dict.update({url: js_feature})
except Exception:
pass
try:
page_dict.update({url: content})
except Exception:
pass
try:
current_pic_save_path = pic_save_path+url+'.png'
current_pic_save_path=current_pic_save_path.replace('http://','')
await page.screenshot({'path': current_pic_save_path})
except Exception as e:
print(e)
pass
await browser.close()
asyncio.get_event_loop().run_until_complete(main('crawresult_12.csv'))
实际在比赛中,主要使用的是标题特征和网页结构内容特征进行识别,js特征和图片特征仅停留在收集阶段,因时间与精力有限未能应用全部设想的四种特征。如果时间精力足够的话,后续想的是分类出一定恶意软件家族后,通过流量的数据进行深度学习来识别那些爬取失败的网页。
第二部分是域名排名攻击,需要选手自己注册一个域名,然后赛方会给你一个DNS服务器地址,选手要做的就是让自己域名的排名在这个DNS服务器地址上尽可能的高。
SACPY模拟UDP发包:
from scapy.all import DNS, DNSQR, IP, sr1, UDP,send
import random
while True:
random_ip= '120.'+str(random.randint(20,100))+'.'+str(random.randint(2,200))+'.'+str(random.randint(2,200))
dns_req = IP(src=random_ip,dst='xxx.xxx.xxx.xxx')/UDP(dport=53)/DNS(rd=1, qd=DNSQR(qname='badamerica.com',qtype=1))
send(dns_req)
SCAPY模拟TCP协议:
from scapy.all import *
while True:
seq = RandInt()
tcp_req = IP(dst='xxx.xxx.xxx.xxx') / TCP(dport=53, sport=RandShort(), seq=1, flags='S') / DNS(rd=1, qd=DNSQR(
qname='badamerica.com', qtype=1))
ns = sr1(tcp_req)
sport = ans[TCP].dport
s_seq = ans[TCP].ack
d_seq = ans[TCP].seq + 1
print(sport, s_seq, d_seq)
tcp_req = IP(dst='xxx.xxx.xxx.xxx') / TCP(dport=53, sport=sport, ack=d_seq, seq=s_seq, flags='A') / DNS(rd=1, qd=DNSQR( qname='badamerica.com', qtype=1))
sr1(tcp_req)
udp_req = IP(dst='xxx.xxx.xxx.xxx') / UDP(dport=53) / DNS(rd=1, qd=DNSQR(qname='badamerica.com', qtype=1))
while True:
try:
ans = sr1(udp_req)
print(ans[DNS])
except Exception:
break
因为浏览第一题的黑产网页,能发现黑产网页采用的都是挂了大量链接,构造蜘蛛池的方法,还有就是通过修改网页标题和关键字来诱导别人点击,能想到出题人的意思是要选手模仿黑产网页来提高自己域名的排名。
但是我们队在阅读题目后发现,题目没有说明给的地址是dns解析器还是dns转发器,但是排名确定是只算某一特定域名服务器上的解析记录,且发现该域名服务器请求的权威服务器是godaddy的域名服务器,首先想到的是使用scapy伪造udp流量。使用scapy可以伪造udp包的源地址和目的地址,但是因为网上购买的服务器在发包的时候需要经过内网的nat转化,所以实际上如果修改了源地址,在阿里云,腾讯云上进行发包是发不出去的。(本队就此问题进行了工单询问,得到的结果如果想发修改源地址的udp包是需要手工进行一定量的配置修改,无法使用镜像进行大规模复制),不过如果使用自己的台式机或者手提电脑则可以任意修改源地址。最终域名攻击1是使用了100台1cpu,1gb的阿里云服务器打流量,购买了一星期花费在2000元左右,最终得分约77.
此外,也考虑过模仿黑产网页构造蜘蛛池的方法,但是考虑到正常用户请求dns解析一般是访问本地的域名解析器,且在解析成功后会在本机留下dns缓存记录,且未想出方法去指定那些正常用户去访问给定的dns解析器。
第二题禁掉了UDP包,只能用TCP协议,暂时想到的也是用scapy模拟tcp协议,写的脚本是建立tcp通信后开始发udp查询包,不过貌似没有记入排名,后续也没有深度研究。