Here we go again! This blog post is tangential to my previous blog post on creating an Osquery extension with Python but this time we are using golang. Osquery is my favorite open-source security tool and golang is becoming a popular programming language so fusing them together allows us to engineer tools to detect threats. This blog post will build an Osquery-go extension to calculate the CommunityID of a network connection utilizing the Osquery-polylogyx extension pack to monitor network connections. In blog posts to follow, we will correlate network-based events monitored by Zeek and host-based events generated by Osquery using the CommunityID. So follow me again as your adventure guide on this development journey to make an Osquery extension with osquery-go.
NOTICE
Osquery version 4.20 was released on February 10th 2019 which contains zwass PR to include communityID generation in the Osquery project. This blog post should be viewed as a learning exercise on how to create an Osquery extension with osquery-go.
NOTICE
Background
What is Threat Detection Engineering?
In a nutshell, Threat Detection Engineering is related but not limited to the following high-level activities:
- Drive proactive identification of threats to the environment with the rapid deployment of detection controls.
- Liaise with internal teams and especially data owners, establishing relationships focused on spotting new detection opportunities.
- Make threat intelligence consumable by integrating it with SIEM and other tools that are part of the security arsenal (SOAR, EDR, etc).
- Create custom code to aid the detection of malicious activity via security alerts (rules), reports (dashboards) or simply automation (scripting).
- Establish a continuous quality assurance process, with special attention to internal users/customers feedback (SOC, monitoring teams — in special).
What is Osquery?
Osquery exposes an operating system as a high-performance relational database. This allows you to write SQL-based queries to explore operating system data. With Osquery, SQL tables represent abstract concepts such as running processes, loaded kernel modules, open network connections, browser plugins, hardware events or file hashes.
What is communityID?
CommunityID is a new feature being implemented by networking security applications such as Zeek and Suricata. A CommunityID is a hash of the tuple (destination IP address, source IP address, destination port, source port, protocol) and this tuple defines a unique connection. For example, let’s say Suricata detects malicious activity and when you examine the details of the alert it will include a unique hash as the value of communityID.
What is Osquery-go?
In osquery, SQL tables, configuration retrieval, log handling, etc. are implemented via a robust plugin and extensions API. This project contains Go bindings for creating osquery extensions in Go. To create an extension, you must create an executable binary which instantiates an ExtensionManagerServer and registers the plugins that you would like to be added to osquery. You can then have osquery load the extension in your desired context (ie: in a long running instance of osqueryd or during an interactive query session with osqueryi).
What is Osquery-polylogyx exetnsions?
PolyLogyx OSQuery Extension (plgx_win_extension.ext.exe) for Windows platform extends the core osquery on Windows by adding real time event collection capabilities to osquery on Windows platform. The capabilities are built using the kernel services library of PolyLogyx. The current release of the extension is a ‘community-only’ release. It is a step in the direction aimed at increasing osquery footprint and adoption on Windows platform. With the extension acting as a proxy into Windows kernel for osquery, the possibilities can be enormous.
Install/Setup Golang environment on macOS
curl -o ~/Downloads/golang.pkg https://dl.google.com/go/go1.13.3.darwin-amd64.pkg
- Open Finder to the Downloads folder
- Install GOLANG
- Open terminal
export GOROOT=/usr/local/go
echo 'export GOROOT=/usr/local/go' >> ~/.zshrc
go version
Setup Osquery-go environment
git clone https://github.com/CptOfEvilMinions/osquery-go-communityid
cd osquery-go-communityid
go get github.com/kolide/osquery-go
go get github.com/Microsoft/go-winio
- Pulls down libraries to generate Windows binaries
Code breakdown
func main()
The main function registers the new table/extension with Osquery. First, if any command-line arguments were specified they will be extracted. Second, the NewExtensionManagerServer
function is used to initialize a server object which will be used to register our plugin. The server object creates a communication bridge between Osquery and our extension. Third, the RegisterPlugin
function takes in three parameters which are the name of the table, a function containing the table column definitions, and the generate function used to create the results of the table.
func CommunityIDColumns()
This function defines the columns of the table and the variable type of each column.
func CommunityIDTableGenerate()
This function extracts all query supplied values necessary to generate a communityID. Next, the GenerateCommunityID
function is called to generate a CommunityID hash of the network connection. Lastly, a map of the query-supplied values and the newly generated CommunityID hash is returned to Osqury to be rendered.
func GenerateCommunityID
gommunityid.MakeFlowTuple()
. Lastly, the function returns the base64 encoded communityID hash.Compile new Osquery extension
- Back to terminal
GOOS=windows GOARCH=amd64 go build -o osquery_community_id_ext.exe osquery_community_id_ext.go
- Compile for Windows
GOOS=darwin GOARCH=amd64 go build -o osquery_community_id.ext osquery_community_id_ext.go
- Compile for macOS
file osquery_community_id_ext.*
Testing newly compiled Osquery extension
- Open a new terminal tab
osqueryi --nodisable_extensions
select value from osquery_flags where name = 'extensions_socket';
- Copy output from query
- Open another terminal tab
./osquery_community_id_ext.macho /Users/thunderwagon/.osquery/shell.em
- Back to the Osqueryi terminal tab
SELECT * FROM community_id WHERE src_ip='1.1.1.1' AND src_port=5858 AND dst_ip='8.8.8.8' AND dst_port=80 AND src_port=5858 and protocol=6;
.quit
Install/Setup Osquery and Osquery extension on Windows 10
Install Osquery on Windows 10
- Open a browser and browse to https://pkg.osquery.io/windows/osquery-4.0.2.msi
- Polyglogyx – “It has been built and tested with 3.2.6. It also works with 3.3.0 and 4.0.x”
- Install Osquery
Test extension on Windows 10
- Open a Powershell instance as Administrator
cd 'C:\Program Files\osquery'
New-Item -Path "C:\Program Files\osquery" -Name "Extensions" -ItemType "directory"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri https://github.com/CptOfEvilMinions/osquery-go-communityid/releases/download/v1.1/osquery_community_id_ext.exe -OutFile 'C:\Program Files\osquery\Extensions\osquery_community_id_ext.exe'
.\osqueryi.exe --nodisable_extensions
SELECT value FROM osquery_flags WHERE name='extensions_socket';
- Open a Powershell instance
cd 'C:\Program Files\osquery\Extensions'
.\osquery_community_id_ext.exe <value from queery above>
- Go back to PowerShell instance running Osqueryi
SELECT * FROM community_id WHERE src_ip='1.1.1.1' AND src_port=5858 AND dst_ip='2.2.2.2' AND dst_port=80 AND protcol=6;
.quit
CommunityID and Polylogyx extensions on Windows 10
Add Osquery-polylogy extensions
- DISABLE WINDOWS DEFENDER
cd $home\Downloads
Invoke-WebRequest -Uri https://github.com/polylogyx/osq-ext-bin/archive/master.zip -OutFile osq-ext-bin.zip -MaximumRedirection 3
Expand-Archive .\osq-ext-bin.zip -DestinationPath .
cd osq-ext-bin-master
Copy-Item .\plgx_win_extension.ext.exe -Destination 'C:\Program Files\osquery\Extensions\plgx_win_extension.ext.exe'
(Get-Content -path .\osquery.flags -Raw) -replace "ProgramData","Program Files" | Set-Content -Path .\osquery.flags
Copy-Item .\osquery.flags -Destination 'C:\Program Files\osquery\osquery.flags'
Set-Content -Path 'C:\Program Files\osquery\extensions.load' -Value 'C:\Program Files\osquery\Extensions\plgx_win_extension.ext.exe'
Test Osquery-polylogy and win_socket_events table
Stop-Service -Name osqueryd
cd 'C:\Program Files\osquery'
.\osqueryi.exe --flagfile .\osquery.flags
.tables
SELECT * FROM win_socket_events;
.quit
Joining win_socket_events and CommunityID tables
.\osqueryi.exe --flagfile .\osquery.flags
- Open another Powershell instance
cd 'C:\Program Files\osquery\Extensions'
.\osquery_community_id_ext.exe <value from query above>
- Go back to Osquery Powershell instance
SELECT s.local_address, s.local_port, s.remote_address, s.remote_port, s.protocol, c.community_id, s.pid, s.process_name
FROM win_socket_events as s JOIN community_id as c
ON s.local_address=c.src_ip AND
s.local_port=c.src_port AND
s.remote_address=c.dst_ip AND
s.remote_port=c.dst_port AND
s.protocol=c.protocol
WHERE action='SOCKET_CONNECT';
.quit
Setup Osquery extension for OsqueryD
- Open Notepad as Adminsitrator
- Open
C:\Program Files\osquery\extensions.load
- Paste the following:
C:\Program Files\osquery\Extensions\osquery_community_id_ext.exe
C:\Program Files\osquery\Extensions\plgx_win_extension.ext.exe
- Open a Powershell instance as Administrator
cd 'C:\Program Files\osquery'
icacls .\Extensions /setowner Administrators /t
icacls .\Extensions /grant Administrators:f /t
icacls .\Extensions /inheritance:r /t
icacls .\Extensions /inheritance:d /t
.\osqueryi.exe --flagfile .\osquery.flags
SELECT s.local_address, s.local_port, s.remote_address, s.remote_port, s.protocol, c.community_id, s.pid, s.process_name FROM win_socket_events as s JOIN community_id as c ON s.local_address=c.src_ip AND s.local_port=c.src_port AND s.remote_address=c.dst_ip AND s.remote_port=c.dst_port AND s.protocol=c.protocol WHERE action='SOCKET_CONNECT';
.quit
Invoke-WebRequest -Uri https://raw.githubusercontent.com/CptOfEvilMinions/osquery-go-communityid/master/conf/windows/osquery.conf -OutFile 'C:\Program Files\osquery\osquery.conf'
Start-Service -name osqueryd
Install/Setup Osquery and Osquery extension on macOS
Install Osquery on macOS
- Open a browser and browse to https://pkg.osquery.io/darwin/osquery-4.0.2.pkg
- Polyglogyx – “It has been built and tested with 3.2.6. It also works with 3.3.0 and 4.0.x”
- Install Osquery
Test extension on macOS
- Open a terminal tab
sudo mkdir /var/osquery/Extensions
- COPY executable to
/var/osquery/Extensions
osqueryi --nodisable_extensions
SELECT value FROM osquery_flags WHERE name = 'extensions_socket';
- Open another terminal tab
cd /var/osquery/Extensions
./osquery_community_id_ext.macho <sokcet path from above>
.tables
- Make sure
community_id
exists
- Make sure
SELECT * FROM community_id WHERE src_ip='1.1.1.1' AND src_port=5858 AND dst_ip='2.2.2.2' AND dst_port=80 AND protocol=6;
.quit
Joining CommunityID and process_open_sockets
SELECT DISTINCT p.local_address, p.local_port, p.remote_address, p.remote_port, p.protocol, p.state, c.community_id FROM process_open_sockets as p JOIN community_id as c ON c.src_ip= p.local_address AND c.src_port=p.local_port AND c.dst_ip=p.remote_address AND c.dst_port=p.remote_port AND p.protocol !=0 AND p.remote_port !=0 AND c.protocol=p.protocol;
Setup Osquery extension for OsqueryD
- Open a terminal tab
cd /var/osquery
curl
curl https://raw.githubusercontent.com/CptOfEvilMinions/osquery-go-communityid/master/conf/macos/osquery.flags -o /var/osquery/osquery.flags
curl https://raw.githubusercontent.com/CptOfEvilMinions/osquery-go-communityid/master/conf/macos/osquery.conf -o /var/osquery/osquery.conf
curl https://raw.githubusercontent.com/CptOfEvilMinions/osquery-go-communityid/master/conf/macos/extensions.load -o /var/osquery/extensions.load
sudo cp /var/osquery/com.facebook.osqueryd.plist /Library/LaunchDaemons/com.facebook.osqueryd.plist
sudo launchctl load /Library/LaunchDaemons/com.facebook.osqueryd.plist
sudo tail -f /var/log/osquery/osqueryd.results.log
Discussion
Github PR for a communityID table
On Tuesday February 4th 2020, I participated in the Osquery office hours where zwass debuted his new PR to the Osquery project for communityID generation. So the timing of my blog post and this PR are very timely. Nonetheless, this exercise was a good learning experience and I hope this blog post is helpful for developing Osquery extensions with golang.
Cross-platform
As you probably noticed above, osquery-go allows for cross-platform compilation on any platform for any platform. Using golang allows for a simple build pipeline because you could use a golang Docker container to build the executable for any platform.
Dependency on Polylogyx for Windows
This blog post relies on the Osquery-Polylogyx extension pack to monitor Windows socket events because Osquery does not provide this functionality. The Osquery-Polylogyx extension pack is a work of art in my opinion and a pinnacle example of threat detection engineering. However, the project only works with version 3.2.6 and 4.0.2 of Osquery and it requires Windows Defender to be disabled. I will make the assumption that if your environment has an EDR product like Carbon Black it will block this for the same reasons as Windows Defender. However, if you can’t afford an EDR product this will get you as close as possible for free but without the prevention capabilities.
Hardware resource consumption
In the previous blog post, I created an osquery-python extension for Windows that consumed 50% of the CPU utilization when idle. I am happy to report that the osquery-go equivalent only consumes less than 1% of CPU and 10MB at idle.
Lessons learned
I am currently reading a book called “Cracking the Coding Interview” and it is a great book!!! One interesting part of the book is their matrix to describe projects you worked on and the matrix contains the following sections which are: challenges, mistakes/failures, enjoyed, leadership, conflicts, and what would you’d do differently. I am going to try and use this model at the end of my blog posts moving forward to summarize and reflect on things I learned but also the challenges. The purpose of this lesson learned section is to distill the experiences from my security research project that should be actively taken into account in future projects and to share the knowledge of my journey.
Challenges
- The biggest challenge of this blog post was learning a new programming language which was golang.
- Figuring out how to get the Polylogyx extensions working on Windows.
- Expressing the golang code in words
- Reading other people’s golang code to understand the osquery-go capabilities
Mistakes/Failures
- Not implementing TravisCI to automatically build new binaries based on new commits
Enjoyed
- I loved that I got to engineer a new security mechanism for my favorite open source project
- Learning how to read golang structs to extract values from the queryContext variable provided by osquery-go
- I frustratedly enjoyed learning how to enforce handling errors in golang. With languages like Python, the error is often abstracted away until it happens but golang enforces you to handle errors and this is helping me learn how to handle different error scenarios.
- Implemented a successful golang project with a “go mod” – requirements.txt equivalent in Python