Compare commits
246 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c849c57435 | ||
|
|
e4254bec17 | ||
|
|
980e6b55f4 | ||
|
|
2712bdaeea | ||
|
|
c0a63b0620 | ||
|
|
12754d1c7a | ||
|
|
0ae36e4976 | ||
|
|
2972aa2480 | ||
|
|
874b317c95 | ||
|
|
ca66674f33 | ||
|
|
6c81fe72b0 | ||
|
|
89402fe6e8 | ||
|
|
745cd01419 | ||
|
|
e3105165e0 | ||
|
|
a37fc3e0b3 | ||
|
|
cbb8ca675e | ||
|
|
7f622a8c24 | ||
|
|
b9d990caae | ||
|
|
6856f3333f | ||
|
|
653651555f | ||
|
|
d973bcc796 | ||
|
|
d772d82d57 | ||
|
|
0d722837db | ||
|
|
8695d4490d | ||
|
|
3b7b78ed4d | ||
|
|
c6ac0ede23 | ||
|
|
bfb06499c9 | ||
|
|
9a8830fa53 | ||
|
|
f00c1250f1 | ||
|
|
9247d0fded | ||
|
|
83ad8c355f | ||
|
|
98bfee2778 | ||
|
|
dd0966883d | ||
|
|
3b9fe5ba6c | ||
|
|
8f38f80ac6 | ||
|
|
df7c3b69f4 | ||
|
|
cf7eb20d95 | ||
|
|
8a4b8be0e7 | ||
|
|
b97aeda086 | ||
|
|
064b3d7c01 | ||
|
|
3d2f80c9bb | ||
|
|
686e1e5119 | ||
|
|
913e9a9f4f | ||
|
|
7a1cf34646 | ||
|
|
ed5e913275 | ||
|
|
be2e859efd | ||
|
|
ef711c4fae | ||
|
|
e8a8072349 | ||
|
|
592d697888 | ||
|
|
f8807b7a60 | ||
|
|
40b97d74b4 | ||
|
|
4b8cc88871 | ||
|
|
3136ed522e | ||
|
|
5cb3460ef4 | ||
|
|
bfe589e5a0 | ||
|
|
a29d3b1739 | ||
|
|
f01f299e02 | ||
|
|
b0f25110a3 | ||
|
|
494e503d84 | ||
|
|
27608f9bdd | ||
|
|
b429355a46 | ||
|
|
0b9474fdbd | ||
|
|
97ea7daef8 | ||
|
|
a39c901b2f | ||
|
|
8ccccea367 | ||
|
|
5df4f180e5 | ||
|
|
b49d97e55c | ||
|
|
570c6b4225 | ||
|
|
6fbebd8544 | ||
|
|
86c2b91c73 | ||
|
|
db2203b286 | ||
|
|
1772d8b9e3 | ||
|
|
9814167212 | ||
|
|
5b039fe0eb | ||
|
|
9d414da7d4 | ||
|
|
b828f2d6f0 | ||
|
|
06ae0958ec | ||
|
|
2747e5a1c5 | ||
|
|
6e210acc90 | ||
|
|
653b1bd340 | ||
|
|
0b719b832c | ||
|
|
7dcb950899 | ||
|
|
41e8b4f047 | ||
|
|
145b6dfa9a | ||
|
|
5368ef3e52 | ||
|
|
a95ba52e97 | ||
|
|
e578b4eea7 | ||
|
|
b9e920d890 | ||
|
|
765a9d161c | ||
|
|
fcd8867a15 | ||
|
|
0928ea12c6 | ||
|
|
641f46892b | ||
|
|
776d02e8cc | ||
|
|
0db0dd263e | ||
|
|
73ac953ee4 | ||
|
|
3b4381cf3b | ||
|
|
3c853b83d4 | ||
|
|
93d6fb12a7 | ||
|
|
2f4fa798c1 | ||
|
|
426ec77eb3 | ||
|
|
40efb237d7 | ||
|
|
ffc8ce6a3c | ||
|
|
8f16948443 | ||
|
|
3a84395551 | ||
|
|
d8923f4b46 | ||
|
|
699c242136 | ||
|
|
347da4825c | ||
|
|
55cba1e04d | ||
|
|
f6559668c2 | ||
|
|
b42323d63c | ||
|
|
e3ee1a7f20 | ||
|
|
75c888c473 | ||
|
|
5735af29b2 | ||
|
|
212aa9601e | ||
|
|
5582892763 | ||
|
|
7b91e6f872 | ||
|
|
d77b2b39e0 | ||
|
|
22aaf3c0b1 | ||
|
|
1099b061bb | ||
|
|
795688ecc9 | ||
|
|
aafa53c66b | ||
|
|
316b0e1cd2 | ||
|
|
f6bc1b2c08 | ||
|
|
93c6c45e28 | ||
|
|
facd5290e0 | ||
|
|
72354066b0 | ||
|
|
41270f6611 | ||
|
|
52db46c340 | ||
|
|
a1fb2932e2 | ||
|
|
39c1e9b8c6 | ||
|
|
9ffdbef805 | ||
|
|
c80f2a1ed2 | ||
|
|
3b83fc0075 | ||
|
|
891e7a1ec5 | ||
|
|
4c61df7638 | ||
|
|
ccf32813fd | ||
|
|
ff3295f08e | ||
|
|
d0e3ff0a0f | ||
|
|
4b3fc76f0c | ||
|
|
a9d092cede | ||
|
|
56b914be88 | ||
|
|
d495fc4d19 | ||
|
|
1618f777d7 | ||
|
|
cb97809ca1 | ||
|
|
4c8b14a788 | ||
|
|
bda14a9a7b | ||
|
|
53b37120f8 | ||
|
|
3860d752f9 | ||
|
|
00ec57ac24 | ||
|
|
068462c2aa | ||
|
|
73f4fc0bb4 | ||
|
|
ce1d1b3652 | ||
|
|
2acdea73f1 | ||
|
|
fe2dae484e | ||
|
|
9cf37f3d5f | ||
|
|
c513c06a85 | ||
|
|
af0244b9f7 | ||
|
|
71cad5045b | ||
|
|
90e3986815 | ||
|
|
772cd79612 | ||
|
|
4dbd84d196 | ||
|
|
2fc28900c4 | ||
|
|
bf481757b7 | ||
|
|
638eb0e436 | ||
|
|
bf2a4afb19 | ||
|
|
23331bdad8 | ||
|
|
7bc7030e5c | ||
|
|
90b0c1c3eb | ||
|
|
70624e7a79 | ||
|
|
4313f6d102 | ||
|
|
813f0b0457 | ||
|
|
adc49c3238 | ||
|
|
f89c1bae9e | ||
|
|
50af0057aa | ||
|
|
a6db483479 | ||
|
|
97f2ac0ae8 | ||
|
|
0dd2fd08b8 | ||
|
|
d871fbafb2 | ||
|
|
d2a2e22ef4 | ||
|
|
70275885fe | ||
|
|
9b4dfb0f62 | ||
|
|
b0800939a1 | ||
|
|
b9aa341e98 | ||
|
|
241b52d63d | ||
|
|
a9e07a2c12 | ||
|
|
4b17db9a3f | ||
|
|
1d78a6986a | ||
|
|
9df00a229b | ||
|
|
0c1ae505bf | ||
|
|
909f338222 | ||
|
|
0dca8a3737 | ||
|
|
e40f19ed90 | ||
|
|
049a27618e | ||
|
|
5a9bcb63a5 | ||
|
|
b2cec3a65a | ||
|
|
bbe1779478 | ||
|
|
288775050d | ||
|
|
dbf838ad9c | ||
|
|
f674e5346d | ||
|
|
189ddb870e | ||
|
|
252dc78766 | ||
|
|
65e5515270 | ||
|
|
ed6d614a61 | ||
|
|
88fe6b9b77 | ||
|
|
751c75b0c1 | ||
|
|
15660e1d87 | ||
|
|
bb41112c50 | ||
|
|
77e274c30f | ||
|
|
80e3528531 | ||
|
|
95a5389082 | ||
|
|
ee8026ae09 | ||
|
|
3f9f03b858 | ||
|
|
ae973da31d | ||
|
|
abc34860c7 | ||
|
|
6669e3bf26 | ||
|
|
d063e8a5f1 | ||
|
|
f865ce0059 | ||
|
|
a9ab411076 | ||
|
|
568f7f0f70 | ||
|
|
aea38f9db5 | ||
|
|
85fb4dc2be | ||
|
|
e266d6d0cb | ||
|
|
ad862ac476 | ||
|
|
dc3875b1a9 | ||
|
|
ca16c87bba | ||
|
|
51caec0406 | ||
|
|
dbb75924d0 | ||
|
|
6f00708c60 | ||
|
|
4d72b4523b | ||
|
|
95fc6fd0da | ||
|
|
174907c37b | ||
|
|
f382c50ec8 | ||
|
|
d961417282 | ||
|
|
36120d07d4 | ||
|
|
8e8509806c | ||
|
|
70836aa8b2 | ||
|
|
4b854221fb | ||
|
|
9f1f259330 | ||
|
|
d77917943f | ||
|
|
b98f704eb8 | ||
|
|
f31a6ec9a6 | ||
|
|
7ed7f90e0b | ||
|
|
fdc361c630 | ||
|
|
a15e002f98 | ||
|
|
98606aa782 | ||
|
|
a1448eda64 |
@@ -1,6 +1,9 @@
|
||||
target
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
docker
|
||||
docs
|
||||
ci
|
||||
.git
|
||||
.gitignore
|
||||
*.sw[op]
|
||||
|
||||
58
.travis.yml
Normal file
58
.travis.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
language: rust
|
||||
|
||||
# upload takes too long on windows and gets killed due to inactivity
|
||||
#cache: cargo
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=test
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=common
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=boxxy
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=docker
|
||||
- os: linux
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=docker-registry
|
||||
- os: osx
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=test
|
||||
- os: osx
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE=common
|
||||
#- os: windows
|
||||
# rust: stable
|
||||
# env:
|
||||
# - BUILD_MODE="windows test"
|
||||
- os: windows
|
||||
rust: stable
|
||||
env:
|
||||
- BUILD_MODE="windows common"
|
||||
|
||||
before_install:
|
||||
- ci/setup.sh "$TRAVIS_OS_NAME"
|
||||
script:
|
||||
- df -h
|
||||
- ci/run.sh $BUILD_MODE
|
||||
- df -h
|
||||
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "ircs://irc.hackint.org:6697/#sn0int"
|
||||
#on_success: change # default: always
|
||||
#on_failure: always # default: always
|
||||
use_notice: true
|
||||
32
CONTRIBUTING.md
Normal file
32
CONTRIBUTING.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# How to contribute
|
||||
|
||||
To contribute to sn0int, clone the repository and make sure both the build and
|
||||
tests pass for you:
|
||||
|
||||
git clone https://github.com/kpcyrd/sn0int.git
|
||||
cd sn0int
|
||||
# build the project
|
||||
cargo build
|
||||
# run regular tests
|
||||
cargo test
|
||||
# run tests depending on the network
|
||||
# these might fail if a service is down
|
||||
cargo test -- --ignored
|
||||
|
||||
The project is loosely structured into a few folders:
|
||||
|
||||
- `src/models/` - database models
|
||||
- `src/runtime/` - the stdlib that's exposed to lua
|
||||
- `src/engine/` - code related to lua
|
||||
- `src/sandbox/` - code related to sandboxing
|
||||
- `src/cmd/` - cli commands
|
||||
- `src/` - misc modules
|
||||
|
||||
After you're done, make sure the build completes without any warnings and both
|
||||
tests pass successfully:
|
||||
|
||||
cargo test
|
||||
cargo test -- --ignored
|
||||
|
||||
If you want to introduce a new feature feel free to open an issue first to make
|
||||
sure your feature is a good fit for the project before implementing it.
|
||||
2581
Cargo.lock
generated
2581
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
58
Cargo.toml
58
Cargo.toml
@@ -1,50 +1,76 @@
|
||||
[package]
|
||||
name = "sn0int"
|
||||
version = "0.1.0"
|
||||
description = "OSINT framework and package manager"
|
||||
version = "0.9.1"
|
||||
description = "Semi-automatic OSINT framework and package manager"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
license = "GPL-3.0"
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
categories = ["command-line-utilities"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "kpcyrd/sn0int" }
|
||||
|
||||
[workspace]
|
||||
members = ["sn0int-registry/sn0int-common",
|
||||
"sn0int-registry"]
|
||||
|
||||
[dependencies]
|
||||
sn0int-common = { version="0.1.0", path="sn0int-registry/sn0int-common" }
|
||||
rustyline = "2"
|
||||
sn0int-common = { version="0.4.0", path="sn0int-registry/sn0int-common" }
|
||||
rustyline = "3"
|
||||
log = "0.4"
|
||||
env_logger = "0.5"
|
||||
env_logger = "0.6"
|
||||
hlua-badtouch = "0.4"
|
||||
structopt = "0.2"
|
||||
failure = "0.1"
|
||||
rand = "0.5"
|
||||
rand = "0.6"
|
||||
colored = "1.6"
|
||||
lazy_static = "1.0"
|
||||
shellwords = "1.0"
|
||||
publicsuffix = { version="1.5", default-features=false }
|
||||
diesel = { version = "1.0.0", features = ["sqlite"] }
|
||||
diesel = { version = "1.0.0", features = ["sqlite", "chrono"] }
|
||||
diesel_migrations = { version = "1.3.0", features = ["sqlite"] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
dirs = "1.0"
|
||||
url = "1.7"
|
||||
nix = "0.11"
|
||||
caps = "0.3"
|
||||
syscallz = "0.7"
|
||||
chrootable-https = "0.3.3"
|
||||
trust-dns-proto = "0.5.0-alpha.1"
|
||||
base64 = "0.9"
|
||||
#chrootable-https = { path = "../chrootable-https" }
|
||||
chrootable-https = "0.8"
|
||||
base64 = "0.10"
|
||||
kuchiki = "0.7.2"
|
||||
serde_urlencoded = "0.5"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
crossbeam-channel = "0.2"
|
||||
crossbeam-channel = "0.3"
|
||||
ctrlc = "3.1"
|
||||
opener = "0.3.0"
|
||||
separator = "0.3.1"
|
||||
separator = "0.4"
|
||||
maplit = "1.0.1"
|
||||
sloppy-rfc4880 = "0.1.2"
|
||||
regex = "1.0"
|
||||
toml = "0.4"
|
||||
maxminddb = "0.13"
|
||||
tar = "0.4.17"
|
||||
libflate = "0.1.14"
|
||||
threadpool = "1.7"
|
||||
x509-parser = "0.4.0"
|
||||
der-parser = "1.1.0"
|
||||
nom = "4.1.1"
|
||||
atty = "0.2"
|
||||
bufstream = "0.1.4"
|
||||
tokio = "0.1.14"
|
||||
|
||||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
caps = "0.3"
|
||||
#syscallz = { path="../syscallz-rs" }
|
||||
syscallz = "0.10"
|
||||
nix = "0.13"
|
||||
|
||||
[target.'cfg(target_os="openbsd")'.dependencies]
|
||||
pledge = "0.3.1"
|
||||
unveil = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
boxxy = "0.7"
|
||||
#boxxy = { path = "../boxxy-rs" }
|
||||
boxxy = "0.8"
|
||||
|
||||
29
ISSUE_TEMPLATE.md
Normal file
29
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
Hello!
|
||||
|
||||
If you want to report a bug we added some common questions below that help us analyse your issue faster.
|
||||
|
||||
All of these are optional so feel free to remove anything that doesn't apply.
|
||||
-->
|
||||
|
||||
please describe your issue here
|
||||
|
||||
---
|
||||
|
||||
## Versions
|
||||
|
||||
- **rustc --version:**
|
||||
- **cargo --version:**
|
||||
- **sn0int --version:**
|
||||
- **uname -a:**
|
||||
|
||||
## Environment
|
||||
|
||||
- **Operating System/Distro:**
|
||||
- **Installed from (source/apt/pacman/brew/docker):**
|
||||
|
||||
<!--
|
||||
Thank you!
|
||||
|
||||
We'll try to respond as quickly as possible.
|
||||
-->
|
||||
5
Makefile
5
Makefile
@@ -14,3 +14,8 @@ test:
|
||||
(cd sn0int-registry; cargo test)
|
||||
cargo test
|
||||
cargo test -- --ignored
|
||||
|
||||
update:
|
||||
get-oui -v -u http://standards-oui.ieee.org/oui/oui.txt -f data/ieee-oui.txt
|
||||
get-iab -v -u http://standards-oui.ieee.org/iab/iab.txt -f data/ieee-iab.txt
|
||||
rm -f data/ieee-*.txt.bak
|
||||
|
||||
127
README.md
127
README.md
@@ -1,6 +1,129 @@
|
||||
# sn0int
|
||||
# sn0int [![Build Status][travis-img]][travis] [![Crates.io][crates-img]][crates] [![Documentation Status][docs-img]][docs]
|
||||
|
||||
work in progress
|
||||
[travis-img]: https://travis-ci.org/kpcyrd/sn0int.svg?branch=master
|
||||
[travis]: https://travis-ci.org/kpcyrd/sn0int
|
||||
[crates-img]: https://img.shields.io/crates/v/sn0int.svg
|
||||
[crates]: https://crates.io/crates/sn0int
|
||||
[docs-img]: https://readthedocs.org/projects/sn0int/badge/?version=latest
|
||||
[docs]: https://sn0int.readthedocs.io/en/latest/?badge=latest
|
||||
|
||||
sn0int is a semi-automatic OSINT framework and package manager. It was built
|
||||
for IT security professionals and bug hunters to gather intelligence about a
|
||||
given target or about yourself. sn0int is enumerating attack surface by
|
||||
semi-automatically processing public information and mapping the results in a
|
||||
unified format for followup investigations.
|
||||
|
||||
Among other things, sn0int is currently able to:
|
||||
|
||||
- Harvest subdomains from certificate transparency logs
|
||||
- Harvest subdomains from various passive dns logs
|
||||
- Sift through subdomain results for publicly accessible websites
|
||||
- Harvest emails from pgp keyservers
|
||||
- Enrich ip addresses with ASN and geoip info
|
||||
- Harvest subdomains from the wayback machine
|
||||
- Gather information about phonenumbers
|
||||
- Bruteforce interesting urls
|
||||
|
||||
sn0int is heavily inspired by recon-ng and maltego, but remains more flexible
|
||||
and is fully opensource. None of the investigations listed above are hardcoded
|
||||
in the source, instead those are provided by modules that are executed in a
|
||||
sandbox. You can easily extend sn0int by writing your own modules and share
|
||||
them with other users by publishing them to the sn0int registry. This allows
|
||||
you to ship updates for your modules on your own since you don't need to send a
|
||||
pull request.
|
||||
|
||||
Join us on IRC: [irc.hackint.org:6697/#sn0int](https://webirc.hackint.org/#irc://irc.hackint.org/#sn0int)
|
||||
|
||||
[](https://asciinema.org/a/shZ3TVY1o0opGFln3Oi2DAMCB)
|
||||
|
||||
## Getting started
|
||||
|
||||
- [Installation](https://sn0int.readthedocs.io/en/latest/install.html)
|
||||
- [Archlinux](https://sn0int.readthedocs.io/en/latest/install.html#archlinux)
|
||||
- [Mac OSX](https://sn0int.readthedocs.io/en/latest/install.html#mac-osx)
|
||||
- [Debian testing/Debian sid/Kali](https://sn0int.readthedocs.io/en/latest/install.html#debian-testing-debian-sid-kali)
|
||||
- [Ubuntu/Debian stable](https://sn0int.readthedocs.io/en/latest/install.html#ubuntu-debian-stable)
|
||||
- [Docker](https://sn0int.readthedocs.io/en/latest/install.html#docker)
|
||||
- [Alpine](https://sn0int.readthedocs.io/en/latest/install.html#alpine)
|
||||
- [OpenBSD](https://sn0int.readthedocs.io/en/latest/install.html#openbsd)
|
||||
- [Windows](https://sn0int.readthedocs.io/en/latest/install.html#windows)
|
||||
- [Running your first investigation](https://sn0int.readthedocs.io/en/latest/usage.html)
|
||||
- [Installing the default modules](https://sn0int.readthedocs.io/en/latest/usage.html#installing-the-default-modules)
|
||||
- [Adding something to scope](https://sn0int.readthedocs.io/en/latest/usage.html#adding-something-to-scope)
|
||||
- [Running a module](https://sn0int.readthedocs.io/en/latest/usage.html#running-a-module)
|
||||
- [Running followup modules on the results](https://sn0int.readthedocs.io/en/latest/usage.html#running-followup-modules-on-the-results)
|
||||
- [Unscoping entities](https://sn0int.readthedocs.io/en/latest/usage.html#unscoping-entities)
|
||||
- [Scripting](https://sn0int.readthedocs.io/en/latest/scripting.html)
|
||||
- [Write your first module](https://sn0int.readthedocs.io/en/latest/scripting.html#write-your-first-module)
|
||||
- [Publish your module](https://sn0int.readthedocs.io/en/latest/scripting.html#publish-your-module)
|
||||
- [Reading data from stdin](https://sn0int.readthedocs.io/en/latest/scripting.html#reading-data-from-stdin)
|
||||
- [Database](https://sn0int.readthedocs.io/en/latest/database.html)
|
||||
- [db_add](https://sn0int.readthedocs.io/en/latest/database.html#db-add)
|
||||
- [db_update](https://sn0int.readthedocs.io/en/latest/database.html#db-update)
|
||||
- [db_select](https://sn0int.readthedocs.io/en/latest/database.html#db-select)
|
||||
- [Keyring](https://sn0int.readthedocs.io/en/latest/keyring.html)
|
||||
- [Managing the keyring](https://sn0int.readthedocs.io/en/latest/keyring.html#managing-the-keyring)
|
||||
- [Using access keys in scripts](https://sn0int.readthedocs.io/en/latest/keyring.html#using-access-keys-in-scripts)
|
||||
- [Using access keys as source argument](https://sn0int.readthedocs.io/en/latest/keyring.html#using-access-keys-as-source-argument)
|
||||
- [Configuration](https://sn0int.readthedocs.io/en/latest/config.html)
|
||||
- [Configuring a proxy](https://sn0int.readthedocs.io/en/latest/config.html#configuring-a-proxy)
|
||||
- [Sandbox](https://sn0int.readthedocs.io/en/latest/sandbox.html)
|
||||
- [Linux](https://sn0int.readthedocs.io/en/latest/sandbox.html#linux)
|
||||
- [OpenBSD](https://sn0int.readthedocs.io/en/latest/sandbox.html#openbsd)
|
||||
- [IPC Protocol](https://sn0int.readthedocs.io/en/latest/sandbox.html#ipc-protocol)
|
||||
- [Limitations](https://sn0int.readthedocs.io/en/latest/sandbox.html#limitations)
|
||||
- [Diagnosing a sandbox failure](https://sn0int.readthedocs.io/en/latest/sandbox.html#diagnosing-a-sandbox-failure)
|
||||
- [Function reference](https://sn0int.readthedocs.io/en/latest/reference.html)
|
||||
- [clear_err](https://sn0int.readthedocs.io/en/latest/reference.html#clear-err)
|
||||
- [datetime](https://sn0int.readthedocs.io/en/latest/reference.html#datetime)
|
||||
- [db_add](https://sn0int.readthedocs.io/en/latest/reference.html#db-add)
|
||||
- [db_add_ttl](https://sn0int.readthedocs.io/en/latest/reference.html#db-add-ttl)
|
||||
- [db_select](https://sn0int.readthedocs.io/en/latest/reference.html#db-select)
|
||||
- [db_update](https://sn0int.readthedocs.io/en/latest/reference.html#db-update)
|
||||
- [dns](https://sn0int.readthedocs.io/en/latest/reference.html#dns)
|
||||
- [error](https://sn0int.readthedocs.io/en/latest/reference.html#error)
|
||||
- [asn_lookup](https://sn0int.readthedocs.io/en/latest/reference.html#asn-lookup)
|
||||
- [geoip_lookup](https://sn0int.readthedocs.io/en/latest/reference.html#geoip-lookup)
|
||||
- [html_select](https://sn0int.readthedocs.io/en/latest/reference.html#html-select)
|
||||
- [html_select_list](https://sn0int.readthedocs.io/en/latest/reference.html#html-select-list)
|
||||
- [http_mksession](https://sn0int.readthedocs.io/en/latest/reference.html#http-mksession)
|
||||
- [http_request](https://sn0int.readthedocs.io/en/latest/reference.html#http-request)
|
||||
- [http_send](https://sn0int.readthedocs.io/en/latest/reference.html#http-send)
|
||||
- [info](https://sn0int.readthedocs.io/en/latest/reference.html#info)
|
||||
- [json_decode](https://sn0int.readthedocs.io/en/latest/reference.html#json-decode)
|
||||
- [json_decode_stream](https://sn0int.readthedocs.io/en/latest/reference.html#json-decode-stream)
|
||||
- [json_encode](https://sn0int.readthedocs.io/en/latest/reference.html#json-encode)
|
||||
- [keyring](https://sn0int.readthedocs.io/en/latest/reference.html#keyring)
|
||||
- [last_err](https://sn0int.readthedocs.io/en/latest/reference.html#last-err)
|
||||
- [pgp_pubkey](https://sn0int.readthedocs.io/en/latest/reference.html#pgp-pubkey)
|
||||
- [pgp_pubkey_armored](https://sn0int.readthedocs.io/en/latest/reference.html#pgp-pubkey-armored)
|
||||
- [print](https://sn0int.readthedocs.io/en/latest/reference.html#print)
|
||||
- [psl_domain_from_dns_name](https://sn0int.readthedocs.io/en/latest/reference.html#psl-domain-from-dns-name)
|
||||
- [regex_find](https://sn0int.readthedocs.io/en/latest/reference.html#regex-find)
|
||||
- [regex_find_all](https://sn0int.readthedocs.io/en/latest/reference.html#regex-find-all)
|
||||
- [sleep](https://sn0int.readthedocs.io/en/latest/reference.html#sleep)
|
||||
- [sock_connect](https://sn0int.readthedocs.io/en/latest/reference.html#sock-connect)
|
||||
- [sock_send](https://sn0int.readthedocs.io/en/latest/reference.html#sock-send)
|
||||
- [sock_recv](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recv)
|
||||
- [sock_sendline](https://sn0int.readthedocs.io/en/latest/reference.html#sock-sendline)
|
||||
- [sock_recvline](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvline)
|
||||
- [sock_recvall](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvall)
|
||||
- [sock_recvline_contains](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvline-contains)
|
||||
- [sock_recvline_regex](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvline-regex)
|
||||
- [sock_recvn](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvn)
|
||||
- [sock_recvuntil](https://sn0int.readthedocs.io/en/latest/reference.html#sock-recvuntil)
|
||||
- [sock_sendafter](https://sn0int.readthedocs.io/en/latest/reference.html#sock-sendafter)
|
||||
- [sock_newline](https://sn0int.readthedocs.io/en/latest/reference.html#sock-newline)
|
||||
- [status](https://sn0int.readthedocs.io/en/latest/reference.html#status)
|
||||
- [stdin_readline](https://sn0int.readthedocs.io/en/latest/reference.html#stdin-readline)
|
||||
- [url_decode](https://sn0int.readthedocs.io/en/latest/reference.html#url-decode)
|
||||
- [url_encode](https://sn0int.readthedocs.io/en/latest/reference.html#url-encode)
|
||||
- [url_escape](https://sn0int.readthedocs.io/en/latest/reference.html#url-escape)
|
||||
- [url_join](https://sn0int.readthedocs.io/en/latest/reference.html#url-join)
|
||||
- [url_parse](https://sn0int.readthedocs.io/en/latest/reference.html#url-parse)
|
||||
- [url_unescape](https://sn0int.readthedocs.io/en/latest/reference.html#url-unescape)
|
||||
- [utf8_decode](https://sn0int.readthedocs.io/en/latest/reference.html#utf8-decode)
|
||||
- [x509_parse_pem](https://sn0int.readthedocs.io/en/latest/reference.html#x509-parse-pem)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
34
ci/bench.sh
Executable file
34
ci/bench.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
X=$(mktemp -d)
|
||||
cd "$X"
|
||||
|
||||
mkdir -p "$X/.cache"
|
||||
cp -r "$HOME/.cache/sn0int" "$X/.cache/"
|
||||
|
||||
#export CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
|
||||
#export RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}"
|
||||
export HOME="$X"
|
||||
|
||||
cat > 1k.lua <<EOF
|
||||
-- Description: Insert 1k random subdomains
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
for i=1,1000 do
|
||||
x = 'foo' .. i .. '.example.com'
|
||||
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=x,
|
||||
})
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
echo '[*] Setting up workspace'
|
||||
echo 'add domain example.com' | "$@" > /dev/null
|
||||
echo '[*] Running 1k inserts'
|
||||
time "$@" run -f ./1k.lua
|
||||
5
ci/boxxy_stage1.txt
Normal file
5
ci/boxxy_stage1.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
stage1
|
||||
ls
|
||||
echo checkpoint1
|
||||
id
|
||||
echo sandbox fail
|
||||
123
ci/integration.py
Executable file
123
ci/integration.py
Executable file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
from subprocess import DEVNULL, PIPE
|
||||
import tempfile
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def _sn0int(tempdir, binary, args, piped_stdout=False):
|
||||
return subprocess.Popen(
|
||||
['/usr/bin/env', 'HOME='+tempdir, binary] + args,
|
||||
stdin=PIPE,
|
||||
stdout=PIPE if piped_stdout else None,
|
||||
)
|
||||
|
||||
|
||||
def sn0int(tempdir, binary, cmds):
|
||||
p = _sn0int(tempdir, binary, [])
|
||||
for cmd in cmds:
|
||||
p.stdin.write((cmd + '\n').encode('utf-8'))
|
||||
p.communicate()
|
||||
if p.returncode != 0:
|
||||
raise Exception('process failed')
|
||||
|
||||
|
||||
def sn0int_select(tempdir, binary, query):
|
||||
p = _sn0int(tempdir, binary, ['select', '--json'] + query, piped_stdout=True)
|
||||
stdout, _ = p.communicate()
|
||||
lines = filter(None, stdout.decode('utf-8').split('\n'))
|
||||
return [json.loads(x) for x in lines]
|
||||
|
||||
|
||||
def main(tempdir, binary):
|
||||
print('[*] setting up workspace')
|
||||
sn0int(tempdir, binary, [])
|
||||
|
||||
print('[*] adding domain')
|
||||
sn0int(tempdir, binary, [
|
||||
'add domain',
|
||||
'example.com',
|
||||
'select domains',
|
||||
])
|
||||
|
||||
print('[*] testing db for domain')
|
||||
domains = sn0int_select(tempdir, binary, ['domains'])
|
||||
assert domains == [{'id': 1, 'value': 'example.com', 'unscoped': False}]
|
||||
|
||||
print('[*] installing modules')
|
||||
sn0int(tempdir, binary, [
|
||||
'mod install kpcyrd/ctlogs',
|
||||
'mod install kpcyrd/dns-resolve',
|
||||
'mod install kpcyrd/url-scan',
|
||||
'mod install kpcyrd/geoip',
|
||||
])
|
||||
|
||||
print('[*] running ctlogs')
|
||||
sn0int(tempdir, binary, [
|
||||
'use ctlogs',
|
||||
'run',
|
||||
'select subdomains',
|
||||
])
|
||||
|
||||
print('[*] testing db for subdomains')
|
||||
subdomains = sn0int_select(tempdir, binary, ['subdomains'])
|
||||
assert {x['value'] for x in subdomains} == {
|
||||
'www.example.com',
|
||||
'm.example.com',
|
||||
'dev.example.com',
|
||||
'products.example.com',
|
||||
'support.example.com',
|
||||
}
|
||||
|
||||
print('[*] running dns-resolve')
|
||||
sn0int(tempdir, binary, [
|
||||
'use dns-resolve',
|
||||
'run',
|
||||
'select ipaddrs',
|
||||
])
|
||||
|
||||
print('[*] testing db for ipaddrs')
|
||||
ipaddrs = sn0int_select(tempdir, binary, ['ipaddrs'])
|
||||
assert len(ipaddrs) >= 1
|
||||
|
||||
print('[*] running url-scan')
|
||||
sn0int(tempdir, binary, [
|
||||
'use url-scan',
|
||||
'run',
|
||||
'select urls',
|
||||
])
|
||||
|
||||
print('[*] testing db for urls')
|
||||
urls = sn0int_select(tempdir, binary, ['urls'])
|
||||
assert {(x['value'], x['status']) for x in urls} == {
|
||||
('http://www.example.com/', 200),
|
||||
('https://www.example.com/', 200),
|
||||
}
|
||||
|
||||
print('[*] running geoip')
|
||||
sn0int(tempdir, binary, [
|
||||
'use geoip',
|
||||
'run',
|
||||
'select ipaddrs',
|
||||
])
|
||||
|
||||
print('[*] testing db for ipaddrs again')
|
||||
ipaddrs2 = sn0int_select(tempdir, binary, ['ipaddrs'])
|
||||
assert ipaddrs != ipaddrs2
|
||||
|
||||
print('')
|
||||
print('\t###########')
|
||||
print('\t# SUCCESS #')
|
||||
print('\t###########')
|
||||
print('')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
binary = sys.argv[1]
|
||||
except IndexError:
|
||||
print('Usage: %s target/release/sn0int' % sys.argv[0])
|
||||
else:
|
||||
with tempfile.TemporaryDirectory(prefix='sn0int-') as tempdir:
|
||||
main(tempdir, binary)
|
||||
18
ci/reprotest.sh
Executable file
18
ci/reprotest.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
set -xue
|
||||
|
||||
# tested with rustc 1.30.0 and cargo 1.30.0
|
||||
|
||||
# by default, the build folder is located in /tmp, which is a tmpfs. The target/ folder
|
||||
# can become quite large, causing the build to fail if we don't have enough RAM.
|
||||
export TMPDIR="$HOME/tmp/repro-test"
|
||||
mkdir -p "$TMPDIR"
|
||||
|
||||
# fileordering: https://github.com/diesel-rs/diesel/pull/1902
|
||||
# build_path: https://github.com/briansmith/ring/issues/715
|
||||
|
||||
reprotest -vv --vary=-time,-domain_host,-fileordering,-build_path --source-pattern 'Cargo.* src/ sn0int-registry/ migrations/' '
|
||||
CARGO_HOME="$PWD/.cargo" RUSTUP_HOME='"$HOME/.rustup"' \
|
||||
RUSTFLAGS="--remap-path-prefix=$HOME=/remap-home --remap-path-prefix=$PWD=/remap-pwd" \
|
||||
cargo build --release --verbose --locked' \
|
||||
target/release/sn0int
|
||||
41
ci/run.sh
Executable file
41
ci/run.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
set -exu
|
||||
case "$1" in
|
||||
build)
|
||||
cargo build --verbose
|
||||
cargo build --verbose --examples
|
||||
;;
|
||||
test)
|
||||
ci/run.sh build
|
||||
wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz \
|
||||
https://geolite.maxmind.com/download/geoip/database/GeoLite2-ASN.tar.gz
|
||||
cargo run --example maxmind -- dl -e GeoLite2-City.tar.gz GeoLite2-City.mmdb GeoLite2-City.mmdb
|
||||
cargo run --example maxmind -- dl -e GeoLite2-ASN.tar.gz GeoLite2-ASN.mmdb GeoLite2-ASN.mmdb
|
||||
cargo test --verbose
|
||||
cargo test --verbose -- --ignored
|
||||
;;
|
||||
common)
|
||||
cd sn0int-registry/sn0int-common
|
||||
cargo test --verbose
|
||||
;;
|
||||
windows)
|
||||
export SQLITE3_LIB_DIR="$TRAVIS_BUILD_DIR"
|
||||
ci/run.sh "$2"
|
||||
;;
|
||||
boxxy)
|
||||
cargo build --verbose --examples
|
||||
if cat ci/boxxy_stage1.txt | RUST_LOG=boxxy cargo run --example boxxy; then
|
||||
echo "SANDOX ERROR: should've crashed"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
docker)
|
||||
docker build -t sn0int .
|
||||
docker images
|
||||
docker run --rm sn0int --help
|
||||
;;
|
||||
docker-registry)
|
||||
docker build -t sn0int-registry sn0int-registry/
|
||||
docker images
|
||||
;;
|
||||
esac
|
||||
13
ci/setup.sh
Executable file
13
ci/setup.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -exu
|
||||
case "$1" in
|
||||
linux)
|
||||
sudo apt update
|
||||
sudo apt install libsqlite3-dev libseccomp-dev
|
||||
;;
|
||||
windows)
|
||||
curl -fsS --retry 3 --retry-connrefused -o sqlite3.zip https://sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip
|
||||
7z e sqlite3.zip -y
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\lib.exe" /def:sqlite3.def /OUT:sqlite3.lib /machine:x64
|
||||
;;
|
||||
esac
|
||||
15
contrib/docker/Dockerfile.alpine
Normal file
15
contrib/docker/Dockerfile.alpine
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM alpine:edge
|
||||
RUN apk add --no-cache sqlite-dev libseccomp-dev
|
||||
RUN apk add --no-cache --virtual .build-rust rust cargo
|
||||
WORKDIR /usr/src/sn0int
|
||||
COPY . .
|
||||
RUN cargo build --release --verbose
|
||||
RUN strip target/release/sn0int
|
||||
|
||||
FROM alpine:edge
|
||||
RUN apk add --no-cache libgcc sqlite-libs libseccomp
|
||||
COPY --from=0 /usr/src/sn0int/target/release/sn0int /usr/local/bin/sn0int
|
||||
VOLUME ["/data", "/cache"]
|
||||
ENV XDG_DATA_HOME=/data \
|
||||
XDG_CACHE_HOME=/cache
|
||||
ENTRYPOINT ["sn0int"]
|
||||
16
contrib/docker/Dockerfile.debian
Normal file
16
contrib/docker/Dockerfile.debian
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM rust
|
||||
RUN apt-get update -q && apt-get install -yq libsqlite3-dev libseccomp-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /usr/src/sn0int
|
||||
COPY . .
|
||||
RUN cargo build --release --verbose
|
||||
RUN strip target/release/sn0int
|
||||
|
||||
FROM debian
|
||||
RUN apt-get update -q && apt-get install -yq libsqlite3-dev libseccomp-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=0 /usr/src/sn0int/target/release/sn0int /usr/local/bin/sn0int
|
||||
VOLUME ["/data", "/cache"]
|
||||
ENV XDG_DATA_HOME=/data \
|
||||
XDG_CACHE_HOME=/cache
|
||||
ENTRYPOINT ["sn0int"]
|
||||
2
contrib/html-toc2md.sh
Executable file
2
contrib/html-toc2md.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
perl -n -e '/toctree-l(\d).*href="([^"]+)">(.+)<\/a/ && print $1==2?" ":"", "- [$3](https://sn0int.readthedocs.io/en/latest/$2)\n"' < docs/_build/html/index.html
|
||||
0
data/.gitkeep
Normal file
0
data/.gitkeep
Normal file
4595
data/ieee-iab.txt
Normal file
4595
data/ieee-iab.txt
Normal file
File diff suppressed because it is too large
Load Diff
25800
data/ieee-oui.txt
Normal file
25800
data/ieee-oui.txt
Normal file
File diff suppressed because it is too large
Load Diff
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/_build/
|
||||
19
docs/Makefile
Normal file
19
docs/Makefile
Normal file
@@ -0,0 +1,19 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
173
docs/conf.py
Normal file
173
docs/conf.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'sn0int'
|
||||
copyright = '2018, kpcyrd'
|
||||
author = 'kpcyrd'
|
||||
|
||||
# The short X.Y version
|
||||
version = ''
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = ''
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# The default sidebars (for documents that don't match any pattern) are
|
||||
# defined by theme itself. Builtin themes are using these templates by
|
||||
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
|
||||
# 'searchbox.html']``.
|
||||
#
|
||||
# html_sidebars = {}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'sn0intdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'sn0int.tex', 'sn0int Documentation',
|
||||
'kpcyrd', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('man', 'sn0int', 'Semi-automatic OSINT framework and package manager',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output ----------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'sn0int', 'sn0int Documentation',
|
||||
author, 'sn0int', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Epub output -------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#
|
||||
# epub_uid = ''
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
17
docs/config.rst
Normal file
17
docs/config.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
Configuration
|
||||
=============
|
||||
|
||||
This file documents the config file at ``~/.config/sn0int.toml``. By default
|
||||
this file does not exist and a default configuration is used instead.
|
||||
|
||||
Configuring a proxy
|
||||
-------------------
|
||||
|
||||
To enable a proxy, add the following to your config file::
|
||||
|
||||
[network]
|
||||
proxy = "127.0.0.1:9050"
|
||||
|
||||
This forces everything through tor and restricts all other functions that
|
||||
depend on the network. For example the ``dns`` function is fully disabled if a
|
||||
proxy is configured.
|
||||
84
docs/database.rst
Normal file
84
docs/database.rst
Normal file
@@ -0,0 +1,84 @@
|
||||
Database
|
||||
========
|
||||
|
||||
There are a few things you need to understand how the database works to use it
|
||||
efficiently.
|
||||
|
||||
The database that is backing sn0int is sqlite, but the api that is exposed to
|
||||
the user and scripts is an nosql-ish object store. The query language that is
|
||||
exposed to the user is still very similar to sql, except that it lacks a column
|
||||
statement::
|
||||
|
||||
select subdomains where value like %.example.com
|
||||
^ ^ ^ ^ ^
|
||||
| | | | this value is going to be quoted automatically
|
||||
| | | |
|
||||
| | | this triggers automatic quoting
|
||||
| | |
|
||||
| | apply a filter, this translates to sql quite literally
|
||||
| |
|
||||
| the entity we want to select is a subdomain.
|
||||
| this affects the table and the deserializer
|
||||
|
|
||||
select entities
|
||||
|
||||
This is how almost all user facing functions work that operate on the database.
|
||||
The functions that are available for scripting are a bit more object based and
|
||||
described below.
|
||||
|
||||
db_add
|
||||
------
|
||||
|
||||
This operation is somewhat straight forward. It adds an entity to the
|
||||
database:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
domain_id = db_add('domain', {
|
||||
value='example.com',
|
||||
})
|
||||
|
||||
If this entity conflicts with an entity that already exists, an upsert is
|
||||
triggered and an db_update is performed instead.
|
||||
|
||||
.. note::
|
||||
This function may return ``nil`` if the entity already exists, but has been
|
||||
removed from scope with ``noscope``. Everytime you use ``db_add`` you need
|
||||
to make sure that the ID that has been returned is not ``nil``.
|
||||
|
||||
db_update
|
||||
---------
|
||||
|
||||
Update some mutable fields of an entity:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
db_update('ipaddr', arg, {
|
||||
asn=lookup['asn'],
|
||||
as_org=lookup['as_org'],
|
||||
})
|
||||
|
||||
The first parameter is usually the same arg that your script was called with.
|
||||
Usually you can use db_add instead of db_update due to the upsert feature, but
|
||||
db_update is still slightly faster.
|
||||
|
||||
.. note::
|
||||
Some fields are immutable and can not be updated.
|
||||
|
||||
db_select
|
||||
---------
|
||||
|
||||
This function is used to check if something is in scope. If the entity has been
|
||||
added to the database and has not been removed from scope, this function
|
||||
returns that entities id. This is somewhat similar to ``db_add``, except that
|
||||
``db_select`` never adds anything to the database.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
domain_id = db_select('domain', 'example.com')
|
||||
if domain_id ~= nil then
|
||||
-- do something
|
||||
end
|
||||
|
||||
This function only accepts a string instead of a lua table. This string is used
|
||||
to filter on the ``value`` column.
|
||||
45
docs/index.rst
Normal file
45
docs/index.rst
Normal file
@@ -0,0 +1,45 @@
|
||||
sn0int
|
||||
======
|
||||
|
||||
sn0int is a semi-automatic OSINT framework and package manager. It was built
|
||||
for IT security professionals and bug hunters to gather intelligence about a
|
||||
given target or about yourself. sn0int is enumerating attack surface by
|
||||
semi-automatically processing public information and mapping the results in a
|
||||
unified format for followup investigations.
|
||||
|
||||
Among other things, sn0int is currently able to:
|
||||
|
||||
- Harvest subdomains from certificate transparency logs
|
||||
- Harvest subdomains from various passive dns logs
|
||||
- Sift through subdomain results for publicly accessible websites
|
||||
- Harvest emails from pgp keyservers
|
||||
- Enrich ip addresses with ASN and geoip info
|
||||
- Harvest subdomains from the wayback machine
|
||||
- Gather information about phonenumbers
|
||||
- Bruteforce interesting urls
|
||||
|
||||
sn0int is heavily inspired by recon-ng and maltego, but remains more flexible
|
||||
and is fully opensource. None of the investigations listed above are hardcoded
|
||||
in the source, instead those are provided by modules that are executed in a
|
||||
sandbox. You can easily extend sn0int by writing your own modules and share
|
||||
them with other users by publishing them to the sn0int registry. This allows
|
||||
you to ship updates for your modules on your own since you don't need to send a
|
||||
pull request.
|
||||
|
||||
Join us on IRC: `irc.hackint.org:6697/#sn0int <https://webirc.hackint.org/#irc://irc.hackint.org/#sn0int>`_
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:glob:
|
||||
|
||||
install
|
||||
usage
|
||||
scripting
|
||||
database
|
||||
keyring
|
||||
config
|
||||
sandbox
|
||||
reference
|
||||
85
docs/install.rst
Normal file
85
docs/install.rst
Normal file
@@ -0,0 +1,85 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
If available, please prefer the package shipped by your linux distribution.
|
||||
|
||||
Archlinux
|
||||
---------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pacman -S sn0int
|
||||
|
||||
Mac OSX
|
||||
-------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ brew install sn0int
|
||||
|
||||
Debian testing/Debian sid/Kali
|
||||
------------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt install build-essential cargo libsqlite3-dev libseccomp-dev
|
||||
$ git clone https://github.com/kpcyrd/sn0int.git
|
||||
$ cd sn0int
|
||||
$ cargo install -f --path .
|
||||
|
||||
Ubuntu/Debian stable
|
||||
--------------------
|
||||
|
||||
cargo in the repos is too old and the build is `going to fail
|
||||
<https://github.com/kpcyrd/sn0int/issues/68>`_. You should either install the
|
||||
most recent rust version with `rustup <https://rustup.rs/>`_ or use the docker
|
||||
instructions instead.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt install build-essential libsqlite3-dev libseccomp-dev
|
||||
$ git clone https://github.com/kpcyrd/sn0int.git
|
||||
$ cd sn0int
|
||||
$ cargo install -f --path .
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ docker run --rm --init -it -v $PWD/.cache:/cache -v $PWD/.data:/data kpcyrd/sn0int
|
||||
|
||||
Alpine
|
||||
------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apk add --no-cache sqlite-dev libseccomp-dev cargo
|
||||
$ git clone https://github.com/kpcyrd/sn0int.git
|
||||
$ cd sn0int
|
||||
$ cargo install -f --path .
|
||||
|
||||
OpenBSD
|
||||
-------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pkg_add sqlite3
|
||||
$ git clone https://github.com/kpcyrd/sn0int.git
|
||||
$ cd sn0int
|
||||
$ cargo install -f --path .
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
This is not recommended and only passively maintained. Please prefer linux in a virtual machine if needed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git clone https://github.com/kpcyrd/sn0int.git
|
||||
$ cd sn0int
|
||||
$ curl -fsS --retry 3 --retry-connrefused -o sqlite3.zip https://sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip
|
||||
$ 7z e sqlite3.zip -y
|
||||
$ "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\lib.exe" /def:sqlite3.def /OUT:sqlite3.lib /machine:x64
|
||||
$ export SQLITE3_LIB_DIR="$TRAVIS_BUILD_DIR"
|
||||
$ cargo install -f --path .
|
||||
73
docs/keyring.rst
Normal file
73
docs/keyring.rst
Normal file
@@ -0,0 +1,73 @@
|
||||
Keyring
|
||||
=======
|
||||
|
||||
A common problem is that you need either an api key or a username/password
|
||||
combination. Instead of hardcoding it in the script you should request them
|
||||
from the keyring. In order to do this you need to request permissions to those
|
||||
credentials.
|
||||
|
||||
Managing the keyring
|
||||
--------------------
|
||||
|
||||
The keyring is a simple namespaced key-value store::
|
||||
|
||||
[sn0int][default] > keyring add aws:AKIAIOSFODNN7EXAMPLE
|
||||
Secretkey: keep-this-secret
|
||||
[sn0int][default] > keyring list
|
||||
aws:AKIAIOSFODNN7EXAMPLE
|
||||
[sn0int][default] >
|
||||
[sn0int][default] > keyring list aws
|
||||
aws:AKIAIOSFODNN7EXAMPLE
|
||||
[sn0int][default] > keyring list instagram
|
||||
[sn0int][default] >
|
||||
[sn0int][default] > keyring get aws:AKIAIOSFODNN7EXAMPLE
|
||||
Namespace: "aws"
|
||||
Access Key: "AKIAIOSFODNN7EXAMPLE"
|
||||
Secret: "keep-this-secret"
|
||||
[sn0int][default] >
|
||||
|
||||
If the service uses a username-password combination, set the username as the
|
||||
access key and the password as the secret.
|
||||
|
||||
If the service uses only a secret key for the api, set the secret key as the
|
||||
access key and leave the secret blank.
|
||||
|
||||
A script doesn't automatically get access to requested keyring namespaces.
|
||||
Instead the user is asked to confirm those requests to limit abusive scripts.
|
||||
|
||||
Using access keys in scripts
|
||||
----------------------------
|
||||
|
||||
We can request all keys of a certain namespace in our script metadata. This is
|
||||
going to prompt the user to grant the script access. This can be done for
|
||||
multiple namespaces in the same script:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- Keyring-Access: aws
|
||||
-- Keyring-Access: asdf
|
||||
|
||||
If the user granted us access to those keys we can read them with ``keyring``:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
creds = keyring('aws')
|
||||
print(creds[1]['accesskey'])
|
||||
print(creds[1]['secretkey'])
|
||||
|
||||
This returns a list of all keys in that namespace. Any empty list is returned
|
||||
if the user doesn't have any keys in that namespace.
|
||||
|
||||
Using access keys as source argument
|
||||
------------------------------------
|
||||
|
||||
We can also use the access keys as source argument. This is useful if each
|
||||
account has access to different things and we want to read through all of them.
|
||||
|
||||
Since access key permissions are granted per namespace we need to specify which
|
||||
credentials we want to use.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- Keyring-Access: aws
|
||||
-- Source: keyring:aws
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
||||
14
docs/man.rst
Normal file
14
docs/man.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
:orphan:
|
||||
|
||||
sn0int
|
||||
======
|
||||
|
||||
todo
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:glob:
|
||||
|
||||
usage
|
||||
config
|
||||
reference
|
||||
696
docs/reference.rst
Normal file
696
docs/reference.rst
Normal file
@@ -0,0 +1,696 @@
|
||||
Function reference
|
||||
==================
|
||||
|
||||
clear_err
|
||||
---------
|
||||
|
||||
Clear the last recorded error from the internal state. See also last_err_.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
if last_err() then
|
||||
-- ignore this error
|
||||
clear_err()
|
||||
end
|
||||
|
||||
datetime
|
||||
--------
|
||||
|
||||
Return current time in UTC. This function is suitable to determine datetimes
|
||||
for ``DATETIME`` database fields.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
now = datetime()
|
||||
|
||||
db_add
|
||||
------
|
||||
|
||||
Add an entity to the database or update it if it already exists. This function
|
||||
may fail or return ``nil``. See `db_add <database.html#db-add>`__ for details.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
domain_id = db_add('domain', {
|
||||
value='example.com',
|
||||
})
|
||||
|
||||
db_add_ttl
|
||||
----------
|
||||
|
||||
Add a temporary entity to the database. This is commonly used to insert
|
||||
temporary links that automatically expire over time. If the entity already
|
||||
exists and is also marked as temporary the new ttl is going to replace the old
|
||||
ttl. If the entity already exists but never expires we are not going to add a
|
||||
ttl.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- this link is valid for 2min
|
||||
domain_id = db_add('network-device', {
|
||||
network_id=1,
|
||||
device_id=13,
|
||||
}, 120)
|
||||
|
||||
db_select
|
||||
---------
|
||||
|
||||
Checks if a target is in scope. If non-nil is returned, this entity is in
|
||||
scope. This function may fail. See `db_select <database.html#db-select>`__ for
|
||||
details.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
domain_id = db_select('domain', 'example.com')
|
||||
if domain_id ~= nil then
|
||||
-- do something
|
||||
end
|
||||
|
||||
db_update
|
||||
---------
|
||||
|
||||
Update an entity in the database. This function may fail. See `db_update
|
||||
<database.html#db-update>`__ for details.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
db_update('ipaddr', arg, {
|
||||
asn=lookup['asn'],
|
||||
as_org=lookup['as_org'],
|
||||
})
|
||||
|
||||
dns
|
||||
---
|
||||
|
||||
Resolve a dns record. If the dns query was successful and the dns reply is
|
||||
``NoError`` then ``x['error']`` is ``nil``. The records of the reply are in
|
||||
``x['answers']``. This function may fail.
|
||||
|
||||
This function accepts the following options:
|
||||
|
||||
``record``
|
||||
The ``query_type``, can be any of ``A``, ``AAAA``, ``MX``, ``AXFR``, etc.
|
||||
``nameserver``
|
||||
The server that should be used for the lookup. Defaults to your system
|
||||
resolver.
|
||||
``tcp``
|
||||
If the lookup should use tcp, true/false.
|
||||
``timeout``
|
||||
The time until the query times out in milliseconds.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
records = dns('example.com', {
|
||||
record='A',
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
.. note::
|
||||
DNS replies with an error code set are not causing a change to
|
||||
``last_err()``. You have to test for this explicitly.
|
||||
|
||||
.. note::
|
||||
This function is unavailable if a socks5 proxy is configured.
|
||||
|
||||
error
|
||||
-----
|
||||
|
||||
Log an error to the terminal.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
error('ohai')
|
||||
|
||||
asn_lookup
|
||||
----------
|
||||
|
||||
Run an ASN lookup for a given ip address. The function returns ``asn`` and
|
||||
``as_org``. This function may fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
lookup = asn_lookup('1.1.1.1')
|
||||
if last_err() then return end
|
||||
|
||||
geoip_lookup
|
||||
------------
|
||||
|
||||
Run a geoip lookup for a given ip address. The function returns:
|
||||
|
||||
- continent
|
||||
- continent_code
|
||||
- country
|
||||
- country_code
|
||||
- city
|
||||
- latitude
|
||||
- longitude
|
||||
|
||||
This function may fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
lookup = geoip_lookup('1.1.1.1')
|
||||
if last_err() then return end
|
||||
|
||||
html_select
|
||||
-----------
|
||||
|
||||
Parses an html document and returns the first element that matches the css
|
||||
selector. The return value is a table with `text` being the inner text and
|
||||
`attrs` being a table of the elements attributes.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
csrf = html_select(html, 'input[name="csrf"]')
|
||||
token = csrf["attrs"]["value"]
|
||||
|
||||
html_select_list
|
||||
----------------
|
||||
|
||||
Same as html_select_ but returns all matches instead of the first one.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
html_select_list(html, 'input[name="csrf"]')
|
||||
|
||||
http_mksession
|
||||
--------------
|
||||
|
||||
Create a session object. This is similar to ``requests.Session`` in
|
||||
python-requests and keeps track of cookies.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
session = http_mksession()
|
||||
|
||||
http_request
|
||||
------------
|
||||
|
||||
Prepares an http request. The first argument is the session reference and
|
||||
cookies from that session are copied into the request. After the request has
|
||||
been sent, the cookies from the response are copied back into the session.
|
||||
|
||||
The next arguments are the ``method``, the ``url`` and additional options.
|
||||
Please note that you still need to specify an empty table ``{}`` even if no
|
||||
options are set. The following options are available:
|
||||
|
||||
``query``
|
||||
A map of query parameters that should be set on the url.
|
||||
``headers``
|
||||
A map of headers that should be set.
|
||||
``basic_auth``
|
||||
Configure the basic auth header with ``{"user, "password"}``.
|
||||
``user_agent``
|
||||
Overwrite the default user agent with a string.
|
||||
``json``
|
||||
The request body that should be json encoded.
|
||||
``form``
|
||||
The request body that should be form encoded.
|
||||
``body``
|
||||
The raw request body as string.
|
||||
|
||||
This function may fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
req = http_request(session, 'POST', 'https://httpbin.org/post', {
|
||||
json={
|
||||
user=user,
|
||||
password=password,
|
||||
}
|
||||
})
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp["status"] ~= 200 then return "invalid status code" end
|
||||
|
||||
http_send
|
||||
---------
|
||||
|
||||
Send the request that has been built with http_request_. Returns a table with
|
||||
the following keys:
|
||||
|
||||
``status``
|
||||
The http status code
|
||||
``headers``
|
||||
A table of headers
|
||||
``text``
|
||||
The response body as string
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
req = http_request(session, 'POST', 'https://httpbin.org/post', {
|
||||
json={
|
||||
user=user,
|
||||
password=password,
|
||||
}
|
||||
})
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp["status"] ~= 200 then return "invalid status code" end
|
||||
|
||||
info
|
||||
----
|
||||
|
||||
Log an info to the terminal.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
info('ohai')
|
||||
|
||||
json_decode
|
||||
-----------
|
||||
|
||||
Decode a lua value from a json string.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
json_decode("{\"data\":{\"password\":\"fizz\",\"user\":\"bar\"},\"list\":[1,3,3,7]}")
|
||||
|
||||
json_decode_stream
|
||||
------------------
|
||||
|
||||
Very similar to json_decode_, but works with multiple json objects directly
|
||||
concatenated to each other or separated by newlines.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
json_decode_stream("{\"data\":1}{\"data\":2}")
|
||||
|
||||
json_encode
|
||||
-----------
|
||||
|
||||
Encode a datastructure into a string.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = json_encode({
|
||||
some=1,
|
||||
fancy={
|
||||
data='structures',
|
||||
}
|
||||
})
|
||||
print(x)
|
||||
|
||||
keyring
|
||||
-------
|
||||
|
||||
Request all keys from a given namespace. See the `keyring <keyring.html>`__
|
||||
section for details.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
creds = keyring('aws')
|
||||
print(creds[1]['accesskey'])
|
||||
print(creds[1]['secretkey'])
|
||||
|
||||
last_err
|
||||
--------
|
||||
|
||||
Returns infos about the last error we've observed, if any. Returns ``nil``
|
||||
otherwise.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
if last_err() then
|
||||
-- Something went wrong, abort
|
||||
return
|
||||
end
|
||||
|
||||
pgp_pubkey
|
||||
----------
|
||||
|
||||
Same as pgp_pubkey_armored_, but without the unarmor step.
|
||||
|
||||
pgp_pubkey_armored
|
||||
------------------
|
||||
|
||||
Extract uids out of a rfc 4880 pgp public key. This function may fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
key = pgp_pubkey_armored([===[
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v2
|
||||
|
||||
mQENBFu6q90BCADgD7Q9aH5683yt7hzPktDkAUNAZJHwYhUNeyGK43frPyDRWQmq
|
||||
N+oXTfiYWLQN+d7KNBTnF9uwyBdaLM7SH44lLNYo8W09mVM2eK+wt19uf5HYNgAE
|
||||
8la45QLo/ce9CQVe1a4oXNWq6l0FOY7M+wLe+G2wMwz8RXGgwd/qQp4/PB5YpUhx
|
||||
nAnzClxvwymrL6BQXsRcKSMSD5bIzIv95n105CvW5Hql7JR9zgOR+gHqVOH8HBUc
|
||||
ZxMumrTM6aKLgAhgM8Sn36gCFOfjlG1b1OFLZhUtgro/nnEOmAurRsCZy8M5h8QM
|
||||
FpZChIH8kgHs90F/CCvGjMq3qvWcH8ZsPUizABEBAAG0NUhhbnMgQWNrZXIgKGV4
|
||||
YW1wbGUgY29tbWVudCkgPGhhbnMuYWNrZXJAZXhhbXBsZS5jb20+iQFOBBMBCAA4
|
||||
FiEEyzeO1eEwbB03hcqBM00IodGdlj8FAlu6q90CGwMFCwkIBwIGFQgJCgsCBBYC
|
||||
AwECHgECF4AACgkQM00IodGdlj/AJQgAjmk+iP5b7Jt7+f+lU4Oprlf3f3DG/uh5
|
||||
Ge6MjV7cvtxlhZJRD5hxGt9RwwnEp61TBSbrem288pM89ilQfTNe0wUr9OzwWzh/
|
||||
8Ngl5iWnD2ah3Mpi5R1V/YMNf2cnwVjqNvfkRHdNc43pZOkC2GoiTUn0QY0UBpOW
|
||||
ZMN3//ANi6ZtiK/L0IZQND/gKvOzu/4tfaJeBl26T3cVYj53p3G3jhlb92vVa8SR
|
||||
uL3S3bzd1h5snDgU1uXHmNHGbhkEc4KUneQ0V9/bdZrg6OzFAfM1ghgfoId+YpQH
|
||||
er9L26ISL3QF58wdEXfIdHYEmMlANjBMO2cUlQXgONuCgkMuY7GBmrkBDQRbuqvd
|
||||
AQgA41jqCumCxYV0NdSYNnTSSDRyd69dOUYCAPT80iZ739s7KKJS9X9KVfGmDjfi
|
||||
u2RcfR/KYj53HoyOm4Pm/+ONN8De4ktzXpIpJxGC+O8NBvd9vkboAS6qnCjK7KVE
|
||||
r91ymxxVKp2dzZvVfpIjWVZR5i2EAvS5vw8UK4gL8ALH+S9leJFZrQWcgyoJOJzH
|
||||
Rzr9pesX2HvdgcNG1O6QUArlsnsTnqpi/hu7tQa8tifBpWDeArOA23Y2DgeehdDF
|
||||
lSU/8KD4J+AkFrWWlcTaMsvSChXQkCHEMRIcSOfXtdpX5KJSE7UBQdD1opm+mR79
|
||||
VeHnuJAAVZZtUZmJA7pjdKykYQARAQABiQE2BBgBCAAgFiEEyzeO1eEwbB03hcqB
|
||||
M00IodGdlj8FAlu6q90CGwwACgkQM00IodGdlj8bMAf+Lq3Qive4vcrCTT4IgvVj
|
||||
arOACdcbtt5RhVBTimT19rDWNH+m+PfPjo3FSlBj5cm70KAXUS2LBFFxhakTZ/Mq
|
||||
cQroWZpVbBxj4kipEVVJZFdUZQaDERJql0xYGOQrNMQ4JGqJ84BRrtOExjSqo41K
|
||||
hAhNe+bwPGH9/Igiixc4tH07xa7TOy4MyJv/6gpbHy/lW1hqpCAgM5fT/im5/6QF
|
||||
k0tED6vIuc54IWiOmwCnjZiQnJ8uCwEu+cuJ5Exwy9CNERLp5v0y4eG+0E+at9j/
|
||||
macOg39qf09t53pTqe9dWv5NIi319TeBsKZ2lb0crrQjsbHqk0DAUwgQuoANqLku
|
||||
vA==
|
||||
=kRIv
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
]===])
|
||||
|
||||
if last_err() then return end
|
||||
print(key)
|
||||
|
||||
print
|
||||
-----
|
||||
|
||||
Write something directly to the terminal.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
print({
|
||||
some=1,
|
||||
fancy={
|
||||
data='structures',
|
||||
}
|
||||
})
|
||||
|
||||
.. warning::
|
||||
This function writes directly to the terminal and can interfere with other
|
||||
terminal features. This function should be used during development only.
|
||||
|
||||
psl_domain_from_dns_name
|
||||
------------------------
|
||||
|
||||
Returns the parent domain according to the public suffix list. For
|
||||
``www.a.b.c.d.example.com`` this is going to be ``example.com``.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
domain = psl_domain_from_dns_name('www.a.b.c.d.example.com')
|
||||
print(domain == 'example.com')
|
||||
|
||||
regex_find
|
||||
----------
|
||||
|
||||
Apply a regex to some text. Returns ``nil`` if the regex didn't match and the
|
||||
capture groups if it did.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
m = regex_find(".(.)", "abcdef")
|
||||
|
||||
if m == nil then
|
||||
print('No captures')
|
||||
end
|
||||
|
||||
print(m[1] == 'ab')
|
||||
print(m[2] == 'b')
|
||||
|
||||
regex_find_all
|
||||
--------------
|
||||
|
||||
Same as regex_find_, but returns all matches.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
m = regex_find_all(".(.)", "abcdef")
|
||||
|
||||
print(m[1][1] == 'ab')
|
||||
print(m[1][2] == 'b')
|
||||
print(m[2][1] == 'cd')
|
||||
print(m[2][2] == 'd')
|
||||
print(m[3][1] == 'ef')
|
||||
print(m[3][2] == 'f')
|
||||
|
||||
sleep
|
||||
-----
|
||||
|
||||
Pause the current program for the specified number of seconds. This is usually
|
||||
only used for debugging.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sleep(1)
|
||||
|
||||
sock_connect
|
||||
------------
|
||||
|
||||
Create a tcp connection.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sock = sock_connect("127.0.0.1", 1337)
|
||||
|
||||
sock_send
|
||||
---------
|
||||
|
||||
Send data to the socket.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sock_send(sock, "hello world")
|
||||
|
||||
sock_recv
|
||||
---------
|
||||
|
||||
Receive up to 4096 bytes from the socket.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recv(sock)
|
||||
|
||||
sock_sendline
|
||||
-------------
|
||||
|
||||
Send a string to the socket. A newline is automatically appended to the string.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sock_sendline(sock, line)
|
||||
|
||||
sock_recvline
|
||||
-------------
|
||||
|
||||
Receive a line from the socket. The line includes the newline.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvline(sock)
|
||||
|
||||
sock_recvall
|
||||
------------
|
||||
|
||||
Receive all data from the socket until EOF.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvall(sock)
|
||||
|
||||
sock_recvline_contains
|
||||
----------------------
|
||||
|
||||
Receive lines from the server until a line contains the needle, then return
|
||||
this line.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvline_contains(sock, needle)
|
||||
|
||||
sock_recvline_regex
|
||||
-------------------
|
||||
|
||||
Receive lines from the server until a line matches the regex, then return this
|
||||
line.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvline_regex(sock, "^250 ")
|
||||
|
||||
sock_recvn
|
||||
----------
|
||||
|
||||
Receive exactly n bytes from the socket.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvn(sock, 4)
|
||||
|
||||
sock_recvuntil
|
||||
--------------
|
||||
|
||||
Receive until the needle is found, then return all data including the needle.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = sock_recvuntil(sock, needle)
|
||||
|
||||
sock_sendafter
|
||||
--------------
|
||||
|
||||
Receive until the needle is found, then write data to the socket.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sock_sendafter(sock, needle, data)
|
||||
|
||||
sock_newline
|
||||
------------
|
||||
|
||||
Overwrite the default ``\n`` newline.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
sock_newline(sock, "\r\n")
|
||||
|
||||
status
|
||||
------
|
||||
|
||||
Update the label of the progress indicator.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
status('ohai')
|
||||
|
||||
stdin_readline
|
||||
--------------
|
||||
|
||||
Read a line from stdin. The final newline is not removed.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
stdin_readline()
|
||||
|
||||
.. note::
|
||||
This only works with `sn0int run --stdin`.
|
||||
|
||||
url_decode
|
||||
----------
|
||||
|
||||
Parse a query string into a map. For raw percent decoding see url_unescape_.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
v = url_decode('a=b&c=d')
|
||||
print(v['a'] == 'b')
|
||||
print(v['c'] == 'd')
|
||||
|
||||
url_encode
|
||||
----------
|
||||
|
||||
Encode a map into a query string. For raw percent encoding see url_escape_.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
v = url_encode({
|
||||
a='b',
|
||||
c='d',
|
||||
})
|
||||
print(v == 'a=b&c=d')
|
||||
|
||||
url_escape
|
||||
----------
|
||||
|
||||
Apply url escaping to a string.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
v = url_escape('foo bar?')
|
||||
print(v == 'foo%20bar%3F')
|
||||
|
||||
url_join
|
||||
--------
|
||||
|
||||
Join a relative link to an absolute link. If both links are absolute we just
|
||||
return the first one:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = url_join('https://example.com/x', '/foo')
|
||||
print(x == 'https://example.com/foo')
|
||||
|
||||
x = url_join('https://example.com/x', 'https://github.com/')
|
||||
print(x == 'https://github.com/')
|
||||
|
||||
url_parse
|
||||
---------
|
||||
|
||||
Parse a url into its components. The following components are returned:
|
||||
|
||||
- scheme
|
||||
- host
|
||||
- port
|
||||
- path
|
||||
- query
|
||||
- fragment
|
||||
- params
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
url = url_parse('https://example.com')
|
||||
print(url['scheme'] == 'https')
|
||||
print(url['host'] == 'example.com')
|
||||
print(url['path'] == '/')
|
||||
|
||||
url_unescape
|
||||
------------
|
||||
|
||||
Remove url escaping of a string.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
v = url_unescape('foo%20bar%3F')
|
||||
print(v == 'foo bar?')
|
||||
|
||||
utf8_decode
|
||||
-----------
|
||||
|
||||
Decodes a list of bytes/numbers into a string. This function might fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = utf8_decode({65, 65, 65, 65})
|
||||
if last_err() then return end
|
||||
print(x == 'AAAA')
|
||||
|
||||
x509_parse_pem
|
||||
--------------
|
||||
|
||||
Parse a pem encoded certificate. This function might fail.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
x = x509_parse_pem([[-----BEGIN CERTIFICATE-----
|
||||
MIID9DCCA3qgAwIBAgIQBWzetBRl/ycHFsBukRYuGTAKBggqhkjOPQQDAjBMMQsw
|
||||
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSYwJAYDVQQDEx1EaWdp
|
||||
Q2VydCBFQ0MgU2VjdXJlIFNlcnZlciBDQTAeFw0xODAzMzAwMDAwMDBaFw0yMDAz
|
||||
MjUxMjAwMDBaMGwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMN
|
||||
U2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQQ2xvdWRmbGFyZSwgSW5jLjEdMBsGA1UE
|
||||
AwwUKi5jbG91ZGZsYXJlLWRucy5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
||||
AASyRQsxrFBjziHmfDQjGsXBU0WWl3oxh7vg6h2V9f8lBMp18PY/td9R6VvJPa20
|
||||
AwVzIJI+dL6OSxviaIZEbmK7o4ICHDCCAhgwHwYDVR0jBBgwFoAUo53mH/naOU/A
|
||||
buiRy5Wl2jHiCp8wHQYDVR0OBBYEFN+XTeVDs7BBp0LykM+Jf64SV4ThMGMGA1Ud
|
||||
EQRcMFqCFCouY2xvdWRmbGFyZS1kbnMuY29thwQBAQEBhwQBAAABghJjbG91ZGZs
|
||||
YXJlLWRucy5jb22HECYGRwBHAAAAAAAAAAAAERGHECYGRwBHAAAAAAAAAAAAEAEw
|
||||
DgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBp
|
||||
BgNVHR8EYjBgMC6gLKAqhihodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc3NjYS1l
|
||||
Y2MtZzEuY3JsMC6gLKAqhihodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc3NjYS1l
|
||||
Y2MtZzEuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEW
|
||||
HGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMHsGCCsGAQUF
|
||||
BwEBBG8wbTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEUG
|
||||
CCsGAQUFBzAChjlodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRF
|
||||
Q0NTZWN1cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNo
|
||||
ADBlAjEAjoyy2Ogh1i1/Kh9+psMc1OChlQIvQF6AkojZS8yliar6m8q5nqC3qe0h
|
||||
HR0fExwLAjAueWRnHX4QJ9loqMhsPk3NB0Cs0mStsNDNG6/DpCYw7XmjoG3y1LS7
|
||||
ZkZZmqNn2Q8=
|
||||
-----END CERTIFICATE-----
|
||||
]])
|
||||
if last_err() then return end
|
||||
print(x)
|
||||
147
docs/sandbox.rst
Normal file
147
docs/sandbox.rst
Normal file
@@ -0,0 +1,147 @@
|
||||
Sandbox
|
||||
=======
|
||||
|
||||
Scripts are generally considered to be untrusted and executed exclusively in a
|
||||
child process. It's important to note that there's a basic sandbox that's
|
||||
active on every operating system, and there's a second line of defense on
|
||||
supported operating systems.
|
||||
|
||||
The first line of defense is the restrictive stdlib. It's assumed that and
|
||||
attacker gains full control over the lua code and is able to call any function
|
||||
with arbitrary arguments. The stdlib only provides functions that are
|
||||
considered safe, so for example it's not possible to start a process or open a
|
||||
file.
|
||||
|
||||
The second line of defense is supposed to make sure the system isn't
|
||||
compromised even if the first layer is fully broken and an attacker gains full
|
||||
control over the child process.
|
||||
|
||||
Right now this is only supported on linux and openbsd.
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
On linux we use seccomp to filter all syscalls that we don't need. We also use
|
||||
chroot to disable filesystem access. It's recommended to install the sn0int
|
||||
binary with ``cap_sys_chroot`` to make sure unprivileged users can use chroot.
|
||||
The chroot location is hard coded and all capabilities are removed after the
|
||||
chroot is done or if no chroot is going to happen.
|
||||
|
||||
OpenBSD
|
||||
-------
|
||||
|
||||
On openbsd we're using ``pledge`` to restrict syscalls and ``unveil`` to
|
||||
restrict filesystem access.
|
||||
|
||||
IPC Protocol
|
||||
------------
|
||||
|
||||
The parent process and the child process communicate using an IPC protocol that
|
||||
is line-based json.
|
||||
|
||||
For a simple hello world the parent process is only going to send a single line
|
||||
to the child process. This line contains:
|
||||
|
||||
- The function argument
|
||||
- The dns config
|
||||
- Keys that the module has been given access to
|
||||
- The module metadata and code
|
||||
- Options, if any
|
||||
- A socks5 proxy, if any
|
||||
- The log level
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"arg":null,"dns_config":{"ns":["1.1.1.1:53","1.0.0.1:53"],"tcp":false,"timeout":{"nanos":0,"secs":3}},"keyring":[],"module":{"author":"anonymous","description":"basic selftest","keyring_access":[],"name":"selftest","script":{"code":"-- Description: basic selftest\n-- Version: 0.1.0\n-- License: GPL-3.0\n\nfunction run()\n -- nothing to do here\nend\n"},"source":null,"version":"0.1.0"},"options":{},"proxy":null,"verbose":2}
|
||||
|
||||
Saving this line in a file called ``start.json`` and sending it to a sandbox
|
||||
process should result in the following output::
|
||||
|
||||
$ sn0int sandbox foobar < start.json
|
||||
{"Exit":"Ok"}
|
||||
$
|
||||
|
||||
This line tells us that the script terminated successfully.
|
||||
|
||||
There are some functions that cause a notification to the parent process. We
|
||||
are going to add a call to the ``info()`` function to our module:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"arg":null,"dns_config":{"ns":["1.1.1.1:53","1.0.0.1:53"],"tcp":false,"timeout":{"nanos":0,"secs":3}},"keyring":[],"module":{"author":"anonymous","description":"basic selftest","keyring_access":[],"name":"selftest","script":{"code":"-- Description: basic selftest\n-- Version: 0.1.0\n-- License: GPL-3.0\n\nfunction run()\n info('ohai')\nend\n"},"source":null,"version":"0.1.0"},"options":{},"proxy":null,"verbose":2}
|
||||
|
||||
This is going to print an additional event::
|
||||
|
||||
$ sn0int sandbox foobar < start2.json
|
||||
{"Log":{"Info":"\"ohai\""}}
|
||||
{"Exit":"Ok"}
|
||||
$
|
||||
|
||||
There are some functions that block the child process until the parent process
|
||||
sent a reply. These functions are mostly database related functions, since the
|
||||
child doesn't have direct database access. To demonstrate this, we're going to
|
||||
write two lines to our file this time, one is the init line and the second one
|
||||
is the reply for the database event:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"arg":null,"dns_config":{"ns":["1.1.1.1:53","1.0.0.1:53"],"tcp":false,"timeout":{"nanos":0,"secs":3}},"keyring":[],"module":{"author":"anonymous","description":"basic selftest","keyring_access":[],"name":"selftest","script":{"code":"-- Description: basic selftest\n-- Version: 0.1.0\n-- License: GPL-3.0\n\nfunction run()\n x = db_add('domain', {value=\"example.com\"})\n info(x)\nend\n"},"source":null,"version":"0.1.0"},"options":{},"proxy":null,"verbose":2}
|
||||
{"Ok":1337}
|
||||
|
||||
Results in the following output::
|
||||
|
||||
$ target/release/sn0int sandbox foobar < start3.json
|
||||
{"Database":{"Insert":{"Domain":{"value":"example.com"}}}}
|
||||
{"Log":{"Info":"1337.0"}}
|
||||
{"Exit":"Ok"}
|
||||
$
|
||||
|
||||
The first line is a database event and indicates that the child wants to insert
|
||||
data. After printing this line the child tries to read a line from stdin, this
|
||||
is why we needed to write two lines to our json file this time. In the second
|
||||
line the child learns if the insert was successful and which id was assigned to
|
||||
that entity.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
There are some limitations that you should be aware:
|
||||
|
||||
- Network access is available and network namespaces aren't isolated. This
|
||||
means scripts have access to your local network, the internet and also your
|
||||
localhost loopback interface.
|
||||
- If chroot is unavailable an attacker could connect to unix domain sockets.
|
||||
|
||||
Diagnosing a sandbox failure
|
||||
----------------------------
|
||||
|
||||
You might experience a sandbox failure, especially on architectures that are
|
||||
less popular. This usually looks like this::
|
||||
|
||||
[sn0int][example][kpcyrd/ctlogs] > run
|
||||
[-] Failed "example.com": EOF while parsing a value at line 1 column 0
|
||||
[+] Finished kpcyrd/ctlogs (1 errors)
|
||||
|
||||
A module that never finishes could also mean an IO thread inside the worker got
|
||||
killed by the sandbox.
|
||||
|
||||
You can try to diagnose this yourself with strace::
|
||||
|
||||
strace -f sn0int run -vv ctlogs 2>&1 | tee strace.log
|
||||
|
||||
Open ``strace.log``, look out for syscalls that didn't return by searching for
|
||||
``= ?`` and ignore calls to exit and similar. You are looking for something
|
||||
like this::
|
||||
|
||||
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=48, filter=0xdd59094e490}) = 0
|
||||
write(1, "[+] activated!\n", 15[+] activated!
|
||||
) = 15
|
||||
getresuid( <unfinished ...>) = ?
|
||||
+++ killed by SIGSYS (core dumped) +++
|
||||
|
||||
This would indicate a call to ``getresuid`` which was not allowed by the
|
||||
seccomp filter.
|
||||
|
||||
If you don't want to diagnose this yourself open a new bug report with as much
|
||||
information as possible, specifically which distro, which release and which
|
||||
architecture you're using.
|
||||
231
docs/scripting.rst
Normal file
231
docs/scripting.rst
Normal file
@@ -0,0 +1,231 @@
|
||||
Scripting
|
||||
=========
|
||||
|
||||
Scripting is the core feature in sn0int. It's not strictly required, but if you
|
||||
want to write your own modules, this section is for you.
|
||||
|
||||
Write your first module
|
||||
-----------------------
|
||||
|
||||
It's highly recommended to use a VCS for development, so let's start by setting
|
||||
that up. We're going to assume you store your repos in ``~/repos`` but you're
|
||||
free to change that to something else::
|
||||
|
||||
$ git init ~/repos/sn0int-modules
|
||||
$ cd ~/repos/sn0int-modules
|
||||
$ ln -s "$PWD" ~/.local/share/sn0int/modules/$YOUR_GITHUB_NAME
|
||||
|
||||
Every module we're adding to ``~/repos/sn0int-modules`` is now going to be
|
||||
picked up by sn0int.
|
||||
|
||||
Make sure you're still in the right folder and add your first module::
|
||||
|
||||
sn0int new first.lua
|
||||
|
||||
This is going to generate some boilerplate for you that every module needs to
|
||||
load successfully. Afterwards we can edit it like this:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- Description: ohai wurld
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
-- TODO: do something here
|
||||
end
|
||||
|
||||
``Description`` (mandatory)
|
||||
This should be a short text that describes what your module is doing.
|
||||
|
||||
``Version`` (mandatory)
|
||||
Every module requires a semver_ version. You can just set it to ``0.1.0``
|
||||
during development, but you need to increase it every time you publish your
|
||||
module. If you don't care about that one, just keep increasing ``0.X.0``.
|
||||
|
||||
.. _semver: https://semver.org/
|
||||
|
||||
``Source`` (mandatory)
|
||||
This is going to specify what kind of entities we're interested in. If we
|
||||
specify ``domains`` our module is going to be called with all domains that
|
||||
are targeted.
|
||||
|
||||
- ``domains``
|
||||
- ``subdomains``
|
||||
- ``ipaddrs``
|
||||
- ``urls``
|
||||
- ``emails``
|
||||
|
||||
``License`` (mandatory)
|
||||
This is somewhat special. We require that every module is licensed under an
|
||||
open source license. Pick one of the following licenses.
|
||||
|
||||
- ``MIT`` - https://opensource.org/licenses/MIT
|
||||
- ``GPL-3.0`` - https://opensource.org/licenses/gpl-license
|
||||
- ``LGPL-3.0`` - https://opensource.org/licenses/lgpl-license
|
||||
- ``BSD-2-Clause`` - https://opensource.org/licenses/BSD-2-Clause
|
||||
- ``BSD-3-Clause`` - https://opensource.org/licenses/BSD-3-Clause
|
||||
- ``WTFPL`` - https://spdx.org/licenses/WTFPL.html
|
||||
|
||||
``function run(arg)`` (mandatory)
|
||||
This is where the actual magic of our module happens. Our function is going
|
||||
to be called in a loop for each entity that is targeted by the user.
|
||||
|
||||
Let's continue. For the sake of an hello world we're going to take some
|
||||
``domains``, check if a ``www`` subdomain exists and if it does, add it to the
|
||||
database.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
function run(arg)
|
||||
subdomain = 'www.' .. arg['value']
|
||||
print(subdomain)
|
||||
end
|
||||
|
||||
Combined with the header we wrote previously we can already execute this
|
||||
module. Make sure you've added a domain to scope with ``add domain
|
||||
example.com``, save your file and run it like this::
|
||||
|
||||
sn0int run -f ./first.lua
|
||||
|
||||
We should see some output by our print function.
|
||||
|
||||
.. note::
|
||||
``print`` is useful for development but must be removed before publishing.
|
||||
|
||||
Next, we want to actually resolve that name, we're going to use the ``dns``
|
||||
function for that. This function takes a name and a query type and returns a
|
||||
result. Note that this function might fail, in which case we want to abort our
|
||||
function. We do that by checking if the return value of ``last_err()`` is
|
||||
truth-y.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
function run(arg)
|
||||
subdomain = 'www.' .. arg['value']
|
||||
|
||||
records = dns(subdomain, {
|
||||
record='A'
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
print(records)
|
||||
end
|
||||
|
||||
If you run your module again you're going to see some output, either
|
||||
``{"answers":[somedata],"error":null}`` or
|
||||
``{"answers":[],"error":"NXDomain"}``. We decide that we add the subdomain to
|
||||
our scope and set it to resolvable if ``error`` is ``nil``.
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
function run(arg)
|
||||
subdomain = 'www.' .. arg['value']
|
||||
|
||||
records = dns(subdomain, {
|
||||
record='A'
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
if records['error'] == nil then
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=subdomain,
|
||||
resolvable=true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
.. hint::
|
||||
See the database section to understand how the database works in detail.
|
||||
|
||||
If we execute our module one more time it's going to log that it discovered a
|
||||
subdomain, if it doesn't, try adding more domains to scope. Note that this only
|
||||
happens the first time. Modules that don't discover anything or don't discover
|
||||
anything new exit silently.
|
||||
|
||||
After putting everything together, our final module looks like this:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- Description: ohai wurld
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
subdomain = 'www.' .. arg['value']
|
||||
|
||||
records = dns(subdomain, {
|
||||
record='A'
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
if records['success'] ~= nil then
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=subdomain,
|
||||
resolvable=true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
There's still some room for improvement, for example, since we already resolved
|
||||
that record, we could also add the ip address to the scope and link it to the
|
||||
subdomain we added.
|
||||
|
||||
Publish your module
|
||||
-------------------
|
||||
|
||||
The public registry uses github usernames to namespace the registry. This means
|
||||
you need to authenticate to the registry using your github username. This can
|
||||
be done using::
|
||||
|
||||
sn0int login
|
||||
|
||||
sn0int is going to open a new tab in your browser, if you are already signed
|
||||
into your github account you only need to confirm an authorization request. The
|
||||
application doesn't need any of your data, so it's only asking you to confirm
|
||||
your identity.
|
||||
|
||||
Afterwards publish your module with::
|
||||
|
||||
sn0int publish ./first.lua
|
||||
|
||||
Reading data from stdin
|
||||
-----------------------
|
||||
|
||||
Sometimes you need to read data that can't be easily accessed from within the
|
||||
sandbox, like output of other programms or file content. In that case you can
|
||||
write a module that reads from stdin:
|
||||
|
||||
.. code-block:: lua
|
||||
|
||||
-- Description: Read from stdin
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
while true do
|
||||
x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
info(x)
|
||||
end
|
||||
end
|
||||
|
||||
Write it to a file and run it like this::
|
||||
|
||||
% echo hello | sn0int run --stdin -vvf stdin.lua
|
||||
[*] anonymous/stdin : "hello\n"
|
||||
[+] Finished anonymous/stdin
|
||||
%
|
||||
|
||||
This is going to read one line at a time and allows you to process it with
|
||||
regular expressions and add data to the database.
|
||||
|
||||
.. note::
|
||||
If you get an error like ``Failed to read stdin: "stdin is unavailable"``
|
||||
make sure the ``--stdin`` flag is set.
|
||||
258
docs/usage.rst
Normal file
258
docs/usage.rst
Normal file
@@ -0,0 +1,258 @@
|
||||
Running your first investigation
|
||||
================================
|
||||
|
||||
This page is going to guide you through the process of setting up your
|
||||
environment and running your first investigation.
|
||||
|
||||
Installing the default modules
|
||||
------------------------------
|
||||
|
||||
By default, sn0int doesn't have any modules installed. If you start up sn0int
|
||||
it's going to download some files that it needs and then suggests to install a
|
||||
number of recommended modules::
|
||||
|
||||
$ sn0int
|
||||
|
||||
___/ .
|
||||
____ , __ .' /\ ` , __ _/_
|
||||
( |' `. | / | | |' `. |
|
||||
`--. | | |,' | | | | |
|
||||
\___.' / | /`---' / / | \__/
|
||||
|
||||
osint | recon | security
|
||||
irc.hackint.org:6697/#sn0int
|
||||
|
||||
[+] Connecting to database
|
||||
[+] Downloading public suffix list
|
||||
[+] Downloading "GeoLite2-City.mmdb"
|
||||
[+] Downloading "GeoLite2-ASN.mmdb"
|
||||
[+] Loaded 0 modules
|
||||
[*] No modules found, run quickstart to install default modules
|
||||
[sn0int][default] >
|
||||
|
||||
Typing ``quickstart`` is going to get you a fair number of featured modules::
|
||||
|
||||
[sn0int][default] > quickstart
|
||||
[+] Installing kpcyrd/asn
|
||||
[+] Installing kpcyrd/ctlogs
|
||||
[+] Installing kpcyrd/dns-resolve
|
||||
[+] Installing kpcyrd/geoip
|
||||
[+] Installing kpcyrd/hackertarget-subdomains
|
||||
[+] Installing kpcyrd/otx-subdomains
|
||||
[+] Installing kpcyrd/passive-spider
|
||||
[+] Installing kpcyrd/pgp-keyserver
|
||||
[+] Installing kpcyrd/threatminer-ipaddr
|
||||
[+] Installing kpcyrd/threatminer-subdomains
|
||||
[+] Installing kpcyrd/url-scan
|
||||
[+] Installing kpcyrd/waybackurls
|
||||
[+] Loaded 12 modules
|
||||
[sn0int][default] >
|
||||
|
||||
Adding something to scope
|
||||
-------------------------
|
||||
|
||||
You probably want to separate your investigations so you should select a
|
||||
workspace where your results should go::
|
||||
|
||||
[sn0int][default] > workspace demo
|
||||
[+] Connecting to database
|
||||
[sn0int][demo] >
|
||||
|
||||
Next, we have to start somewhere and add the first entity to our scope::
|
||||
|
||||
[sn0int][demo] > add domain
|
||||
Domain: example.com
|
||||
[sn0int][demo] >
|
||||
|
||||
.. note::
|
||||
There is a concept of a domain vs a subdomain. We are referring to a domain
|
||||
as everything that is a subdomain of a `public suffix`_. For example, .com
|
||||
is a public suffix, which makes example.com a domain in sn0int terms. Every
|
||||
subdomain of that, like www.example.com, is referred to as a subdomain.
|
||||
|
||||
Note that example.com can be added as a subdomain as well since it can hold
|
||||
records. In that case, example.com is both the name of the dns zone, while
|
||||
also being an entity in that zone.
|
||||
|
||||
.. _public suffix: https://publicsuffix.org/
|
||||
|
||||
You can confirm this by running a select on the domains we now have::
|
||||
|
||||
[sn0int][demo] > select domains
|
||||
#1, "example.com"
|
||||
[sn0int][demo] >
|
||||
|
||||
Something we don't need right now, but is going to be useful later on is the
|
||||
ability to filter your entities::
|
||||
|
||||
[sn0int][demo] > select domains where id=1
|
||||
#1, "example.com"
|
||||
[sn0int][demo] >
|
||||
[sn0int][demo] > select domains where value like %.com
|
||||
#1, "example.com"
|
||||
[sn0int][demo] >
|
||||
[sn0int][demo] > select domains where ( value like e% and value like %m ) or false
|
||||
#1, "example.com"
|
||||
[sn0int][demo] >
|
||||
|
||||
.. note::
|
||||
Almost all entities have a ``value`` column that holds the primary value of
|
||||
the entity.
|
||||
|
||||
Running a module
|
||||
----------------
|
||||
|
||||
Now that we have something to get started with, we can run our first module.
|
||||
First lets list all modules we have::
|
||||
|
||||
[sn0int][demo] > mod list
|
||||
kpcyrd/asn (0.1.0)
|
||||
Run a asn lookup for an ip address
|
||||
kpcyrd/ctlogs (0.1.0)
|
||||
Query certificate transparency logs to discover subdomains
|
||||
kpcyrd/dns-resolve (0.1.0)
|
||||
Query subdomains to discovery ip addresses and verify the record is visible
|
||||
kpcyrd/geoip (0.1.0)
|
||||
Run a geoip lookup for an ip address
|
||||
kpcyrd/hackertarget-subdomains (0.1.0)
|
||||
Query hackertarget for subdomains of a domain
|
||||
kpcyrd/otx-subdomains (0.1.0)
|
||||
Query alienvault otx passive dns for subdomains of a domain
|
||||
kpcyrd/passive-spider (0.1.0)
|
||||
Scrape known http responses for urls
|
||||
kpcyrd/pgp-keyserver (0.1.0)
|
||||
Query pgp keyserver for email addresses
|
||||
kpcyrd/threatminer-ipaddr (0.1.0)
|
||||
Query ThreatMiner passive dns for subdomains of an ip address
|
||||
kpcyrd/threatminer-subdomains (0.1.0)
|
||||
Query ThreatMiner passive dns for subdomains of a domain
|
||||
kpcyrd/url-scan (0.1.0)
|
||||
Scan subdomains for websites
|
||||
kpcyrd/waybackurls (0.1.0)
|
||||
Discover subdomains from wayback machine
|
||||
[sn0int][demo] >
|
||||
|
||||
Let's start by querying certificate transparency logs::
|
||||
|
||||
[sn0int][demo] > use ctlogs
|
||||
[sn0int][demo][kpcyrd/ctlogs] > run
|
||||
[*] "example.com" : Subdomain: "www.example.com"
|
||||
[*] "example.com" : Subdomain: "m.example.com"
|
||||
[*] "example.com" : Subdomain: "dev.example.com"
|
||||
[*] "example.com" : Subdomain: "products.example.com"
|
||||
[*] "example.com" : Subdomain: "support.example.com"
|
||||
[+] Finished kpcyrd/ctlogs
|
||||
[sn0int][demo][kpcyrd/ctlogs] >
|
||||
|
||||
Looks like we've discovered some subdomains here. It might be tempting to throw
|
||||
some of them in a browser but hold on, there's a more efficient way to approach
|
||||
this.
|
||||
|
||||
.. hint::
|
||||
You can run the modules concurrently with ``run -j 8``.
|
||||
|
||||
Running followup modules on the results
|
||||
---------------------------------------
|
||||
|
||||
A lot of time has been spent on the database part. While it sort of feels like
|
||||
a no-sql database we are actually enforcing a schema for a reason instead of
|
||||
just using generic dictionaries and calling it a day.
|
||||
|
||||
It's crucial that entities created by one module can be picked up by another
|
||||
module, like LEGOs. Let's continue with a module to query the dns records::
|
||||
|
||||
[sn0int][demo][kpcyrd/ctlogs] > use dns-resolve
|
||||
[sn0int][demo][kpcyrd/dns-resolve] > run
|
||||
[*] "www.example.com" : Updating "www.example.com" (resolvable => true)
|
||||
[*] "www.example.com" : IpAddr: 93.184.216.34
|
||||
[*] "www.example.com" : "www.example.com" -> 93.184.216.34
|
||||
[*] "m.example.com" : Updating "m.example.com" (resolvable => false)
|
||||
[*] "dev.example.com" : Updating "dev.example.com" (resolvable => false)
|
||||
[*] "products.example.com" : Updating "products.example.com" (resolvable => false)
|
||||
[*] "support.example.com" : Updating "support.example.com" (resolvable => false)
|
||||
[+] Finished kpcyrd/dns-resolve
|
||||
[sn0int][demo][kpcyrd/dns-resolve] >
|
||||
|
||||
.. TODO: mention https://github.com/kpcyrd/sn0int/issues/27
|
||||
|
||||
Two things happened here: We've discovered some IP addresses and added them to
|
||||
scope, and we also updated our subdomain entities with new information, since
|
||||
we now know which of them are resolvable and which aren't.
|
||||
|
||||
Let's run the next module, which is actually going to check for websites on
|
||||
them, but let's only target subdomains that we know are resolvable::
|
||||
|
||||
[sn0int][demo][kpcyrd/dns-resolve] > use url-scan
|
||||
[sn0int][demo][kpcyrd/url-scan] > target
|
||||
#1, "www.example.com"
|
||||
93.184.216.34
|
||||
#2, "m.example.com"
|
||||
#3, "dev.example.com"
|
||||
#4, "products.example.com"
|
||||
#5, "support.example.com"
|
||||
[sn0int][demo][kpcyrd/url-scan] > target where resolvable
|
||||
[+] 1 entities selected
|
||||
[sn0int][demo][kpcyrd/url-scan] > target
|
||||
#1, "www.example.com"
|
||||
93.184.216.34
|
||||
[sn0int][demo][kpcyrd/url-scan] >
|
||||
|
||||
We can both preview and limit the targets that are going to be passed to the
|
||||
module with the target command. Once we are satisfied with our selection we can
|
||||
run this module::
|
||||
|
||||
[sn0int][demo][kpcyrd/url-scan] > run
|
||||
[*] "www.example.com" : Url: "http://www.example.com/" (200)
|
||||
[*] "www.example.com" : Url: "https://www.example.com/" (200)
|
||||
[+] Finished kpcyrd/url-scan
|
||||
[sn0int][demo][kpcyrd/url-scan] >
|
||||
|
||||
We've now probed both port 80 and port 443 for each subdomain and found two
|
||||
http responses this way. If you want a list of urls you may want to visit in
|
||||
your browser can now query them::
|
||||
|
||||
[sn0int][demo][kpcyrd/url-scan] > select urls
|
||||
#1, "http://www.example.com/" (200)
|
||||
#2, "https://www.example.com/" (200)
|
||||
[sn0int][demo][kpcyrd/url-scan] >
|
||||
|
||||
Unscoping entities
|
||||
------------------
|
||||
|
||||
Something you are going to run into is that modules are too greedy and add
|
||||
things to the scope we are not interested in. You can delete them using the
|
||||
delete command, but those are likely picked up by a module again.
|
||||
|
||||
What you can do instead is setting a flag on an entity that removes it from
|
||||
our scope. This is done using the noscope command::
|
||||
|
||||
[sn0int][demo] > use ctlogs
|
||||
[sn0int][demo][kpcyrd/ctlogs] > target
|
||||
#1, "example.com"
|
||||
[sn0int][demo][kpcyrd/ctlogs] > add domain
|
||||
Domain: google.com
|
||||
[sn0int][demo][kpcyrd/ctlogs] > target
|
||||
#1, "example.com"
|
||||
#2, "google.com"
|
||||
[sn0int][demo][kpcyrd/ctlogs] > noscope domains where value=google.com
|
||||
[+] Updated 1 rows
|
||||
[sn0int][demo][kpcyrd/ctlogs] > target
|
||||
#1, "example.com"
|
||||
[sn0int][demo][kpcyrd/ctlogs] >
|
||||
|
||||
Entities that are unscoped are automatically ignored by all modules.
|
||||
|
||||
You can reverse this using the scope command::
|
||||
|
||||
[sn0int][demo][kpcyrd/ctlogs] > target
|
||||
#1, "example.com"
|
||||
[sn0int][demo][kpcyrd/ctlogs] > scope domains where true
|
||||
[+] Updated 2 rows
|
||||
[sn0int][demo][kpcyrd/ctlogs] > target
|
||||
#1, "example.com"
|
||||
#2, "google.com"
|
||||
[sn0int][demo][kpcyrd/ctlogs] >
|
||||
|
||||
.. hint::
|
||||
All entities have this field, you can refer to it in queries using
|
||||
``unscoped=1``.
|
||||
104
examples/maxmind.rs
Normal file
104
examples/maxmind.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
extern crate sn0int;
|
||||
extern crate env_logger;
|
||||
extern crate chrootable_https;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
// workaround for rustc 1.29.2 support
|
||||
#[cfg(not(target_os = "openbsd"))]
|
||||
extern crate structopt;
|
||||
#[cfg(target_os = "openbsd")]
|
||||
#[macro_use] extern crate structopt;
|
||||
|
||||
use sn0int::errors::*;
|
||||
use sn0int::geoip::{AsnDB, GeoIP, Maxmind};
|
||||
use sn0int::paths;
|
||||
use std::fs;
|
||||
use std::net::IpAddr;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum Args {
|
||||
#[structopt(name="dl")]
|
||||
Download(Download),
|
||||
#[structopt(name="asn")]
|
||||
Asn(AsnArgs),
|
||||
#[structopt(name="geoip")]
|
||||
GeoIP(GeoIPArgs),
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Download {
|
||||
url: String,
|
||||
filter: String,
|
||||
target: String,
|
||||
#[structopt(short="e", long="extract-only")]
|
||||
extract_only: bool,
|
||||
}
|
||||
|
||||
impl Download {
|
||||
fn run(&self) -> Result<()> {
|
||||
let path = paths::cache_dir()?.join(&self.target);
|
||||
if self.extract_only {
|
||||
let body = fs::read(&self.url)?;
|
||||
sn0int::archive::extract(&mut &body[..], &self.filter, path)?;
|
||||
} else {
|
||||
GeoIP::download(path, &self.filter, &self.url)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct AsnArgs {
|
||||
ip: IpAddr,
|
||||
}
|
||||
|
||||
impl AsnArgs {
|
||||
fn run(&self) -> Result<()> {
|
||||
let asndb = AsnDB::open_or_download()?;
|
||||
|
||||
let asn = asndb.lookup(self.ip)?;
|
||||
println!("{:#?}", asn);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct GeoIPArgs {
|
||||
ip: IpAddr,
|
||||
}
|
||||
|
||||
impl GeoIPArgs {
|
||||
fn run(&self) -> Result<()> {
|
||||
let geoip = GeoIP::open_or_download()?;
|
||||
|
||||
let lookup = geoip.lookup(self.ip)?;
|
||||
println!("{:#?}", lookup);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let args = Args::from_args();
|
||||
debug!("{:?}", args);
|
||||
match args {
|
||||
Args::Download(args) => args.run(),
|
||||
Args::Asn(args) => args.run(),
|
||||
Args::GeoIP(args) => args.run(),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
if let Err(err) = run() {
|
||||
eprintln!("Error: {}", err);
|
||||
for cause in err.iter_chain().skip(1) {
|
||||
eprintln!("Because: {}", cause);
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,67 @@
|
||||
extern crate sn0int;
|
||||
|
||||
use std::env;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use sn0int::term::{SPINNERS, Spinner};
|
||||
use sn0int::term::{SPINNERS, Spinner, StackedSpinners};
|
||||
use structopt::StructOpt;
|
||||
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum Args {
|
||||
#[structopt(name="single")]
|
||||
Single(Single),
|
||||
#[structopt(name="stacked")]
|
||||
Stacked(Stacked),
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Single {
|
||||
idx: usize,
|
||||
#[structopt(long="ticks", default_value="100")]
|
||||
ticks: usize,
|
||||
}
|
||||
|
||||
impl Single {
|
||||
fn run(&self) {
|
||||
let mut s = Spinner::new(SPINNERS[self.idx], "Demo".to_string());
|
||||
|
||||
for _ in 0..self.ticks {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
s.tick();
|
||||
}
|
||||
|
||||
s.finish("Done".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Stacked {
|
||||
}
|
||||
|
||||
impl Stacked {
|
||||
fn run(&self) {
|
||||
let mut stack = StackedSpinners::new();
|
||||
stack.add("1".into(), String::from("spinner1"));
|
||||
stack.add("2".into(), String::from("spinner2"));
|
||||
stack.add("3".into(), String::from("spinner3"));
|
||||
|
||||
for x in 1..=3 {
|
||||
for _ in 0..50 {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
stack.tick();
|
||||
}
|
||||
// stack.log("ohai");
|
||||
stack.remove(&x.to_string());
|
||||
}
|
||||
|
||||
stack.clear();
|
||||
|
||||
// stack.finish("Done".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let idx = env::args().skip(1).next().expect("Expected argv[1]");
|
||||
let idx = idx.parse::<usize>().expect("argv[1] is not a number");
|
||||
|
||||
let mut s = Spinner::new(SPINNERS[idx], "Demo".to_string());
|
||||
|
||||
for _ in 0..100 {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
s.tick();
|
||||
let args = Args::from_args();
|
||||
match args {
|
||||
Args::Single(args) => args.run(),
|
||||
Args::Stacked(args) => args.run(),
|
||||
}
|
||||
|
||||
s.finish("Done".to_string());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ CREATE TABLE subdomain_ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
ip_addr_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES domains(id),
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
FOREIGN KEY(ip_addr_id) REFERENCES ipaddrs(id),
|
||||
CONSTRAINT subdomain_ipaddr_unique UNIQUE (subdomain_id, ip_addr_id)
|
||||
);
|
||||
@@ -35,6 +35,6 @@ CREATE TABLE urls (
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES domains(id),
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
1
migrations/2018-10-12-051052_emails/down.sql
Normal file
1
migrations/2018-10-12-051052_emails/down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP TABLE emails;
|
||||
5
migrations/2018-10-12-051052_emails/up.sql
Normal file
5
migrations/2018-10-12-051052_emails/up.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE emails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
CONSTRAINT email_unique UNIQUE (value)
|
||||
);
|
||||
90
migrations/2018-10-15-061351_scope/down.sql
Normal file
90
migrations/2018-10-15-061351_scope/down.sql
Normal file
@@ -0,0 +1,90 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- domains
|
||||
|
||||
ALTER TABLE domains RENAME TO _domains_old;
|
||||
|
||||
CREATE TABLE domains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
CONSTRAINT domain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO domains (id, value)
|
||||
SELECT id, value
|
||||
FROM _domains_old;
|
||||
|
||||
DROP TABLE _domains_old;
|
||||
|
||||
-- subdomains
|
||||
|
||||
ALTER TABLE subdomains RENAME TO _subdomains_old;
|
||||
|
||||
CREATE TABLE subdomains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
domain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
FOREIGN KEY(domain_id) REFERENCES domains(id),
|
||||
CONSTRAINT subdomain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO subdomains (id, domain_id, value)
|
||||
SELECT id, domain_id, value
|
||||
FROM _subdomains_old;
|
||||
|
||||
DROP TABLE _subdomains_old;
|
||||
|
||||
-- ipaddrs
|
||||
|
||||
ALTER TABLE ipaddrs RENAME TO _ipaddrs_old;
|
||||
|
||||
CREATE TABLE ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO ipaddrs (id, family, value)
|
||||
SELECT id, family, value
|
||||
FROM _ipaddrs_old;
|
||||
|
||||
DROP TABLE _ipaddrs_old;
|
||||
|
||||
-- urls
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body)
|
||||
SELECT id, subdomain_id, value, status, body
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
-- emails
|
||||
|
||||
ALTER TABLE emails RENAME TO _emails_old;
|
||||
|
||||
CREATE TABLE emails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
CONSTRAINT domain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO emails (id, value)
|
||||
SELECT id, value
|
||||
FROM _emails_old;
|
||||
|
||||
DROP TABLE _emails_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
5
migrations/2018-10-15-061351_scope/up.sql
Normal file
5
migrations/2018-10-15-061351_scope/up.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE domains ADD COLUMN unscoped BOOLEAN DEFAULT 0 NOT NULL;
|
||||
ALTER TABLE subdomains ADD COLUMN unscoped BOOLEAN DEFAULT 0 NOT NULL;
|
||||
ALTER TABLE ipaddrs ADD COLUMN unscoped BOOLEAN DEFAULT 0 NOT NULL;
|
||||
ALTER TABLE urls ADD COLUMN unscoped BOOLEAN DEFAULT 0 NOT NULL;
|
||||
ALTER TABLE emails ADD COLUMN unscoped BOOLEAN DEFAULT 0 NOT NULL;
|
||||
60
migrations/2018-10-17-112220_verifiable/down.sql
Normal file
60
migrations/2018-10-17-112220_verifiable/down.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- subdomains
|
||||
|
||||
ALTER TABLE subdomains RENAME TO _subdomains_old;
|
||||
|
||||
CREATE TABLE subdomains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
domain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY(domain_id) REFERENCES domains(id),
|
||||
CONSTRAINT subdomain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO subdomains (id, domain_id, value, unscoped)
|
||||
SELECT id, domain_id, value, unscoped
|
||||
FROM _subdomains_old;
|
||||
|
||||
DROP TABLE _subdomains_old;
|
||||
|
||||
-- emails
|
||||
|
||||
ALTER TABLE emails RENAME TO _emails_old;
|
||||
|
||||
CREATE TABLE emails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT emails_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO emails (id, value, unscoped)
|
||||
SELECT id, value, unscoped
|
||||
FROM _emails_old;
|
||||
|
||||
DROP TABLE _emails_old;
|
||||
|
||||
-- urls
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body, unscoped)
|
||||
SELECT id, subdomain_id, value, status, body, unscoped
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
3
migrations/2018-10-17-112220_verifiable/up.sql
Normal file
3
migrations/2018-10-17-112220_verifiable/up.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE subdomains ADD COLUMN resolvable BOOLEAN;
|
||||
ALTER TABLE emails ADD COLUMN valid BOOLEAN;
|
||||
ALTER TABLE urls ADD COLUMN online BOOLEAN;
|
||||
21
migrations/2018-10-22-141819_geoip/down.sql
Normal file
21
migrations/2018-10-22-141819_geoip/down.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- ipaddrs
|
||||
|
||||
ALTER TABLE ipaddrs RENAME TO _ipaddrs_old;
|
||||
|
||||
CREATE TABLE ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO ipaddrs (id, family, value, unscoped)
|
||||
SELECT id, family, value, unscoped
|
||||
FROM _ipaddrs_old;
|
||||
|
||||
DROP TABLE _ipaddrs_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
7
migrations/2018-10-22-141819_geoip/up.sql
Normal file
7
migrations/2018-10-22-141819_geoip/up.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE ipaddrs ADD COLUMN continent VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN continent_code VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN country VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN country_code VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN city VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN latitude FLOAT;
|
||||
ALTER TABLE ipaddrs ADD COLUMN longitude FLOAT;
|
||||
28
migrations/2018-10-24-131856_asn/down.sql
Normal file
28
migrations/2018-10-24-131856_asn/down.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- ipaddrs
|
||||
|
||||
ALTER TABLE ipaddrs RENAME TO _ipaddrs_old;
|
||||
|
||||
CREATE TABLE ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
continent VARCHAR,
|
||||
continent_code VARCHAR,
|
||||
country VARCHAR,
|
||||
country_code VARCHAR,
|
||||
city VARCHAR,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO ipaddrs (id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude)
|
||||
SELECT id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude
|
||||
FROM _ipaddrs_old;
|
||||
|
||||
DROP TABLE _ipaddrs_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
2
migrations/2018-10-24-131856_asn/up.sql
Normal file
2
migrations/2018-10-24-131856_asn/up.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ipaddrs ADD COLUMN asn INTEGER;
|
||||
ALTER TABLE ipaddrs ADD COLUMN as_org VARCHAR;
|
||||
126
migrations/2018-10-26-001932_cascade-delete/down.sql
Normal file
126
migrations/2018-10-26-001932_cascade-delete/down.sql
Normal file
@@ -0,0 +1,126 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- domains
|
||||
|
||||
ALTER TABLE domains RENAME TO _domains_old;
|
||||
|
||||
CREATE TABLE domains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT domain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO domains (id, value, unscoped)
|
||||
SELECT id, value, unscoped
|
||||
FROM _domains_old;
|
||||
|
||||
DROP TABLE _domains_old;
|
||||
|
||||
-- subdomains
|
||||
|
||||
ALTER TABLE subdomains RENAME TO _subdomains_old;
|
||||
|
||||
CREATE TABLE subdomains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
domain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
resolvable BOOLEAN,
|
||||
FOREIGN KEY(domain_id) REFERENCES domains(id),
|
||||
CONSTRAINT subdomain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO subdomains (id, domain_id, value, unscoped, resolvable)
|
||||
SELECT id, domain_id, value, unscoped, resolvable
|
||||
FROM _subdomains_old;
|
||||
|
||||
DROP TABLE _subdomains_old;
|
||||
|
||||
-- subdomain_ipaddrs
|
||||
|
||||
ALTER TABLE subdomain_ipaddrs RENAME TO _subdomain_ipaddrs_old;
|
||||
|
||||
CREATE TABLE subdomain_ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
ip_addr_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
FOREIGN KEY(ip_addr_id) REFERENCES ipaddrs(id),
|
||||
CONSTRAINT subdomain_ipaddr_unique UNIQUE (subdomain_id, ip_addr_id)
|
||||
);
|
||||
|
||||
INSERT INTO subdomain_ipaddrs (id, subdomain_id, ip_addr_id)
|
||||
SELECT id, subdomain_id, ip_addr_id
|
||||
FROM _subdomain_ipaddrs_old;
|
||||
|
||||
DROP TABLE _subdomain_ipaddrs_old;
|
||||
|
||||
-- urls
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
online BOOLEAN,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id),
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body, unscoped, online)
|
||||
SELECT id, subdomain_id, value, status, body, unscoped, online
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
-- emails
|
||||
|
||||
ALTER TABLE emails RENAME TO _emails_old;
|
||||
|
||||
CREATE TABLE emails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
valid BOOLEAN,
|
||||
CONSTRAINT email_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO emails (id, value, unscoped, valid)
|
||||
SELECT id, value, unscoped, valid
|
||||
FROM _emails_old;
|
||||
|
||||
DROP TABLE _emails_old;
|
||||
|
||||
-- ipaddrs
|
||||
|
||||
ALTER TABLE ipaddrs RENAME TO _ipaddrs_old;
|
||||
|
||||
CREATE TABLE ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
continent VARCHAR,
|
||||
continent_code VARCHAR,
|
||||
country VARCHAR,
|
||||
country_code VARCHAR,
|
||||
city VARCHAR,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT,
|
||||
asn INTEGER,
|
||||
as_org VARCHAR,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO ipaddrs (id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude, asn, as_org)
|
||||
SELECT id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude, asn, as_org
|
||||
FROM _ipaddrs_old;
|
||||
|
||||
DROP TABLE _ipaddrs_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
126
migrations/2018-10-26-001932_cascade-delete/up.sql
Normal file
126
migrations/2018-10-26-001932_cascade-delete/up.sql
Normal file
@@ -0,0 +1,126 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- domains
|
||||
|
||||
ALTER TABLE domains RENAME TO _domains_old;
|
||||
|
||||
CREATE TABLE domains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
CONSTRAINT domain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO domains (id, value, unscoped)
|
||||
SELECT id, value, unscoped
|
||||
FROM _domains_old;
|
||||
|
||||
DROP TABLE _domains_old;
|
||||
|
||||
-- subdomains
|
||||
|
||||
ALTER TABLE subdomains RENAME TO _subdomains_old;
|
||||
|
||||
CREATE TABLE subdomains (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
domain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
resolvable BOOLEAN,
|
||||
FOREIGN KEY(domain_id) REFERENCES domains(id) ON DELETE CASCADE,
|
||||
CONSTRAINT subdomain_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO subdomains (id, domain_id, value, unscoped, resolvable)
|
||||
SELECT id, domain_id, value, unscoped, resolvable
|
||||
FROM _subdomains_old;
|
||||
|
||||
DROP TABLE _subdomains_old;
|
||||
|
||||
-- urls
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
online BOOLEAN,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id) ON DELETE CASCADE,
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body, unscoped, online)
|
||||
SELECT id, subdomain_id, value, status, body, unscoped, online
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
-- emails
|
||||
|
||||
ALTER TABLE emails RENAME TO _emails_old;
|
||||
|
||||
CREATE TABLE emails (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
valid BOOLEAN,
|
||||
CONSTRAINT email_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO emails (id, value, unscoped, valid)
|
||||
SELECT id, value, unscoped, valid
|
||||
FROM _emails_old;
|
||||
|
||||
DROP TABLE _emails_old;
|
||||
|
||||
-- ipaddrs
|
||||
|
||||
ALTER TABLE ipaddrs RENAME TO _ipaddrs_old;
|
||||
|
||||
CREATE TABLE ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
continent VARCHAR,
|
||||
continent_code VARCHAR,
|
||||
country VARCHAR,
|
||||
country_code VARCHAR,
|
||||
city VARCHAR,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT,
|
||||
asn INTEGER,
|
||||
as_org VARCHAR,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO ipaddrs (id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude, asn, as_org)
|
||||
SELECT id, family, value, unscoped, continent, continent_code, country, country_code, city, latitude, longitude, asn, as_org
|
||||
FROM _ipaddrs_old;
|
||||
|
||||
DROP TABLE _ipaddrs_old;
|
||||
|
||||
-- subdomain_ipaddrs
|
||||
|
||||
ALTER TABLE subdomain_ipaddrs RENAME TO _subdomain_ipaddrs_old;
|
||||
|
||||
CREATE TABLE subdomain_ipaddrs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
ip_addr_id INTEGER NOT NULL,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(ip_addr_id) REFERENCES ipaddrs(id) ON DELETE CASCADE,
|
||||
CONSTRAINT subdomain_ipaddr_unique UNIQUE (subdomain_id, ip_addr_id)
|
||||
);
|
||||
|
||||
INSERT INTO subdomain_ipaddrs (id, subdomain_id, ip_addr_id)
|
||||
SELECT id, subdomain_id, ip_addr_id
|
||||
FROM _subdomain_ipaddrs_old;
|
||||
|
||||
DROP TABLE _subdomain_ipaddrs_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
25
migrations/2018-11-03-230746_extend-urls/down.sql
Normal file
25
migrations/2018-11-03-230746_extend-urls/down.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
-- urls
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
online BOOLEAN,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id) ON DELETE CASCADE,
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body, unscoped, online)
|
||||
SELECT id, subdomain_id, value, status, body, unscoped, online
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
2
migrations/2018-11-03-230746_extend-urls/up.sql
Normal file
2
migrations/2018-11-03-230746_extend-urls/up.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE urls ADD COLUMN title VARCHAR;
|
||||
ALTER TABLE urls ADD COLUMN redirect VARCHAR;
|
||||
25
migrations/2018-12-03-143558_url_path/down.sql
Normal file
25
migrations/2018-12-03-143558_url_path/down.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
online BOOLEAN,
|
||||
title VARCHAR,
|
||||
redirect VARCHAR,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id) ON DELETE CASCADE,
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, status, body, unscoped, online, title, redirect)
|
||||
SELECT id, subdomain_id, value, status, body, unscoped, online, title, redirect
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
26
migrations/2018-12-03-143558_url_path/up.sql
Normal file
26
migrations/2018-12-03-143558_url_path/up.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
ALTER TABLE urls RENAME TO _urls_old;
|
||||
|
||||
CREATE TABLE urls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
subdomain_id INTEGER NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
path VARCHAR NOT NULL,
|
||||
status INTEGER,
|
||||
body BLOB,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
online BOOLEAN,
|
||||
title VARCHAR,
|
||||
redirect VARCHAR,
|
||||
FOREIGN KEY(subdomain_id) REFERENCES subdomains(id) ON DELETE CASCADE,
|
||||
CONSTRAINT url_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO urls (id, subdomain_id, value, path, status, body, unscoped, online, title, redirect)
|
||||
SELECT id, subdomain_id, value, '/', status, body, unscoped, online, title, redirect
|
||||
FROM _urls_old;
|
||||
|
||||
DROP TABLE _urls_old;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
1
migrations/2018-12-14-094011_phonenumbers/down.sql
Normal file
1
migrations/2018-12-14-094011_phonenumbers/down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP TABLE phonenumbers;
|
||||
16
migrations/2018-12-14-094011_phonenumbers/up.sql
Normal file
16
migrations/2018-12-14-094011_phonenumbers/up.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE phonenumbers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
name VARCHAR,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
valid BOOLEAN,
|
||||
last_online DATETIME,
|
||||
country VARCHAR,
|
||||
carrier VARCHAR,
|
||||
line VARCHAR,
|
||||
is_ported BOOLEAN,
|
||||
last_ported DATETIME,
|
||||
caller_name VARCHAR,
|
||||
caller_type VARCHAR,
|
||||
CONSTRAINT phonenumber_unique UNIQUE (value)
|
||||
);
|
||||
27
migrations/2018-12-23-230955_reverse-dns/down.sql
Normal file
27
migrations/2018-12-23-230955_reverse-dns/down.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
PRAGMA foreign_keys=off;
|
||||
|
||||
CREATE TABLE _ipaddrs_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
continent VARCHAR,
|
||||
continent_code VARCHAR,
|
||||
country VARCHAR,
|
||||
country_code VARCHAR,
|
||||
city VARCHAR,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT,
|
||||
asn INTEGER,
|
||||
as_org VARCHAR,
|
||||
CONSTRAINT ipaddr_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
INSERT INTO _ipaddrs_new (id, family, value, unscoped, continent, continent_code, city, latitude, longitude, asn, as_org)
|
||||
SELECT id, family, value, unscoped, continent, continent_code, city, latitude, longitude, asn, as_org
|
||||
FROM ipaddrs;
|
||||
|
||||
DROP TABLE ipaddrs;
|
||||
ALTER TABLE _ipaddrs_new RENAME TO ipaddrs;
|
||||
|
||||
PRAGMA foreign_keys=on;
|
||||
2
migrations/2018-12-23-230955_reverse-dns/up.sql
Normal file
2
migrations/2018-12-23-230955_reverse-dns/up.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ipaddrs ADD COLUMN description VARCHAR;
|
||||
ALTER TABLE ipaddrs ADD COLUMN reverse_dns VARCHAR;
|
||||
3
migrations/2018-12-24-141533_networks/down.sql
Normal file
3
migrations/2018-12-24-141533_networks/down.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
DROP TABLE network_devices;
|
||||
DROP TABLE networks;
|
||||
DROP TABLE devices;
|
||||
30
migrations/2018-12-24-141533_networks/up.sql
Normal file
30
migrations/2018-12-24-141533_networks/up.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
CREATE TABLE networks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
latitude FLOAT,
|
||||
longitude FLOAT,
|
||||
CONSTRAINT network_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
CREATE TABLE devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
value VARCHAR NOT NULL,
|
||||
name VARCHAR,
|
||||
hostname VARCHAR,
|
||||
vendor VARCHAR,
|
||||
unscoped BOOLEAN DEFAULT 0 NOT NULL,
|
||||
last_seen DATETIME,
|
||||
CONSTRAINT device_unique UNIQUE (value)
|
||||
);
|
||||
|
||||
CREATE TABLE network_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
network_id INTEGER NOT NULL,
|
||||
device_id INTEGER NOT NULL,
|
||||
ipaddr VARCHAR,
|
||||
last_seen DATETIME,
|
||||
FOREIGN KEY(network_id) REFERENCES networks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(device_id) REFERENCES devices(id) ON DELETE CASCADE,
|
||||
CONSTRAINT network_device_unique UNIQUE (network_id, device_id)
|
||||
);
|
||||
1
migrations/2019-01-20-000235_ttl/down.sql
Normal file
1
migrations/2019-01-20-000235_ttl/down.sql
Normal file
@@ -0,0 +1 @@
|
||||
DROP TABLE ttls;
|
||||
7
migrations/2019-01-20-000235_ttl/up.sql
Normal file
7
migrations/2019-01-20-000235_ttl/up.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE ttls (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
family VARCHAR NOT NULL,
|
||||
key INTEGER NOT NULL,
|
||||
expire DATETIME NOT NULL,
|
||||
CONSTRAINT ttl_unique UNIQUE (family, key)
|
||||
);
|
||||
@@ -1,12 +0,0 @@
|
||||
-- Description: Reproduce a rust stdlib panic
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
|
||||
function run()
|
||||
--[[
|
||||
See for details:
|
||||
https://github.com/rust-lang/rust/issues/54267
|
||||
https://github.com/rust-lang/rust/issues/39364
|
||||
]]--
|
||||
sleep(10)
|
||||
end
|
||||
45
modules/dev/arp-scan.lua
Normal file
45
modules/dev/arp-scan.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
-- Description: Parse arp-scan output
|
||||
-- Version: 0.3.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
-- sudo arp-scan -qglI wlp3s0
|
||||
|
||||
function run()
|
||||
network = getopt('network')
|
||||
if not network then
|
||||
return 'network option is missing'
|
||||
end
|
||||
|
||||
network_id = db_select('network', network)
|
||||
if not network_id then
|
||||
return 'network not found in database'
|
||||
end
|
||||
|
||||
while true do
|
||||
x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
|
||||
m = regex_find('(.+)\t(.+)', x)
|
||||
if m ~= nil then
|
||||
ipaddr = m[2]
|
||||
mac = m[3]
|
||||
now = datetime()
|
||||
|
||||
device_id = db_add('device', {
|
||||
value=mac,
|
||||
last_seen=now,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
db_add_ttl('network-device', {
|
||||
network_id=network_id,
|
||||
device_id=device_id,
|
||||
ipaddr=ipaddr,
|
||||
last_seen=now,
|
||||
}, 300)
|
||||
if last_err() then return end
|
||||
end
|
||||
end
|
||||
end
|
||||
16
modules/dev/asn.lua
Normal file
16
modules/dev/asn.lua
Normal file
@@ -0,0 +1,16 @@
|
||||
-- Description: Run a asn lookup for an ip address
|
||||
-- Version: 0.1.0
|
||||
-- Source: ipaddrs
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
lookup = asn_lookup(arg['value'])
|
||||
if last_err() then return end
|
||||
|
||||
if arg['asn'] ~= lookup['asn'] or arg['as_org'] ~= lookup['as_org'] then
|
||||
db_update('ipaddr', arg, {
|
||||
asn=lookup['asn'],
|
||||
as_org=lookup['as_org'],
|
||||
})
|
||||
end
|
||||
end
|
||||
159
modules/dev/axfr.lua
Normal file
159
modules/dev/axfr.lua
Normal file
@@ -0,0 +1,159 @@
|
||||
-- Description: Try a zone transfer for subdomains
|
||||
-- Version: 0.2.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function strip_root_dot(name)
|
||||
m = regex_find("(.+)\\.$", name)
|
||||
if last_err() then return end
|
||||
|
||||
if m == nil then
|
||||
return name
|
||||
else
|
||||
return m[2]
|
||||
end
|
||||
end
|
||||
|
||||
function add_pointer(name)
|
||||
local domain, domain_id, subdomain_id
|
||||
|
||||
-- select psl+1
|
||||
domain = psl_domain_from_dns_name(name)
|
||||
if last_err() then return end
|
||||
|
||||
-- add domain
|
||||
domain_id = db_add('domain', {
|
||||
value=domain,
|
||||
})
|
||||
if last_err() then return end
|
||||
if domain_id == nil then return end
|
||||
|
||||
-- add subdomain
|
||||
subdomain_id = db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=name,
|
||||
})
|
||||
if last_err() then return end
|
||||
end
|
||||
|
||||
function iter_axfr(zone, arg)
|
||||
local name, r, m, domain
|
||||
|
||||
debug(arg)
|
||||
|
||||
name = arg[1]
|
||||
r = arg[2]
|
||||
|
||||
-- select psl+1
|
||||
domain = psl_domain_from_dns_name(name)
|
||||
if last_err() then return end
|
||||
|
||||
-- add domain
|
||||
domain_id = db_add('domain', {
|
||||
value=domain,
|
||||
})
|
||||
if last_err() then return end
|
||||
if domain_id == nil then return end
|
||||
|
||||
-- add subdomain
|
||||
subdomain_id = db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=name,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
-- this is a A record
|
||||
if r['A'] ~= nil then
|
||||
-- add the name and ip
|
||||
ipaddr_id = db_add('ipaddr', {
|
||||
family='4',
|
||||
value=r['A'],
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
db_add('subdomain-ipaddr', {
|
||||
subdomain_id=subdomain_id,
|
||||
ip_addr_id=ipaddr_id,
|
||||
})
|
||||
if last_err() then return end
|
||||
end
|
||||
|
||||
if r['CNAME'] ~= nil then
|
||||
-- add the name and the name it's pointing to
|
||||
name = strip_root_dot(r['CNAME'])
|
||||
add_pointer(name)
|
||||
end
|
||||
|
||||
if r['NS'] ~= nil then
|
||||
-- add the name and the name it's pointing to
|
||||
name = strip_root_dot(r['NS'])
|
||||
add_pointer(name)
|
||||
end
|
||||
|
||||
if r['MX'] ~= nil then
|
||||
-- add the name and the name it's pointing to
|
||||
name = strip_root_dot(r['MX'][2])
|
||||
add_pointer(name:lower())
|
||||
end
|
||||
end
|
||||
|
||||
function iter_a(zone, arg)
|
||||
local i, records, r
|
||||
|
||||
if arg == nil then return end
|
||||
|
||||
debug('nameserver: ' .. arg)
|
||||
records = dns(zone, {
|
||||
record='AXFR',
|
||||
nameserver=arg .. ':53',
|
||||
tcp=true,
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
iter_axfr(zone, records[i])
|
||||
if last_err() then return end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
|
||||
function iter_ns(zone, arg)
|
||||
local i, records, r
|
||||
|
||||
if arg == nil then return end
|
||||
|
||||
records = dns(arg, {
|
||||
record='A',
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
r = records[i][2]
|
||||
iter_a(zone, r['A'])
|
||||
if last_err() then return end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
records = dns(arg['value'], {
|
||||
record='NS',
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
r = records[i][2]
|
||||
iter_ns(arg['value'], r['NS'])
|
||||
if last_err() then return end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
50
modules/dev/cname-harvest.lua
Normal file
50
modules/dev/cname-harvest.lua
Normal file
@@ -0,0 +1,50 @@
|
||||
-- Description: Query for CNAMES to find subdomains
|
||||
-- Version: 0.2.0
|
||||
-- Source: subdomains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function iter(r)
|
||||
if r == nil then
|
||||
return
|
||||
end
|
||||
|
||||
m = regex_find("(.+)\\.$", r)
|
||||
if last_err() then return end
|
||||
|
||||
if m == nil then
|
||||
return
|
||||
end
|
||||
r = m[2]
|
||||
|
||||
domain = psl_domain_from_dns_name(r)
|
||||
if last_err() then return end
|
||||
|
||||
domain_id = db_add('domain', {
|
||||
value=domain,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
if domain_id ~= nil then
|
||||
db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=r,
|
||||
})
|
||||
if last_err() then return end
|
||||
end
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
records = dns(arg['value'], 'A')
|
||||
if last_err() then return end
|
||||
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
r = records[i][2]
|
||||
iter(r['CNAME'])
|
||||
if last_err() then return end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
@@ -1,11 +1,59 @@
|
||||
-- Description: Query certificate transparency logs to discover subdomains
|
||||
-- Version: 0.1.0
|
||||
-- Version: 0.4.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function each_name(name)
|
||||
local domain_id, psl_domain
|
||||
|
||||
if seen[name] == 1 then
|
||||
return
|
||||
end
|
||||
seen[name] = 1
|
||||
debug(name)
|
||||
|
||||
if name:find('*.') == 1 then
|
||||
-- ignore wildcard domains
|
||||
return
|
||||
end
|
||||
|
||||
-- the cert might be valid for subdomains that do not belong to the
|
||||
-- domain we started with
|
||||
psl_domain = psl_domain_from_dns_name(name)
|
||||
domain_id = domains[psl_domain]
|
||||
if domain_id == nil then
|
||||
if any_domain then
|
||||
-- unknown domains should be added to database
|
||||
domain_id = db_add('domain', {
|
||||
value=psl_domain,
|
||||
})
|
||||
else
|
||||
-- only use domains that are already in scope
|
||||
domain_id = db_select('domain', psl_domain)
|
||||
end
|
||||
|
||||
-- if we didn't get a valid id, skip
|
||||
if domain_id == nil then
|
||||
return
|
||||
end
|
||||
|
||||
domains[psl_domain] = domain_id
|
||||
end
|
||||
|
||||
db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=name,
|
||||
})
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
full = getopt('full') ~= nil
|
||||
any_domain = getopt('any-domain') ~= nil
|
||||
|
||||
-- TODO: example.com needs to be dynamic
|
||||
domains = {}
|
||||
domains[arg['value']] = arg['id']
|
||||
|
||||
session = http_mksession()
|
||||
req = http_request(session, 'GET', 'https://crt.sh/', {
|
||||
query={
|
||||
q='%.' .. arg['value'],
|
||||
@@ -17,7 +65,7 @@ function run(arg)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
certs = json_decode_stream(resp['text'])
|
||||
certs = json_decode(resp['text'])
|
||||
if last_err() then return end
|
||||
|
||||
seen = {}
|
||||
@@ -25,22 +73,32 @@ function run(arg)
|
||||
i = 1
|
||||
while i <= #certs do
|
||||
c = certs[i]
|
||||
-- print(c)
|
||||
debug(c)
|
||||
|
||||
name = c['name_value']
|
||||
|
||||
if name:find("*.") == 1 then
|
||||
-- ignore wildcard domains
|
||||
seen[name] = 1
|
||||
end
|
||||
|
||||
if seen[name] == nil then
|
||||
-- info(name)
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=name,
|
||||
if full then
|
||||
-- fetch certificate
|
||||
id = c['min_cert_id']
|
||||
req = http_request(session, 'GET', 'https://crt.sh/', {
|
||||
query={
|
||||
d=id .. '', -- TODO: find nicer way for tostring
|
||||
}
|
||||
})
|
||||
seen[name] = 1
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
-- iterate over all valid names
|
||||
crt = x509_parse_pem(resp['text'])
|
||||
if last_err() then return end
|
||||
names = crt['valid_names']
|
||||
|
||||
j = 1
|
||||
while j <= #names do
|
||||
each_name(names[j])
|
||||
j = j+1
|
||||
end
|
||||
else
|
||||
each_name(c['name_value'])
|
||||
end
|
||||
|
||||
i = i+1
|
||||
|
||||
32
modules/dev/dns-ptr.lua
Normal file
32
modules/dev/dns-ptr.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Description: Run reverse dns lookups
|
||||
-- Version: 0.1.0
|
||||
-- Source: ipaddrs
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
if arg['family'] == '4' then
|
||||
m = regex_find('^(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)$', arg['value'])
|
||||
|
||||
q = m[5] .. '.' .. m[4] .. '.' .. m[3] .. '.' .. m[2] .. '.in-addr.arpa'
|
||||
debug('Resolving: ' .. q)
|
||||
|
||||
records = dns(q, {
|
||||
record='PTR',
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
r = records[i][2]
|
||||
if r['PTR'] then
|
||||
db_update('ipaddr', arg, {
|
||||
reverse_dns=r['PTR'],
|
||||
})
|
||||
if last_err() then return end
|
||||
end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,25 +1,30 @@
|
||||
-- Description: Query subdomains to discovery ip addresses and verify the record is visible
|
||||
-- Version: 0.1.0
|
||||
-- Version: 0.2.0
|
||||
-- Source: subdomains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
records = dns(arg['value'], 'A')
|
||||
if last_err() then return end
|
||||
|
||||
if records['success'] == nil then
|
||||
-- TODO: consider marking the subdomain is inaccessible
|
||||
-- update subdomain
|
||||
resolvable = records['error'] == nil
|
||||
if arg['resolvable'] ~= resolvable then
|
||||
-- TODO: pass arg to function as well
|
||||
db_update('subdomain', arg, {
|
||||
resolvable=resolvable
|
||||
})
|
||||
end
|
||||
|
||||
if not resolvable then
|
||||
return
|
||||
end
|
||||
|
||||
records = records['success']
|
||||
|
||||
-- there is a bug in struct -> lua that causes tables to be zero indexed
|
||||
-- this checks if there's something at index 0 but uses index 1 if this is fixed
|
||||
i = 0
|
||||
if records[i] == nil then i = 1 end
|
||||
records = records['answers']
|
||||
|
||||
i = 1
|
||||
while records[i] ~= nil do
|
||||
r = records[i]
|
||||
r = records[i][2]
|
||||
if r['A'] ~= nil then
|
||||
ipaddr_id = db_add('ipaddr', {
|
||||
family='4',
|
||||
@@ -31,6 +36,7 @@ function run(arg)
|
||||
subdomain_id=arg['id'],
|
||||
ip_addr_id=ipaddr_id,
|
||||
})
|
||||
if last_err() then return end
|
||||
end
|
||||
i = i+1
|
||||
end
|
||||
|
||||
10
modules/dev/geoip.lua
Normal file
10
modules/dev/geoip.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
-- Description: Run a geoip lookup for an ip address
|
||||
-- Version: 0.1.0
|
||||
-- Source: ipaddrs
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
lookup = geoip_lookup(arg['value'])
|
||||
if last_err() then return end
|
||||
db_update('ipaddr', arg, lookup)
|
||||
end
|
||||
28
modules/dev/git-webroot.lua
Normal file
28
modules/dev/git-webroot.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
-- Description: Search for git checkouts in webroot
|
||||
-- Version: 0.1.0
|
||||
-- Source: urls
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
url = url_join(arg['value'], '.git/HEAD')
|
||||
|
||||
session = http_mksession()
|
||||
req = http_request(session, 'GET', url, {})
|
||||
reply = http_send(req)
|
||||
if last_err() then return end
|
||||
|
||||
if reply['status'] ~= 200 then
|
||||
return
|
||||
end
|
||||
|
||||
if not regex_find('^ref: ', reply['text']) then
|
||||
return
|
||||
end
|
||||
|
||||
db_add('url', {
|
||||
subdomain_id=arg['subdomain_id'],
|
||||
value=url,
|
||||
status=reply['status'],
|
||||
body=reply['text'],
|
||||
})
|
||||
end
|
||||
30
modules/dev/hackertarget-subdomains.lua
Normal file
30
modules/dev/hackertarget-subdomains.lua
Normal file
@@ -0,0 +1,30 @@
|
||||
-- Description: Query hackertarget for subdomains of a domain
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
req = http_request(session, 'GET', 'https://api.hackertarget.com/hostsearch/', {
|
||||
query={
|
||||
q=arg['value']
|
||||
}
|
||||
})
|
||||
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
m = regex_find_all("([^,]+),.+\\n?", resp['text'])
|
||||
|
||||
i = 1
|
||||
while i <= #m do
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=m[i][2]
|
||||
})
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
78
modules/dev/isc-dhcpd-leases.lua
Normal file
78
modules/dev/isc-dhcpd-leases.lua
Normal file
@@ -0,0 +1,78 @@
|
||||
-- Description: Parse isc-dhcpd dhcpd.leases(5)
|
||||
-- Version: 0.2.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
-- cat /var/lib/dhcpd/dhcpd.leases
|
||||
|
||||
function add(lease)
|
||||
if not lease['active'] then return end
|
||||
|
||||
now = datetime()
|
||||
|
||||
device_id = db_add('device', {
|
||||
value=lease['mac'],
|
||||
hostname=lease['hostname'],
|
||||
last_seen=now,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
db_add_ttl('network-device', {
|
||||
network_id=network_id,
|
||||
device_id=device_id,
|
||||
ipaddr=lease['ipaddr'],
|
||||
last_seen=now,
|
||||
}, 180)
|
||||
if last_err() then return end
|
||||
end
|
||||
|
||||
function each_line(x)
|
||||
debug(x)
|
||||
m = regex_find('^lease (\\S+) \\{\n$', x)
|
||||
if m then
|
||||
lease = {}
|
||||
debug('ipaddr=' .. m[2])
|
||||
lease['ipaddr'] = m[2]
|
||||
end
|
||||
m = regex_find('^\\s*hardware ethernet (\\S+);\n$', x)
|
||||
if m then
|
||||
debug('mac=' .. m[2])
|
||||
lease['mac'] = m[2]
|
||||
end
|
||||
m = regex_find('^\\s*client-hostname \"(.+)\";\n$', x)
|
||||
if m then
|
||||
debug('hostname=' .. m[2])
|
||||
lease['hostname'] = m[2]
|
||||
end
|
||||
m = regex_find('^\\s*binding state active;\n$', x)
|
||||
if m then
|
||||
debug('active=true')
|
||||
lease['active'] = true
|
||||
end
|
||||
m = regex_find('^\\}\n$', x)
|
||||
if m then
|
||||
add(lease)
|
||||
end
|
||||
end
|
||||
|
||||
function run()
|
||||
network = getopt('network')
|
||||
if not network then
|
||||
return 'network option is missing'
|
||||
end
|
||||
|
||||
network_id = db_select('network', network)
|
||||
if not network_id then
|
||||
return 'network not found in database'
|
||||
end
|
||||
|
||||
while true do
|
||||
x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
|
||||
if not regex_find('^\\s*(#.*|\\s*)\n$', x) then
|
||||
each_line(x)
|
||||
end
|
||||
end
|
||||
end
|
||||
77
modules/dev/iw-station-dump.lua
Normal file
77
modules/dev/iw-station-dump.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
-- Description: Parse iw station dump
|
||||
-- Version: 0.2.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
-- iw dev wlan0 station dump
|
||||
|
||||
function add(client)
|
||||
if
|
||||
client['authenticated'] == 'yes' and
|
||||
client['authorized'] == 'yes' and
|
||||
client['mac']
|
||||
then
|
||||
debug(client)
|
||||
|
||||
now = datetime()
|
||||
|
||||
device_id = db_add('device', {
|
||||
value=client['mac'],
|
||||
last_seen=now,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
db_add_ttl('network-device', {
|
||||
network_id=network_id,
|
||||
device_id=device_id,
|
||||
last_seen=now,
|
||||
}, 180)
|
||||
if last_err() then return end
|
||||
end
|
||||
|
||||
client = nil
|
||||
end
|
||||
|
||||
function each_line(x)
|
||||
debug(x)
|
||||
m = regex_find('^Station (\\S+)', x)
|
||||
if m then
|
||||
if client then
|
||||
add(client)
|
||||
end
|
||||
client = {}
|
||||
client['mac'] = m[2]
|
||||
debug('mac=' .. m[2])
|
||||
end
|
||||
|
||||
m = regex_find('^\\s+([^:]+):\\s*(.+)\n$', x)
|
||||
if m and client then
|
||||
client[m[2]] = m[3]
|
||||
debug(m[2] .. '=' .. m[3])
|
||||
end
|
||||
end
|
||||
|
||||
function run()
|
||||
network = getopt('network')
|
||||
if not network then
|
||||
return 'network option is missing'
|
||||
end
|
||||
|
||||
network_id = db_select('network', network)
|
||||
if not network_id then
|
||||
return 'network not found in database'
|
||||
end
|
||||
|
||||
client = nil
|
||||
while true do
|
||||
x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
|
||||
each_line(x)
|
||||
end
|
||||
|
||||
if client then
|
||||
add(client)
|
||||
end
|
||||
end
|
||||
32
modules/dev/otx-subdomains.lua
Normal file
32
modules/dev/otx-subdomains.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
-- Description: Query alienvault otx passive dns for subdomains of a domain
|
||||
-- Version: 0.2.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
url = 'https://otx.alienvault.com/api/v1/indicators/domain/' .. arg['value'] .. '/passive_dns'
|
||||
|
||||
req = http_request(session, 'GET', url, {})
|
||||
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
o = json_decode(resp['text'])
|
||||
if last_err() then return end
|
||||
o = o['passive_dns']
|
||||
|
||||
i = 1
|
||||
while o[i] do
|
||||
x = o[i]
|
||||
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=x['hostname'],
|
||||
})
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
79
modules/dev/passive-arp.lua
Normal file
79
modules/dev/passive-arp.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
-- Description: Passive arp-scanner with sniffglue
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
-- sudo sniffglue -jv enp0s25
|
||||
|
||||
function each_frame(frame)
|
||||
if not frame['Ether'] then return end
|
||||
|
||||
local arp = frame['Ether'][2]['Arp']
|
||||
if not arp then return end
|
||||
|
||||
if arp['Request'] then
|
||||
arp = arp['Request']
|
||||
elseif arp['Reply'] then
|
||||
arp = arp['Reply']
|
||||
else
|
||||
-- unknown, abort
|
||||
return
|
||||
end
|
||||
|
||||
debug(arp)
|
||||
|
||||
-- TODO: this might change to a string in the future
|
||||
local mac = mac(arp['src_mac'])
|
||||
local ipaddr = arp['src_addr']
|
||||
debug({src_mac=mac, src_addr=ipaddr})
|
||||
|
||||
local now = datetime()
|
||||
|
||||
local device_id = db_add('device', {
|
||||
value=mac,
|
||||
last_seen=now,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
db_add_ttl('network-device', {
|
||||
network_id=network_id,
|
||||
device_id=device_id,
|
||||
ipaddr=ipaddr,
|
||||
last_seen=now,
|
||||
}, 120)
|
||||
if last_err() then return end
|
||||
end
|
||||
|
||||
function mac(m)
|
||||
return
|
||||
hex({m[1]}) .. ':' ..
|
||||
hex({m[2]}) .. ':' ..
|
||||
hex({m[3]}) .. ':' ..
|
||||
hex({m[4]}) .. ':' ..
|
||||
hex({m[5]}) .. ':' ..
|
||||
hex({m[6]})
|
||||
end
|
||||
|
||||
function run()
|
||||
network = getopt('network')
|
||||
if not network then
|
||||
return 'network option is missing'
|
||||
end
|
||||
|
||||
network_id = db_select('network', network)
|
||||
if not network_id then
|
||||
return 'network not found in database'
|
||||
end
|
||||
|
||||
while true do
|
||||
local x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
|
||||
local frame = json_decode(x)
|
||||
if last_err() then return end
|
||||
|
||||
each_frame(frame)
|
||||
if last_err() then return end
|
||||
end
|
||||
end
|
||||
63
modules/dev/passive-spider.lua
Normal file
63
modules/dev/passive-spider.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
-- Description: Scrape known http responses for urls
|
||||
-- Version: 0.1.0
|
||||
-- Source: urls
|
||||
-- License: GPL-3.0
|
||||
|
||||
function entry(parent, href)
|
||||
-- TODO: parse mailto:foo@example.com?subject=asdf
|
||||
-- TODO: parse tel:+4912345
|
||||
-- TODO: allow discovering 3rd-party domains
|
||||
-- TODO: maybe record urls as well
|
||||
|
||||
local psl, parts, url, host
|
||||
|
||||
if href == nil then
|
||||
return
|
||||
end
|
||||
|
||||
url = url_join(parent, href)
|
||||
if last_err() then return clear_err() end
|
||||
if url:match('^https?://') == nil then
|
||||
return
|
||||
end
|
||||
|
||||
parts = url_parse(url)
|
||||
if last_err() then return end
|
||||
host = parts['host']
|
||||
psl = psl_domain_from_dns_name(host)
|
||||
|
||||
|
||||
domain_id = db_select('domain', psl)
|
||||
if domain_id ~= nil then
|
||||
db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=host,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
if arg['body'] == nil or #arg['body'] == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
body = utf8_decode(arg['body'])
|
||||
if last_err() then return end
|
||||
|
||||
links = html_select_list(body, 'a')
|
||||
if last_err() then return end
|
||||
|
||||
if #links == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- process html links
|
||||
i = 1
|
||||
while i <= #links do
|
||||
href = links[i]['attrs']['href']
|
||||
|
||||
entry(arg['value'], href)
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
58
modules/dev/pgp-keyserver.lua
Normal file
58
modules/dev/pgp-keyserver.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
-- Description: Query pgp keyserver for email addresses
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
--lookup_url = 'https://pgp.mit.edu/pks/lookup'
|
||||
lookup_url = 'https://sks-keyservers.net/pks/lookup'
|
||||
|
||||
req = http_request(session, 'GET', lookup_url, {
|
||||
query={
|
||||
search=arg['value'],
|
||||
}
|
||||
})
|
||||
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
links = html_select_list(resp['text'], 'a')
|
||||
i = 1
|
||||
while i <= #links do
|
||||
href = links[i]['attrs']['href']
|
||||
|
||||
if href:find('/pks/lookup%?op=get&search=') == 1 then
|
||||
url = url_join(lookup_url, href)
|
||||
|
||||
req = http_request(session, 'GET', url, {})
|
||||
|
||||
resp = http_send(req)
|
||||
-- TODO: do not abort script if one attempt fails
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
pubkey = pgp_pubkey_armored(resp['text'])
|
||||
|
||||
-- print(pubkey)
|
||||
|
||||
-- TODO: ensure at least one email matches our target domain
|
||||
if pubkey['uids'] then
|
||||
j = 1
|
||||
while j <= #pubkey['uids'] do
|
||||
m = regex_find("<([^< ]+@[^< ]+)>$", pubkey['uids'][j])
|
||||
if m then
|
||||
db_add('email', {
|
||||
value=m[2],
|
||||
})
|
||||
end
|
||||
j = j+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
106
modules/dev/phpmyadmin.lua
Normal file
106
modules/dev/phpmyadmin.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
-- Description: Search for phpmyadmin
|
||||
-- Version: 0.1.0
|
||||
-- Source: urls
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
paths = {
|
||||
"phpmyadmin/index.php",
|
||||
"phpMyAdmin/index.php",
|
||||
"pmd/index.php",
|
||||
"pma/index.php",
|
||||
"PMA/index.php",
|
||||
"PMA2/index.php",
|
||||
"pmamy/index.php",
|
||||
"pmamy2/index.php",
|
||||
"mysql/index.php",
|
||||
"admin/index.php",
|
||||
"db/index.php",
|
||||
"dbadmin/index.php",
|
||||
"web/phpMyAdmin/index.php",
|
||||
"admin/pma/index.php",
|
||||
"admin/PMA/index.php",
|
||||
"admin/mysql/index.php",
|
||||
"admin/mysql2/index.php",
|
||||
"admin/phpmyadmin/index.php",
|
||||
"admin/phpMyAdmin/index.php",
|
||||
"admin/phpmyadmin2/index.php",
|
||||
"mysqladmin/index.php",
|
||||
"mysql-admin/index.php",
|
||||
"mysql_admin/index.php",
|
||||
"phpadmin/index.php",
|
||||
"phpAdmin/index.php",
|
||||
"phpmyadmin0/index.php",
|
||||
"phpmyadmin1/index.php",
|
||||
"phpmyadmin2/index.php",
|
||||
"phpMyAdmin-4.4.0/index.php",
|
||||
"myadmin/index.php",
|
||||
"myadmin2/index.php",
|
||||
"xampp/phpmyadmin/index.php",
|
||||
"phpMyadmin_bak/index.php",
|
||||
"www/phpMyAdmin/index.php",
|
||||
"tools/phpMyAdmin/index.php",
|
||||
"phpmyadmin-old/index.php",
|
||||
"phpMyAdminold/index.php",
|
||||
"phpMyAdmin.old/index.php",
|
||||
"pma-old/index.php",
|
||||
"claroline/phpMyAdmin/index.php",
|
||||
"typo3/phpmyadmin/index.php",
|
||||
"phpma/index.php",
|
||||
"phpmyadmin/phpmyadmin/index.php",
|
||||
"phpMyAdmin/phpMyAdmin/index.php",
|
||||
"phpMyAbmin/index.php",
|
||||
"phpMyAdmin__/index.php",
|
||||
"phpMyAdmin+++---/index.php",
|
||||
"v/index.php",
|
||||
"phpmyadm1n/index.php",
|
||||
"phpMyAdm1n/index.php",
|
||||
"shaAdmin/index.php",
|
||||
"phpMyadmi/index.php",
|
||||
"phpMyAdmion/index.php",
|
||||
"MyAdmin/index.php",
|
||||
"phpMyAdmin1/index.php",
|
||||
"phpMyAdmin123/index.php",
|
||||
"pwd/index.php",
|
||||
"phpMyAdmina/index.php",
|
||||
"program/index.php",
|
||||
"shopdb/index.php",
|
||||
"phppma/index.php",
|
||||
"phpmy/index.php",
|
||||
"mysql/admin/index.php",
|
||||
"mysql/dbadmin/index.php",
|
||||
"mysql/sqlmanager/index.php",
|
||||
"mysql/mysqlmanager/index.php",
|
||||
"wp-content/plugins/portable-phpmyadmin/wp-pma-mod/index.php",
|
||||
}
|
||||
|
||||
session = http_mksession()
|
||||
|
||||
i = 1
|
||||
while i <= #paths do
|
||||
p = paths[i]
|
||||
url = url_join(arg['value'], p)
|
||||
debug(url)
|
||||
|
||||
req = http_request(session, 'GET', url, {
|
||||
timeout=5000
|
||||
})
|
||||
reply = http_send(req)
|
||||
debug(reply)
|
||||
|
||||
if last_err() then
|
||||
clear_err()
|
||||
else
|
||||
if reply['status'] == 200 then
|
||||
db_add('url', {
|
||||
subdomain_id=arg['subdomain_id'],
|
||||
value=url,
|
||||
status=reply['status'],
|
||||
body=reply['text'],
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
90
modules/dev/smtp-check.lua
Normal file
90
modules/dev/smtp-check.lua
Normal file
@@ -0,0 +1,90 @@
|
||||
-- Description: Verify email address by asking the smtp server
|
||||
-- Version: 0.1.0
|
||||
-- Source: emails
|
||||
-- License: GPL-3.0
|
||||
|
||||
function find_mx(domain)
|
||||
local records, i, r
|
||||
|
||||
records = dns(domain, {
|
||||
record='MX',
|
||||
})
|
||||
if last_err() then return end
|
||||
if records['error'] ~= nil then return end
|
||||
records = records['answers']
|
||||
-- debug(records)
|
||||
|
||||
i = 1
|
||||
while i <= #records do
|
||||
r = records[i][2]['MX']
|
||||
if r then
|
||||
debug('mx: ' .. r[2])
|
||||
return r[2]
|
||||
end
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
-- extract domain
|
||||
domain = arg['value']:match('@(.*)')
|
||||
if doman ~= nil then
|
||||
-- malformed domain
|
||||
return
|
||||
end
|
||||
|
||||
-- mx lookup
|
||||
mx = find_mx(domain)
|
||||
if last_err() then return end
|
||||
if not mx then return end
|
||||
|
||||
-- create connection
|
||||
c = sock_connect(mx, 25, {})
|
||||
if last_err() then return end
|
||||
|
||||
l = sock_recvline(c)
|
||||
if last_err() then return end
|
||||
debug(l)
|
||||
|
||||
-- send hello
|
||||
sock_sendline(c, 'ehlo localhost')
|
||||
if last_err() then return end
|
||||
|
||||
l = sock_recvline_regex(c, '^250 ')
|
||||
if last_err() then return end
|
||||
debug(l)
|
||||
|
||||
-- send email
|
||||
sock_sendline(c, 'mail from:<root@localhost>')
|
||||
if last_err() then return end
|
||||
|
||||
l = sock_recvline(c)
|
||||
if last_err() then return end
|
||||
debug(l)
|
||||
|
||||
-- send rcpt
|
||||
sock_sendline(c, 'rcpt to:<' .. arg['value'] .. '>')
|
||||
if last_err() then return end
|
||||
|
||||
l = sock_recvline(c)
|
||||
if last_err() then return end
|
||||
debug(l)
|
||||
|
||||
-- check status
|
||||
verified = nil
|
||||
if l:match('^2') then
|
||||
debug('email is valid')
|
||||
verified = true
|
||||
elseif l:match('^5') then
|
||||
debug('email is invalid')
|
||||
verified = false
|
||||
elseif l:match('^4') then
|
||||
debug('unknown status, temporary delivery failure')
|
||||
end
|
||||
|
||||
if verified ~= nil then
|
||||
db_update('email', arg, {
|
||||
valid=verified,
|
||||
})
|
||||
end
|
||||
end
|
||||
52
modules/dev/threatminer-ipaddr.lua
Normal file
52
modules/dev/threatminer-ipaddr.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
-- Description: Query ThreatMiner passive dns for subdomains of an ip address
|
||||
-- Version: 0.2.0
|
||||
-- Source: ipaddrs
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
-- TODO: add option to filter old entries based on last_seen
|
||||
|
||||
req = http_request(session, 'GET', 'https://api.threatminer.org/v2/host.php', {
|
||||
query={
|
||||
rt='2',
|
||||
q=arg['value']
|
||||
}
|
||||
})
|
||||
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
o = json_decode(resp['text'])
|
||||
if last_err() then return end
|
||||
o = o['results']
|
||||
|
||||
i = 1
|
||||
while o[i] do
|
||||
x = o[i]
|
||||
|
||||
domain = psl_domain_from_dns_name(x['domain'])
|
||||
-- TODO: if this fails, skip this entry instead
|
||||
if last_err() then return end
|
||||
|
||||
domain_id = db_add('domain', {
|
||||
value=domain,
|
||||
})
|
||||
|
||||
if domain_id ~= nil then
|
||||
subdomain_id = db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=x['domain'],
|
||||
})
|
||||
|
||||
db_add('subdomain-ipaddr', {
|
||||
subdomain_id=subdomain_id,
|
||||
ip_addr_id=arg['id'],
|
||||
})
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
35
modules/dev/threatminer-subdomains.lua
Normal file
35
modules/dev/threatminer-subdomains.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Description: Query ThreatMiner passive dns for subdomains of a domain
|
||||
-- Version: 0.2.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
req = http_request(session, 'GET', 'https://api.threatminer.org/v2/domain.php', {
|
||||
query={
|
||||
rt='5',
|
||||
q=arg['value']
|
||||
}
|
||||
})
|
||||
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
if resp['status'] ~= 200 then return 'http error: ' .. resp['status'] end
|
||||
|
||||
o = json_decode(resp['text'])
|
||||
if last_err() then return end
|
||||
o = o['results']
|
||||
|
||||
i = 1
|
||||
while o[i] do
|
||||
x = o[i]
|
||||
|
||||
db_add('subdomain', {
|
||||
domain_id=arg['id'],
|
||||
value=x,
|
||||
})
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
48
modules/dev/thunderbird-autoconfig.lua
Normal file
48
modules/dev/thunderbird-autoconfig.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Description: Query thunderbird autoconfig db for subdomains
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
session = http_mksession()
|
||||
|
||||
-- check if an autoconfig exists without disclosing our target yet
|
||||
req = http_request(session, 'GET', 'https://autoconfig.thunderbird.net/v1.1/', {})
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
|
||||
if resp['status'] ~= 200 then
|
||||
return 'index request failed'
|
||||
end
|
||||
|
||||
if resp['text']:find(arg['value'], 1, true) == nil then
|
||||
debug('no autoconfig available')
|
||||
return
|
||||
end
|
||||
|
||||
-- request config
|
||||
req = http_request(session, 'GET', 'https://autoconfig.thunderbird.net/v1.1/' .. arg['value'], {})
|
||||
resp = http_send(req)
|
||||
if last_err() then return end
|
||||
|
||||
m = regex_find_all('<hostname>([^<]+)</hostname>', resp['text'])
|
||||
|
||||
i = 1
|
||||
while i <= #m do
|
||||
subdomain = m[i][2]
|
||||
|
||||
domain = psl_domain_from_dns_name(subdomain)
|
||||
if last_err() then return end
|
||||
|
||||
domain_id = db_select('domain', domain)
|
||||
if last_err() then return end
|
||||
|
||||
db_add('subdomain', {
|
||||
domain_id=domain_id,
|
||||
value=subdomain,
|
||||
})
|
||||
if last_err() then return end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
48
modules/dev/twilio-lookup.lua
Normal file
48
modules/dev/twilio-lookup.lua
Normal file
@@ -0,0 +1,48 @@
|
||||
-- Description: Retrieve additional information about a phone number
|
||||
-- Version: 0.1.0
|
||||
-- Source: phonenumbers
|
||||
-- Keyring-Access: twilio
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
number = url_escape(arg['value'])
|
||||
--url = 'https://lookups.twilio.com/v1/PhoneNumbers/' .. number
|
||||
url = 'https://lookups.twilio.com/v1/PhoneNumbers/' .. number .. '?Type=carrier&Type=caller-name'
|
||||
|
||||
--debug(url)
|
||||
|
||||
key = keyring('twilio')[1]
|
||||
if not key then
|
||||
return 'Missing required twilio access key'
|
||||
end
|
||||
|
||||
session = http_mksession()
|
||||
req = http_request(session, 'GET', url, {
|
||||
basic_auth={key['access_key'], key['secret_key']},
|
||||
})
|
||||
reply = http_send(req)
|
||||
if last_err() then return end
|
||||
|
||||
if reply['status'] ~= 200 then
|
||||
return 'api returned error'
|
||||
end
|
||||
|
||||
v = json_decode(reply['text'])
|
||||
if last_err() then return end
|
||||
debug(v)
|
||||
|
||||
update = {}
|
||||
update['country'] = v['country_code']
|
||||
|
||||
if v['carrier'] then
|
||||
update['carrier'] = v['carrier']['name']
|
||||
update['line'] = v['carrier']['type']
|
||||
end
|
||||
|
||||
if v['caller_name'] then
|
||||
update['caller_name'] = v['caller_name']['caller_name']
|
||||
update['caller_type'] = v['caller_name']['caller_type']
|
||||
end
|
||||
|
||||
db_update('phonenumber', arg, update)
|
||||
end
|
||||
@@ -1,6 +1,7 @@
|
||||
-- Description: Check subdomains for websites
|
||||
-- Version: 0.1.0
|
||||
-- Description: Scan subdomains for websites
|
||||
-- Version: 0.3.0
|
||||
-- Source: subdomains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function request(subdomain_id, url)
|
||||
req = http_request(session, 'GET', url, {
|
||||
@@ -13,16 +14,23 @@ function request(subdomain_id, url)
|
||||
return
|
||||
end
|
||||
|
||||
db_add('url', {
|
||||
obj = {
|
||||
subdomain_id=subdomain_id,
|
||||
value=url,
|
||||
status=reply['status'],
|
||||
body=reply['text'],
|
||||
})
|
||||
}
|
||||
|
||||
-- info(json_encode(reply['status']))
|
||||
-- info(json_encode(reply['headers']['location']))
|
||||
-- info(json_encode(reply['text']))
|
||||
redirect = reply['headers']['location']
|
||||
if redirect then
|
||||
obj['redirect'] = url_join(url, redirect)
|
||||
end
|
||||
|
||||
db_add('url', obj)
|
||||
|
||||
-- debug(reply['status'])
|
||||
-- debug(reply['headers']['location'])
|
||||
-- debug(reply['text'])
|
||||
end
|
||||
|
||||
function run(arg)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
-- Description: Discover subdomains from wayback machine
|
||||
-- Version: 0.1.0
|
||||
-- Version: 0.3.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
|
||||
@@ -17,21 +18,21 @@ function run(arg)
|
||||
if last_err() then return end
|
||||
|
||||
-- no known urls
|
||||
if o[0] == nil then
|
||||
if o[1] == nil then
|
||||
return
|
||||
end
|
||||
|
||||
-- ensure the api response is still what we expect
|
||||
if o[0][2] == nil then
|
||||
if o[1][3] == nil then
|
||||
return 'api returned unexpected json format'
|
||||
end
|
||||
|
||||
seen = {}
|
||||
|
||||
i = 1
|
||||
i = 2
|
||||
while o[i] do
|
||||
url = o[i][2]
|
||||
|
||||
url = o[i][3]
|
||||
debug(url)
|
||||
parts = url_parse(url)
|
||||
|
||||
if last_err() then
|
||||
@@ -39,6 +40,7 @@ function run(arg)
|
||||
error("Failed to parse url: " .. json_encode(url))
|
||||
else
|
||||
subdomain = parts['host']
|
||||
subdomain, _ = subdomain:gsub('%.$', '')
|
||||
|
||||
if seen[subdomain] == nil then
|
||||
db_add('subdomain', {
|
||||
|
||||
64
modules/dev/well-known-uris.lua
Normal file
64
modules/dev/well-known-uris.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
-- Description: Scan for known /.well-known/ locations
|
||||
-- Version: 0.1.0
|
||||
-- Source: urls
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
-- https://www.iana.org/assignments/well-known-uris/well-known-uris.xhtml
|
||||
-- https://en.wikipedia.org/wiki/List_of_/.well-known/_services_offered_by_webservers
|
||||
|
||||
-- TODO: check if every location causes a 200/redirect
|
||||
|
||||
locations = {
|
||||
{path='security.txt'}, -- expect 200
|
||||
{path='dnt-policy.txt'}, -- expect 200
|
||||
{path='caldav', redirect=true}, -- expect redirect
|
||||
{path='autoconfig/mail/config-v1.1.xml'}, -- expect 200
|
||||
{path='assetlinks.json'}, -- expect 200
|
||||
{path='apple-app-site-association'}, -- expect 200
|
||||
{path='keybase.txt'}, -- expect 200
|
||||
{path='apple-developer-merchantid-domain-association'}, -- expect 200
|
||||
{path='openpgpkey'}, -- expect 200
|
||||
{path='change-password', redirect=true}, -- expect redirect
|
||||
}
|
||||
|
||||
session = http_mksession()
|
||||
|
||||
i = 1
|
||||
while i <= #locations do
|
||||
path = locations[i]['path']
|
||||
expect_redirect = locations[i]['redirect']
|
||||
|
||||
url = url_join(arg['value'], '/.well-known/' .. path)
|
||||
debug(url)
|
||||
|
||||
req = http_request(session, 'GET', url, {
|
||||
timeout=5000,
|
||||
})
|
||||
reply = http_send(req)
|
||||
debug(reply)
|
||||
|
||||
if last_err() then
|
||||
clear_err()
|
||||
else
|
||||
status = reply['status']
|
||||
if (status == 200 and not expect_redirect) or (expect_redirect and status >= 300 and status < 400) then
|
||||
obj = {
|
||||
subdomain_id=arg['subdomain_id'],
|
||||
value=url,
|
||||
status=reply['status'],
|
||||
body=reply['text'],
|
||||
}
|
||||
|
||||
redirect = reply['headers']['location']
|
||||
if redirect then
|
||||
obj['redirect'] = url_join(url, redirect)
|
||||
end
|
||||
|
||||
db_add('url', obj)
|
||||
end
|
||||
end
|
||||
|
||||
i = i+1
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,8 @@
|
||||
-- Description: Test error handling
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
error('This is a non fatal error')
|
||||
return "This is an error: " .. 123
|
||||
end
|
||||
15
modules/harness/ip.lua
Normal file
15
modules/harness/ip.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
-- Description: Show your ip
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function get(url)
|
||||
req = http_request(session, 'GET', url, {})
|
||||
r = http_send(req)
|
||||
info(r['text'])
|
||||
end
|
||||
|
||||
function run()
|
||||
session = http_mksession()
|
||||
get('https://icanhazip.com')
|
||||
get('https://icanhazptr.com')
|
||||
end
|
||||
9
modules/harness/keyring.lua
Normal file
9
modules/harness/keyring.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Description: Request access to keyring
|
||||
-- Version: 0.1.0
|
||||
-- Keyring-Access: twilio
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
keys = keyring('twilio')
|
||||
debug(keys)
|
||||
end
|
||||
9
modules/harness/keyring2.lua
Normal file
9
modules/harness/keyring2.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Description: Request access to keyring
|
||||
-- Version: 0.1.0
|
||||
-- Source: keyring:twilio
|
||||
-- Keyring-Access: twilio
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run(arg)
|
||||
info(arg)
|
||||
end
|
||||
7
modules/harness/options.lua
Normal file
7
modules/harness/options.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
-- Description: Read an option
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
info(getopt('hello'))
|
||||
end
|
||||
8
modules/harness/selftest.lua
Normal file
8
modules/harness/selftest.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Description: basic selftest
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
-- nothing to do here
|
||||
info('ohai')
|
||||
end
|
||||
8
modules/harness/sleep.lua
Normal file
8
modules/harness/sleep.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Description: Sleep for 10 seconds
|
||||
-- Version: 0.1.0
|
||||
-- Source: domains
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
sleep(10)
|
||||
end
|
||||
13
modules/harness/stdin.lua
Normal file
13
modules/harness/stdin.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
-- Description: Read from stdin
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
while true do
|
||||
x = stdin_readline()
|
||||
if x == nil then
|
||||
break
|
||||
end
|
||||
info(x)
|
||||
end
|
||||
end
|
||||
18
modules/harness/tcp-hello.lua
Normal file
18
modules/harness/tcp-hello.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Description: Send a hello to a server on port 1337
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
addr = getopt('addr')
|
||||
if not addr then
|
||||
return 'addr is not set'
|
||||
end
|
||||
|
||||
-- create connection
|
||||
c = sock_connect(addr, 1337, {})
|
||||
if last_err() then return end
|
||||
|
||||
-- send ohai
|
||||
sock_sendline(c, 'ohai')
|
||||
if last_err() then return end
|
||||
end
|
||||
9
modules/harness/ttl.lua
Normal file
9
modules/harness/ttl.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Description: Add an expiring domain
|
||||
-- Version: 0.1.0
|
||||
-- License: GPL-3.0
|
||||
|
||||
function run()
|
||||
db_add_ttl('domain', {
|
||||
value='example.com',
|
||||
}, 30)
|
||||
end
|
||||
@@ -1,16 +1,17 @@
|
||||
[package]
|
||||
name = "sn0int-registry"
|
||||
version = "0.1.0"
|
||||
version = "0.5.0"
|
||||
description = "sn0int registry"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
license = "GPL-3.0"
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
sn0int-common = { version="0.1.0", path="sn0int-common" }
|
||||
rocket = "0.3.16"
|
||||
rocket_codegen = "0.3.16"
|
||||
rocket_contrib = { version = "0.3.16", features = ["handlebars_templates"] }
|
||||
sn0int-common = { version="0.4.0", path="sn0int-common" }
|
||||
rocket = "0.4"
|
||||
rocket_failure = { version = "0.1", features = ["with-rocket"] }
|
||||
rocket_contrib = { version = "0.4", features = ["handlebars_templates"] }
|
||||
|
||||
diesel = { version = "1.3", features = ["postgres", "r2d2"] }
|
||||
diesel_migrations = { version = "1.3.0", features = ["postgres"] }
|
||||
@@ -22,8 +23,13 @@ failure = "0.1"
|
||||
url = "1.0"
|
||||
log = "0.4"
|
||||
semver = "0.9.0"
|
||||
lazy_static = "1.1"
|
||||
blake2 = "0.8.0"
|
||||
hex = "0.3.1"
|
||||
maplit = "1.0.1"
|
||||
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
dotenv = "0.13"
|
||||
|
||||
@@ -19,3 +19,17 @@ h1 a {
|
||||
text-decoration: none;
|
||||
color: #00aa00;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00aa00;
|
||||
}
|
||||
|
||||
.code {
|
||||
background-color: #222;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.list-unstyled {
|
||||
list-style: none;
|
||||
padding: 0 0 0 20px;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
[package]
|
||||
name = "sn0int-common"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
description = "Common code for sn0int"
|
||||
authors = ["kpcyrd <git@rxv.cc>"]
|
||||
license = "GPL-3.0"
|
||||
repository = "https://github.com/kpcyrd/sn0int"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
#rocket_failure = { path = "../../../rocket_failure" }
|
||||
rocket_failure = "0.1.1"
|
||||
failure = "0.1"
|
||||
nom = "4.0"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user