免责声明
本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。
只供对已授权的目标使用测试,对未授权目标的测试作者不承担责任,均由使用本人自行承担。
文章正文
在目标资产梳理的过程中,总能遇到一些IP的Whois信息中存在与目标的关联。
例如上图中218.108.40.56属于218.108.40.56 - 218.108.40.63,整个段的网络名称为xihu-***,描述为Hangzhou xihu ***,根据这个描述和名称推测与之相关联的极有可能是杭州市西湖区****。
这种关联给了我们另一种寻找目标资产的思路,即通过一定的手段处理目标资产名称为关键词,在数据库中查找与之相关联的IP段。但我在网络中没有寻找到适合红队进行此类资产梳理的工具(应该是我菜),因此决定自己动手。
不想看过程的师傅们可直接在下边下载工具用。
工具(开源)与数据库下载:后台回复“0321" 获取
总共两个功能,根据IP查询所属IP段信息、根据关键词查询IP段信息。
要进行IP Whois信息的查询,首先得知道从哪里获取这些信息,通过搜索引擎一把梭得知:
IP地址的划分,有RIR机构来进行统筹管理。负责亚洲地区IP地址分配的,就是APNIC,总部位于澳大利亚墨尔本。各大RIR机构都提供了关于IP地址划分的登记信息,即whois记录。
APNIC提供了每日更新的亚太地区IPv4,IPv6,AS号分配的信息表,访问url是
http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest
向上级目录访问,并浏览一番后得知
http://ftp.apnic.net/apnic/whois/apnic.db.inetnum.gz
http://ftp.apnic.net/apnic/whois/apnic.db.inet6num.gz
这里存放着亚洲地区所有IP地址段信息。
那这就很方便了,免除了编写爬虫的过程,只需要读取下载的文件进行解析即可。
解压后的apnic.db.inetnum文件有足足500m,直接对文本文件进行检索性能绝对有问题,那就需要对数据进行解析后存进数据库,在对数据库进行检索。为了方便到处跑,这里选自包含的、无服务的、零配置的sqlite3数据库
既然要存数据库,那必然得建表,这里写了个小脚本统计到底有多少像这样的列。
inetnums = []
with open("apnic.db.inetnum","rb") as f:
inetnums = f.read().split(b"\n")
result = []
for line in inetnums:
if b": " in line:
key = line.split(b":")[0]
if key not in result:
result.append(key)
for each in result:
print(each.decode("UTF-8"))
inetnum
netname
country
descr
admin-c
tech-c
status
mnt-by
mnt-routes
last-modified
source
remarks
org
abuse-c
mnt-lower
mnt-irt
geoloc
language
主键就以inetnum中的ip段的起始ip的十进制数字形式存储为start字段,ip段结束ip存储为end字段,而不是以字符串形式存储ip段,这样会方便搜索ip属于哪个ip段。
为了方便搜索ip属于哪个ip段,这里添加ip段的起始ip的十进制数字形式存储为start字段,ip段结束ip为end字段。
一个IP段中可能存在多个descr,那么可以将多个重复项通过空格合并写入数据库的统一列,不影响数据搜索。
这就可以建表了,建表语句如下
CREATE TABLE "ipseg" (
"start" INTEGER NOT NULL COLLATE BINARY,
"end" INTEGER NOT NULL,
"inetnum" TEXT NOT NULL UNIQUE,
"netname" TEXT,
"country" TEXT,
"descr" TEXT,
"admin-c" TEXT,
"tech-c" TEXT,
"status" TEXT,
"mnt-by" TEXT,
"mnt-routes" TEXT,
"last-modified" TEXT,
"source" TEXT,
"remarks" TEXT,
"org" TEXT,
"abuse-c" TEXT,
"mnt-lower" TEXT,
"mnt-irt" TEXT,
"geoloc" TEXT,
"language" TEXT,
PRIMARY KEY("inetnum")
);
再写一个小脚本负责解析并导入sqlite
import sqlite3
import IPywhoisinfo = []
def analysis_whois(whois_str):
start = 0
end = 0
inetnum = ""
netname = ""
country = ""
descr = ""
admin_c = ""
tech_c = ""
status = ""
mnt_by = ""
mnt_routes = ""
last_modified = ""
source = ""
remarks = ""
org = ""
abuse_c = ""
mnt_lower = ""
mnt_irt = ""
geoloc = ""
language = ""
for _line in whois_str.split(b"\n"):
if _line == b"":
continue
line = _line.decode(encoding='UTF-8',errors='ignore')
line = line.replace("\"","")
if "inetnum:" in line:
inetnum = line.split(":")[1].strip()
#print(inetnum)
if " - " not in inetnum:
continue
ip = inetnum.split(" - ")
start = int(IPy.IP(ip[0]).strDec())
end = int(IPy.IP(ip[1]).strDec())
elif "netname:" in line:
netname = line.replace("netname:","").strip()
elif "country:" in line:
country = line.replace("country:", "").strip()
elif "descr:" in line:
descr += line.replace("descr:", "").strip() + " "
elif "admin-c:" in line:
admin_c += line.replace("admin-c:", "").strip() + " "
elif "tech-c:" in line:
tech_c += line.replace("tech-c:", "").strip() + " "
elif "status:" in line:
status += line.replace("status:", "").strip() + " "
elif "mnt-by:" in line:
mnt_by += line.replace("mnt-by:", "").strip() + " "
elif "mnt-routes:" in line:
mnt_routes += line.replace("mnt-routes:", "").strip() + " "
elif "last-modified:" in line:
last_modified += line.replace("last-modified:", "").strip() + " "
elif "source:" in line:
source += line.replace("source:", "").strip() + " "
elif "remarks:" in line:
remarks += line.replace("remarks:", "").strip() + " "
elif "org:" in line:
org += line.replace("org:", "").strip() + " "
elif "abuse-c:" in line:
abuse_c += line.replace("abuse-c:", "").strip() + " "
elif "mnt-lower:" in line:
mnt_lower += line.replace("mnt-lower:", "").strip() + " "
elif "mnt-irt:" in line:
mnt_irt += line.replace("mnt-irt:", "").strip() + " "
elif "geoloc:" in line:
geoloc += line.replace("geoloc:", "").strip() + " "
elif "language:" in line:
language += line.replace("language:", "").strip() + " "
if " - " not in inetnum or "." not in inetnum:
return
sql = """INSERT INTO ipseg(start,end,inetnum,netname,country,descr,"admin-c","tech-c",status,"mnt-by","mnt-routes","last-modified",source,remarks,org,"abuse-c","mnt-lower","mnt-irt",geoloc,language) VALUES ( """ + "\"{}\","*19 + "\"{}\" )"
sql = sql.format(start,end,inetnum,netname,country,descr[:-1],admin_c[:-1],tech_c[:-1],status[:-1],mnt_by[:-1],mnt_routes[:-1],last_modified[:-1],source[:-1],remarks[:-1],org[:-1],abuse_c[:-1],mnt_lower[:-1],mnt_irt[:-1],geoloc[:-1],language[:-1])
try:
conn.execute(sql)
except BaseException as e:
print(inetnum,"error",e,descr)
conn = sqlite3.connect("IP.db")
with open("apnic.db.inetnum","rb") as f:
whoisinfo = f.read().split(b"\n\n")
count = 0
for info in whoisinfo:
analysis_whois(info)
if count % 1000 == 0:
conn.commit()
count += 1
conn.close()
运行后看起来效果还不戳。
不想麻烦的师傅们可以直接进github的Release找到IP.zip下载。
数据库有了,接下来就是查询工具的实现。
语言方面我选择能一次编码到处运行的Golang。
在线版IPwhois
http://ip.webmasterhome.cn/ipwhois.asp?ip=1.1.1.1
这个功能是为了实现IPWhois的离线版,解决在线查询目标资产大量IP会比较慢,也会给服务器造成负担的问题。
前边建表的时候为了方便此功能,增加了start与end字段,这时候该派上用场了。
比如我要搜索222.222.222.222的IP段信息,转换得到十进制IP为3739147998,那就搜索start比这个小于等于,end比这个大于等于的ip段。
出现了三个包含目标的IP段,选范围最小最精细的。那就用end-start排序一下,最后只输出最小的那个。
最终sql语句与代码如下
select inetnum,netname,country,descr,status,"last-modified" from ipseg where start <= 3739147998 and end >= 3739147998 order by end-start ASC limit 0,1;
func queryInfoByIP(ip int, db *sql.DB) {
var inetnum string
var netname string
var country string
var descr string
var status string
var last_modified string
query := "select inetnum,netname,country,descr,status,\"last-modified\" from ipseg where start <= %d and end >= %d order by end-start ASC limit 0,1;"
query = fmt.Sprintf(query, ip, ip)
rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(&inetnum, &netname, &country, &descr, &status, &last_modified)
if err != nil {
log.Fatal(err)
}
}
fmt.Println("IP段:", inetnum)
fmt.Println("名称:", netname)
fmt.Println("描述:", descr)
fmt.Println("国家:", country)
fmt.Println("状态:", status)
fmt.Println("最后修改:", last_modified)
}
一般与目标名称关联的信息在descr与netname中,只要信息中出现关键词即可输出。
sql语句很简单。
select inetnum,netname,descr from ipseg where descr like "%key%" or netname like "%key%";
多个key查询只需要稍稍构造下sql语句。
func queryInfoByKey(keys []string, db *sql.DB) {
query := "select inetnum,netname,descr from ipseg where "
descrQuery := "("
for _, key := range keys {
descrQuery += fmt.Sprintf("descr like \"%%%s%%\" and ", key)
}
descrQuery = descrQuery[:len(descrQuery)-5] + ") "
query += descrQuery
query += "or "
netnameQuery := "("
for _, key := range keys {
netnameQuery += fmt.Sprintf("netname like \"%%%s%%\" and ", key)
}
netnameQuery = netnameQuery[:len(netnameQuery)-5] + ")"
query += netnameQuery + ";" rows, err := db.Query(query)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
count := 0
for rows.Next() {
var inetnum string
var netname string
var descr string
err = rows.Scan(&inetnum, &netname, &descr)
if err != nil {
log.Fatal(err)
}
count += 1
fmt.Println("序号:", count)
fmt.Println("IP段:", inetnum)
fmt.Println("名称:", netname)
fmt.Println("描述:", descr, "\n")
// 限制数量
if count > 2000 {
break
}
}
}
搜开头对应的西湖****IP段就直接这么调用
queryInfoByKey([]string{"xihu", "***"}, db)
可见还是可以获取到目标IP段的,但会有奇怪的东西混进来,需要人工筛选。
场景一
护网中拿到目标名称为杭州市西湖区***,根据西湖可查询xihu.
使用IPSearch直接搜索相关IP段。
[email protected] IPSearch % ./IPSearch -k xihu,***,***
序号: 1
IP段: 218.108.40.56 - 218.108.40.63
名称: xihu-***
描述: Hangzhou xihu ***. 序号: 2
IP段: 222.133.244.192 - 222.133.244.207
名称: LCZFXXH
描述: liaocheng***ermentxinxihua
序号: 3
IP段: 61.164.41.224 - 61.164.41.255
名称: XIHU-***-INFORMATION-CENTER
描述: XIHU District ***ernment Information Center
序号: 4
IP段: 58.241.40.64 - 58.241.40.127
名称: XINXIHUABANGONGSHI
描述: XINXIHUABANGONGSHI,WUXI,JIANGSU PROVINCE
序号: 5
IP段: 115.238.92.224 - 115.238.92.239
名称: HZ-XIHU
描述: The people's ***ernment of Hangzhou City,Xihu District
可以得到五个结果,通过对描述的详细观察,序号为1,5的IP段内的IP极大概率为杭州市西湖区的资产。
场景二
演练中收集到一个与目标关联的IP,也许会查看当前IP下的所有资产是否与目标有关,但这个C段范围也许太大了,很可能耗费大量时间却打偏了。但将收集到的ip塞入IPSearch工具查询,通过ip信息查找相同段的资产,再看描述先过滤一次能降低日偏的风险,节约宝贝的攻击时间。
技术交流
知识星球
致力于红蓝对抗,实战攻防,星球不定时更新内外网攻防渗透技巧,以及最新学习研究成果等。常态化更新最新安全动态。专题更新奇技淫巧小Tips及实战案例。
涉及方向包括Web渗透、免杀绕过、内网攻防、代码审计、应急响应、云安全。星球中已发布 200+ 安全资源,针对网络安全成员的普遍水平,并为星友提供了教程、工具、POC&EXP以及各种学习笔记等等。
交流群
关注公众号回复“加群”,添加Z2OBot 小K自动拉你加入Z2O安全攻防交流群分享更多好东西。
关注我们
关注福利:
回复“app" 获取 app渗透和app抓包教程
回复“渗透字典" 获取 针对一些字典重新划分处理,收集了几个密码管理字典生成器用来扩展更多字典的仓库。
回复“书籍" 获取 网络安全相关经典书籍电子版pdf
回复“资料" 获取 网络安全、渗透测试相关资料文档
往期文章