解析NMAP XML问题

时间:2017-08-29 19:17:19

标签: python xml xml-parsing

考虑一下你是否会拥有默认的nmap XML输出。

我特意试图解析IP地址(这里没问题)和OS供应商(这是问题)。

问题是因为xml标记有几个实例,以及属性,我无法弄清楚如何使用untangle语法从也需要索引的标记中提取和归属。

xml看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.40 scan initiated Tue Aug 29 12:45:56 2017 as: nmap -sV -O -oX ./nmap_results.xml 1.2.3.4/24 -->
<nmaprun attributes="">
    <scaninfo attributes="" />
    <debugging attributes="" />
    <host attributes="">
        <status attributes="" />
        <address attributes="" />
        <hostnames>
            <hostname attributes="" />
        </hostnames>
        <ports>
            <extraports attributes="">
                <extrareasons attributes="" />
            </extraports>
            <port attributes="">
                <state attributes="" />
                <service attributes="" />
            </port>
            <port attributes="">
                <state attributes="" />
                <service attributes="">
                    <cpe>stuff</cpe>
                    <cpe>more stuff</cpe>
                </service>
            </port>
            ...

让我们假设我想从第一个端口实例中提取属性。

在我的python中,我认为它看起来像这样:

#!/bin/env python
import untangle

nmap = untangle.parse('./location/to/results.xml')

alive = int(nmap.nmaprun.runstats.hosts['up'])

count = range(0,alive,1)
for tick in count:
    print(nmap.nmaprun.host[tick].ports.port[0, 'attribute'])

这里的问题是端口[0,&#39;属性&#39;]的实例,因为它想要并且需要0索引,但是还有我想要提取的属性。

这是python错误:

/usr/bin/python2.7 /path/to/my/dot.py
Traceback (most recent call last):
  File "/path/to/my/dot.py", line 10, in <module>
    print(nmap.nmaprun.host[tick].ports.port[0, 'vendor'])
TypeError: list indices must be integers, not tuple

Process finished with exit code 1

如果我尝试仅使用属性名称而没有索引,我会得到:

/usr/bin/python2.7 /path/to/my/dot.py
Traceback (most recent call last):
  File "/path/to/my/dot.py", line 10, in <module>
    print(nmap.nmaprun.host[tick].ports.port['vendor'])
TypeError: list indices must be integers, not str

Process finished with exit code 1

如果我只提供索引,我会得到一个包含所有属性的字符串,但我只需要一个。

我做错了什么或有办法吗?

1 个答案:

答案 0 :(得分:0)

我没有盲目猜测,而是下载了模块(它是一个 .py 文件)并开始使用它。我学到了什么:

  1. 基于 xml 节点, xml 树确实已转换为 Python 对象(untangle.Element)的树状结构标签(将成为对象属性)
  2. Element支持索引([Python]: object.__getitem__(self, key))并返回名称与给定密钥匹配的 xml 节点属性
  3. xml 节点有多个具有相同标记的节点时,相应的转换对象将是 Element个对象的列表
  4. Element 支持迭代[Python]: object.__iter__(self))并在迭代时产生自身
  5. 从子弹3.和4.结果它&#39;最好总是遍历一个元素,该元素可以出现一次或多次 旁注*

    这里有一些代码可以证明:

    #!/bin/env python
    
    import untangle
    
    FILE_NAME = "a.xml" # "./location/to/results.xml" # You should change the name back to match your location
    
    
    def main():
        nmap = untangle.parse(FILE_NAME)
        up_host_count = int(nmap.nmaprun.runstats.hosts['up'])
        host_iterator = nmap.nmaprun.host
        for host in host_iterator:
            print("IP Address: {}".format(host.address["addr"]))
    
            vendors = set()
            osmatch_iterator = host.os.osmatch
            for osmatch in osmatch_iterator:
                osclass_iterator = osmatch.osclass
                for osclass in osclass_iterator:
                    vendor = osclass["vendor"]
                    if vendor is not None:
                        vendors.add(vendor)
            print("    OS Vendors: {}".format(vendors))
    
            port_iterator = host.ports.port
            for port in port_iterator:
                print("    Port number: {}".format(port["portid"]))
    
    
    if __name__ == "__main__":
        main()
    

    备注

    • 代码中的每个for循环都是迭代的一个例子(我在上面讨论过),我从提供的 xml 示例(完整版本)中得到它,看看它在哪里有多个节点具有相同的标签
    • 当然,除了总是检查对象类型的迭代之外,还有其他选择,但这既不好也不可扩展
    • 问题中不需要端口处理,但我把它放在那里,因为有一个涉及端口的例子没有工作
    • 由于 nmap 扫描可以识别来自不同供应商的多个操作系统(在我们的案例中发生)(在我们的案例中没有发生,特别是在 Ux Unix )风格之间,我为 OS <添加了一些逻辑/ em>供应商部分仅显示一次(您可以手动修改 xml 文件,并为其中一个osclass节点指定 Linux 以外的供应商并看到它出现在输出中)
    • 代码使用 Python3 Python2
    • 运行

    <强>输出

    E:\Work\Dev\StackOverflow\q45946779>python b.py
    IP Address: 127.0.0.1
        OS Vendors: set([u'Linux'])
        Port number: 22
        Port number: 111
        Port number: 631
        Port number: 2222
        Port number: 8081
        Port number: 30000
    

    旁注* :我谈过一个可能出现一次或多次的元素,但这种方法(我在谈论untangle模块方法)并不是很错误验证 xml 是否不完整。采取以下代码行(不再使用,但我保留它只是为了说明一点):

    up_host_count = int(nmap.nmaprun.runstats.hosts['up'])
    

    如果 xml 中缺少任何节点nmaprunrunstatshosts,该行将吐出 AttributeError 。相同的行,但具有防错功能,如下所示:

    up_host_count = int(getattr(getattr(getattr(nmap, "nmaprun", None), "runstats", None), "hosts", None)["up"] or 0)
    

    但这很难看,而且在推进 xml 树深度时会变得更加混乱。