furas.pl
# prywatne notatki - Python, Linux, Machine Learning, etc.

Jak używać osquery w Pythonie.

2019.02.17 - niedziela / kategoria: Python / tagi: python /

osquery to program stworzony przez Facebook, który za pomocą zapytań SQL pozwala pobierać informacje o systemie, sprzęcie, uruchomionych programach itp.

Program można pobrać z https://osquery.io/downloads/ w postaci paczki instalacyjnej pod Linux, MacOS lub Windows. Można też dodać repozytorium i instalować za pomocą np. apt pod Debian/Ubuntu/Mint.

Strona: https://osquery.io/

Dokumentacja: https://osquery.readthedocs.io

GitHub: https://github.com/facebook/osquery

Po instalacji programu osquery (który jest dostępny z terminala jako osqueryi) należy jeszcze doinstalować moduł dla Pythona.

$ pip install osquery

GitHub osquery-python: https://github.com/osquery/osquery-python

UWAGA: Część pobieranych informacji może wymagać uprawnień administratora więc wywołania zapytań mogą wymagać użycia sudo.

$ sudo osqueryi 'ZAPYTANIE SQL'

$ sudo python skrypt.py

Przykładem informacji wymagających sudo są dane o kościach pamięci RAM.

$ sudo osqueryi 'SELECT * FROM memory_devices'

Te same dane z użyciem Pythona (tyle, że zamiast tabeli mamy listę ze słownikami)

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query('SELECT * FROM memory_devices')
pprint.pprint(data.response)

Moduł Pythona dostarcza dane w postaci listy ze słownikami więc łatwo można wyświetlać tylko wybrane informacje:

import osquery

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query('SELECT * FROM memory_devices')

for item in data.response:
    size = item['size']
    if size != '0':  # pominięcie pustych banków
        print(item['form_factor'], item['memory_type'], size, item['max_speed'])

Ponieważ mam dwie kości (i dwa puste banki) więc otrzymuję wynik podobny do

SODIMM DDR3 4096 1334
SODIMM DDR3 4096 1334

To samo można także uzyskać z użyciem WHERE w SQL zamiast if w Pythonie.

import osquery

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query('SELECT * FROM memory_devices WHERE size != 0')

for item in data.response:
    print(item['form_factor'], item['memory_type'], size, item['max_speed'])

Zapytania SQL pozwalają na używanie wiele innych elementów znanych z tradycyjnych zapytań do baz danych.

Nazwy tabel

osqueryi posiada krótką komendę do listowania dostępnych tabel. Nie jest ona jednak dostępna z poziomu Pythona.

$ osqueryi '.tables'

To samo można otrzymać z użyciem zapytania, które można już zastosować w Pythonie.

$ osqueryi "SELECT name FROM osquery_registry WHERE registry = 'table'"

I to samo w Pythonie

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query("SELECT name FROM osquery_registry WHERE registry = 'table'")
pprint.pprint(data.response)

Używając count() można nawet policzyć ilość dostępnych tabel.

$ osqueryi "SELECT count(name) FROM osquery_registry WHERE registry = 'table'"

I to samo w Pythonie

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query("SELECT count(name) FROM osquery_registry WHERE registry = 'table'")
pprint.pprint(data.response)

.tables pozwala wyświetlić listę tabel zaczynających się na podany ciąg znaków np. me

$ osqueryi '.tables me'

  => memory_array_mapped_addresses
  => memory_arrays
  => memory_device_mapped_addresses
  => memory_devices
  => memory_error_info
  => memory_info
  => memory_map

To samo można uzyskać z pełnym zapytaniem i użyciem % na końcu me%

$ osqueryi "SELECT name FROM osquery_registry WHERE registry = 'table' AND name LIKE 'me%'"

I to samo w Pythonie

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query("SELECT name FROM osquery_registry WHERE registry = 'table' AND name LIKE 'me%'")
pprint.pprint(data.response)

.tables nie potrafi jedna wyświetlać listy tabel, które zawierają podany ciąg gdzieś wewnątrz nazwy - np. kończące się na info

A zapytanie SQL to potrafi z użyciem % na początku %info

$ osqueryi "SELECT name FROM osquery_registry WHERE registry = 'table' AND name LIKE '%info'"

Podobnie Python

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query("SELECT name FROM osquery_registry WHERE registry = 'table' AND name LIKE '%info'")
pprint.pprint(data.response)

Nazwy kolumn

osqueryi ma specjalną komendę do podania listy kolumn jakie zwróci zapytanie.

$ osqueryi '.types SELECT * FROM time;'

+----------------+---------+
| name           | type    |
+----------------+---------+
| weekday        | TEXT    |
| year           | INTEGER |
| month          | INTEGER |
| day            | INTEGER |
| hour           | INTEGER |
| minutes        | INTEGER |
| seconds        | INTEGER |
| timezone       | TEXT    |
| local_time     | INTEGER |
| local_timezone | TEXT    |
| unix_time      | INTEGER |
| timestamp      | TEXT    |
| datetime       | TEXT    |
| iso_8601       | TEXT    |
+----------------+---------+

Python ma na to osobną metodę: .getQueryColumns("ZAPYTANIE SQL")

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.getQueryColumns("SELECT * FROM time")
pprint.pprint(data.response)
[{'weekday': 'TEXT'},
 {'year': 'INTEGER'},
 {'month': 'INTEGER'},
 {'day': 'INTEGER'},
 {'hour': 'INTEGER'},
 {'minutes': 'INTEGER'},
 {'seconds': 'INTEGER'},
 {'timezone': 'TEXT'},
 {'local_time': 'INTEGER'},
 {'local_timezone': 'TEXT'},
 {'unix_time': 'INTEGER'},
 {'timestamp': 'TEXT'},
 {'datetime': 'TEXT'},
 {'iso_8601': 'TEXT'}]

Za pomocą zapytania z PRAGMA można też dowiedzieć się coś więcej na temat kolumn z istniejącej tabeli.

$ osqueryi 'PRAGMA table_info(time);'

+-----+----------------+---------+---------+------------+----+
| cid | name           | type    | notnull | dflt_value | pk |
+-----+----------------+---------+---------+------------+----+
| 0   | weekday        | TEXT    | 0       |            | 0  |
| 1   | year           | INTEGER | 0       |            | 0  |
| 2   | month          | INTEGER | 0       |            | 0  |
| 3   | day            | INTEGER | 0       |            | 0  |
| 4   | hour           | INTEGER | 0       |            | 0  |
| 5   | minutes        | INTEGER | 0       |            | 0  |
| 6   | seconds        | INTEGER | 0       |            | 0  |
| 7   | timezone       | TEXT    | 0       |            | 0  |
| 8   | local_time     | INTEGER | 0       |            | 0  |
| 9   | local_timezone | TEXT    | 0       |            | 0  |
| 10  | unix_time      | INTEGER | 0       |            | 0  |
| 11  | timestamp      | TEXT    | 0       |            | 0  |
| 12  | datetime       | TEXT    | 0       |            | 0  |
| 13  | iso_8601       | TEXT    | 0       |            | 0  |
+-----+----------------+---------+---------+------------+----+

I podobnie w Pythonie

import osquery
import pprint

instance = osquery.SpawnInstance()
instance.open()

data = instance.client.query("PRAGMA table_info(time)")
pprint.pprint(data.response)

[{'cid': '0', 'dflt_value': '', 'name': 'weekday', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}, {'cid': '1', 'dflt_value': '', 'name': 'year', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '2', 'dflt_value': '', 'name': 'month', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '3', 'dflt_value': '', 'name': 'day', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '4', 'dflt_value': '', 'name': 'hour', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '5', 'dflt_value': '', 'name': 'minutes', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '6', 'dflt_value': '', 'name': 'seconds', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '7', 'dflt_value': '', 'name': 'timezone', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}, {'cid': '8', 'dflt_value': '', 'name': 'local_time', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '9', 'dflt_value': '', 'name': 'local_timezone', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}, {'cid': '10', 'dflt_value': '', 'name': 'unix_time', 'notnull': '0', 'pk': '0', 'type': 'INTEGER'}, {'cid': '11', 'dflt_value': '', 'name': 'timestamp', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}, {'cid': '12', 'dflt_value': '', 'name': 'datetime', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}, {'cid': '13', 'dflt_value': '', 'name': 'iso_8601', 'notnull': '0', 'pk': '0', 'type': 'TEXT'}]

Książki: python-dla-kazdego-podstawy-programowania python-wprowadzenie python-leksykon-kieszonkowy python-receptury python-programuj-szybko-i-wydajnie python-projekty-do-wykorzystania black-hat-python-jezyk-python-dla-hackerow-i-pentesterow efektywny-python-59-sposobow-na-lepszy-kod tdd-w-praktyce-niezawodny-kod-w-jezyku-python aplikacje-internetowe-z-django-najlepsze-receptury