freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

根据IP地址登记信息进行目标信息收集
2023-02-21 11:56:03
所属地 浙江省

0x00 前言

在目标资产梳理的过程中,总能遇到一些IP的Whois信息中存在与目标的关联。

image

例如上图中218.108.40.56属于218.108.40.56 - 218.108.40.63,整个段的网络名称为xihu-gov,描述为Hangzhou xihu gov,根据这个描述和名称推测与之相关联的极有可能是杭州市西湖区人民zf。

这种关联给了我们另一种寻找目标资产的思路,即通过一定的手段处理目标资产名称为关键词,在数据库中查找与之相关联的IP段。但我在网络中没有寻找到适合红队进行此类资产梳理的工具(应该是我菜没错了),决定自己动手。

不想看过程的师傅们可直接在下边下载工具用。

工具(开源)与数据库下载:https://github.com/SleepingBag945/IPSearch

总共两个功能,根据IP查询所属IP段信息、根据关键词查询IP段信息。

若是师傅们喜欢此工具,给个star呗?

0x01 数据来源

要进行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地址段信息。

image

那这就很方便了,免除了编写爬虫的过程,只需要读取下载的文件进行解析即可。

0x02 数据解析

解压后的apnic.db.inetnum文件有足足500m,直接对文本文件进行检索性能绝对有问题,那就需要对数据进行解析后存进数据库,在对数据库进行检索。为了方便到处跑,这里选自包含的、无服务的、零配置的sqlite3数据库

既然要存数据库,那必然得建表,这里写了个小脚本统计到底有多少像这样的列。





image

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,那么可以将多个重复项通过空格合并写入数据库的统一列,不影响数据搜索。

image

这就可以建表了,建表语句如下

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 IPy

whoisinfo = []

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()

代码看着比较笨,但运行后看起来效果还不戳。

image

不想麻烦的师傅们可以直接进github的Release找到IP.zip下载。

数据库有了,接下来就是查询工具的实现。

语言方面我选择能一次编码到处运行的Golang。

0x03 IP查询IP段

在线版IPwhois

http://ip.webmasterhome.cn/ipwhois.asp?ip=1.1.1.1

这个功能是为了实现IPWhois的离线版,解决在线查询目标资产大量IP会比较慢,也会给服务器造成负担的问题。

前边建表的时候为了方便此功能,增加了start与end字段,这时候该派上用场了。

image

比如我要搜索222.222.222.222的IP段信息,转换得到十进制IP为3739147998,那就搜索start比这个小于等于,end比这个大于等于的ip段。

image

出现了三个包含目标的IP段,选范围最小最精细的。那就用end-start排序一下,最后只输出最小的那个。

image

最终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)
}

0x04 关键词查询IP段

一般与目标名称关联的信息在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
		}
	}
}

搜开头对应的西湖人民zf IP段就直接这么调用

queryInfoByKey([]string{"xihu", "gov"}, db)

image

可见还是可以获取到目标IP段的,但会有奇怪的东西混进来,需要人工筛选。

0x05 使用场景

演练中用户收集目标信息、拓展攻击面。

拿到目标名称为杭州市西湖区人民zf后,根据西湖可查询xihu,人民zf可查询gov,合并为xihu,gov.

使用IPSearch直接搜索相关IP段。

coco@Mac IPSearch % ./IPSearch -k xihu,gov
序号: 1
IP段: 218.108.40.56 - 218.108.40.63
名称: xihu-gov
描述: Hangzhou xihu gov. 

序号: 2
IP段: 222.133.244.192 - 222.133.244.207
名称: LCZFXXH
描述: liaochenggovermentxinxihua 

序号: 3
IP段: 61.164.41.224 - 61.164.41.255
名称: XIHU-GOV-INFORMATION-CENTER
描述: XIHU District government Information Center

序号: 4
IP段: 58.241.40.64 - 58.241.40.127
名称: XINXIHUABANGONGSHI-GOV
描述: XINXIHUABANGONGSHI-GOV,WUXI,JIANGSU PROVINCE 

序号: 5
IP段: 115.238.92.224 - 115.238.92.239
名称: HZ-XIHU
描述: The people's government of Hangzhou City,Xihu District

可以得到五个结果,通过对描述的详细观察,序号为1,5的IP段内的IP极大概率为杭州市西湖区人民zf的资产。

代替C段拓展IP

演练中收集到一个与目标关联的IP,也许会查看当前IP下的所有资产是否与目标有关,但这个C段范围也许太大了,很可能耗费大量时间却打偏了。但将收集到的ip塞入IPSearch工具查询,通过ip信息查找相同段的资产,再看描述先过滤一次能降低日偏的风险,节约宝贝的攻击时间。

# 渗透测试 # 网络安全 # 数据安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者