pythoncpython-2.7solariscomputer-forensics

Creating an implimentation of netstat for Solaris in Python2.6+


I am writing a collection script in python to grab forensic artifacts from various *nix hosts using Python 2 scripts, however I am running into some issues writing netstat collection for Solaris.

There are a few limitations to how I am to achieve this. I cannot use the ELF binaries (except Python) on the computer (as they are often manipulated when *nix machines are attacked), I cannot import external libraries (as this will not run on computers which I administer), and it must be written in Python 2 for backwards compatibility reasons.

All this being said, I am using the netstat.c document by Illumos (an open source Solaris implementation) as my base, and essentially trying to reverse engineer this and rewrite it in Python 2.

https://searchcode.com/codesearch/raw/52916131/

What I've figured out so far is, the script opens '/dev/arp' and '/dev/kstat' to ensure there are return values.

import os
sd = os.open('/dev/arp', os.O_RDWR)
kc = os.open('/dev/kstat', os.O_RDWR)

After this has been done, the 'sd' value is used to collect network statistics with the mibget() method.

And here's the rub. To collect the netstat information, Solaris is querying sd with the syscall getmsg, however it is using a datastructure I am unfamiliar with.

getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags);

I have no idea how to rebuild this in Python 2, do any of you fine people have any ideas of where I can go from here? Am I interpreting this wrong?

I understand the psutil implementation of netstat works on Solaris, so there must be a means to accomplish this.

Thank you all for your help.

Edit:

This appears to be the relevant section of truss netstat:

open("/etc/default/inet_type", O_RDONLY)        Err#2 ENOENT
open("/dev/arp", O_RDWR)                        = 3
ioctl(3, I_PUSH, "tcp")                         = 0
ioctl(3, I_PUSH, "udp")                         = 0
ioctl(3, I_PUSH, "icmp")                        = 0
putmsg(3, 0x08047E3C, 0x00000000, 0)            = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
getmsg(3, 0x00000000, 0x08047E48, 0x08047E5C)   = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
getmsg(3, 0x00000000, 0x08047E48, 0x08047E5C)   = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
getmsg(3, 0x00000000, 0x08047E48, 0x08047E5C)   = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
getmsg(3, 0x00000000, 0x08047E48, 0x08047E5C)   = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
getmsg(3, 0x00000000, 0x08047E48, 0x08047E5C)   = 0
getmsg(3, 0x08047E3C, 0x00000000, 0x08047E5C)   = 2
brk(0x080736E0)                                 = 0

Solution

  • getmsg() and struct strbuf are specified by POSIX, albeit marked as obsolescent in POSIX 7:

    NAME

    getmsg, getpmsg - receive next message from a STREAMS file (STREAMS)

    SYNOPSIS

    [OB XSR] [Option Start] #include <stropts.h>
    
    int getmsg(int fildes, struct strbuf *restrict ctlptr,
           struct strbuf *restrict dataptr, int *restrict flagsp);
    int getpmsg(int fildes, struct strbuf *restrict ctlptr,
           struct strbuf *restrict dataptr, int *restrict bandp,
           int *restrict flagsp); [Option End]
    

    DESCRIPTION

    The getmsg() function shall retrieve the contents of a message located at the head of the STREAM head read queue associated with a STREAMS file and place the contents into one or more buffers. The message contains either a data part, a control part, or both. The data and control parts of the message shall be placed into separate buffers, as described below. The semantics of each part are defined by the originator of the message.

    ...

    I'm not aware of any Python bindings for STREAMS on Solaris.

    STREAMS functionality was never implemented on Linux.