Amass Workshop
Scripting OWASP Amass for a Customized Experience
Red Team Village
Would you like greater control over your recon process?
Agenda
Speaker
Jeff Foley
Project Leader, OWASP
The Amass Flagship Project
Over 20 years of experience focused on applied research & development, security assessment, vulnerability management, and attack surface management:
GitHub: @caffix
Discord: @caffix
Twitter: @jeff_foley
Email: jeff.foley@owasp.org
Mastodon: @caffix@infosec.exchange
LinkedIn: https://www.linkedin.com/in/caffix
RTV - Volunteers
James Bettke
Security Researcher, ZeroFox
Adem Rosic
Security Researcher, ZeroFox
Introduction
What is OWASP Amass?
Where can you find OWASP Amass?
Why was the Amass Project Started?
Following Along with the Exercises
The Open Asset Model
The Announcement at DEF CON 30!
Amass Project Leader announced that its definition of attack surface would be broadened to go beyond IT and internet infrastructure assets.
Jeff Foley, Jason Haddix and Ben Sadeghipour at the Recon Village Attack Surface Management panel discussion.
What exactly did this mean for Amass?
What is the Open Asset Model?
What is the Asset Database?
Under the Hood
ID | Type | From | To |
1 | a_record | 1 | 2 |
2 | contains | 3 | 2 |
3 | announces | 4 | 3 |
4 | managed_by | 4 | 5 |
ID | Type | JSON |
1 | FQDN | {name: ‘owasp.org’} |
2 | IPAddress | {address: ‘172.67.10.39’} |
3 | Netblock | {cidr: 172.67.10.0/25} |
4 | ASN | {number: 13335} |
5 | RIROrg | {name: ‘Cloudflare, Inc.’,�desc: ‘AS13335’} |
Assets
Relations
The Advantages
KEY BENEFITS
The relations tie everything together, making it possible to navigate the data in new ways
Ask new questions from your data while not having to rescan targets
The design and taxonomy are very extendable, allowing the community to create new asset types
Open Asset Model Workshop Exercises
Exploring the Collected Data
Extraction of Subdomains
sqlite3 amass.sqlite "SELECT content->>'name' FROM assets WHERE type = 'FQDN' AND content->>'name' LIKE '%owasp.org'"
SELECT content->>’name’ – Request the FQDN names�FROM assets �WHERE type = ‘FQDN’ – We are only interested in FQDN assets�AND content->>’name’ LIKE ‘%owasp.org’ – Filter by namespace
Names and IP Addresses
sqlite3 amass.sqlite "SELECT fqdns.content->>'name',ips.content->>'address' FROM ((assets AS fqdns INNER JOIN relations ON fqdns.id = relations.from_asset_id) INNER JOIN assets AS ips ON relations.to_asset_id = ips.id) WHERE fqdns.type = 'FQDN' AND ips.type = 'IPAddress' AND relations.type IN ('a_record', 'aaaa_record') AND fqdns.content->>’name’ LIKE ‘%owasp.org’'"
SELECT fqdns.content->>'name',ips.content->>'address' – Request the JSON data�FROM ((assets AS fqdns �INNER JOIN relations ON fqdns.id = relations.from_asset_id) �INNER JOIN assets AS ips ON relations.to_asset_id = ips.id) �WHERE fqdns.type = 'FQDN' AND ips.type = 'IPAddress' – Only specific assets types�AND relations.type IN ('a_record', 'aaaa_record') – Multiple types of interest�AND fqdns.content->>’name’ LIKE ‘%owasp.org’ – Filter by namespace
Extract Zones in the Namespace
sqlite3 amass.sqlite "SELECT content->>'name' FROM assets WHERE type = 'FQDN' AND content->>'name' LIKE '%owasp.org' AND id IN (SELECT from_asset_id FROM relations WHERE type = 'node')"
SELECT content->>’name’ – Request the JSON data�FROM assets�WHERE type = 'FQDN' – Only specific assets types
AND content->>’name’ LIKE ‘%owasp.org’ – Filter by namespace�AND id IN (SELECT from_asset_id FROM relations WHERE type = ‘node’) – Apex?�
Names Resolve to what CIDRs?
sqlite3 amass.sqlite "SELECT DISTINCT nets.content->>'cidr' FROM ((((assets AS fqdns INNER JOIN relations AS rels1 ON fqdns.id = rels1.from_asset_id) INNER JOIN assets AS ips ON rels1.to_asset_id = ips.id) INNER JOIN relations AS rels2 ON ips.id = rels2.to_asset_id) INNER JOIN assets AS nets ON rels2.from_asset_id = nets.id) WHERE rels1.type IN ('a_record', 'aaaa_record') AND rels2.type = 'contains' AND rels1.last_seen > '2023-08-01 00:00:00' AND rels2.last_seen > '2023-08-01 00:00:00' AND fqdns.type = 'FQDN' AND ips.type = 'IPAddress' AND nets.type = 'Netblock' AND fqdns.content->>'name' LIKE '%owasp.org'"
SELECT DISTINCT nets.content->>'cidr' -- Requesting unique CIDR blocks
FROM ((((assets AS fqdns
INNER JOIN relations AS rels1 ON fqdns.id = rels1.from_asset_id) -- Connect FQDNs and IP addresses
INNER JOIN assets AS ips ON rels1.to_asset_id = ips.id)
INNER JOIN relations AS rels2 ON ips.id = rels2.to_asset_id) -- Connect Netblocks to IP addresses
INNER JOIN assets AS nets ON rels2.from_asset_id = nets.id)
WHERE rels1.type IN ('a_record', 'aaaa_record') AND rels2.type = 'contains' -- Check for the correct types of relations
AND rels1.last_seen > '2023-08-01 00:00:00' AND rels2.last_seen > '2023-08-01 00:00:00' -- Filter by date/time
AND fqdns.type = 'FQDN' AND ips.type = 'IPAddress' AND nets.type = 'Netblock' -- Check for the correct asset types
AND fqdns.content->>'name' LIKE '%owasp.org' -- All the FQDNs must exist in the DNS namespace of interest
Extra Example: Name Servers
sqlite3 amass.sqlite "SELECT apex.content->>'name',servers.content->>'name' FROM ((assets AS apex INNER JOIN relations ON apex.id = relations.from_asset_id) INNER JOIN assets AS servers ON relations.to_asset_id = servers.id) WHERE apex.type = 'FQDN' AND servers.type = 'FQDN' AND relations.type = 'ns_record' AND apex.content->>'name' LIKE '%owasp.org'"
SELECT apex.content->>'name',servers.content->>'name' – Request the JSON data
FROM ((assets AS apex
INNER JOIN relations ON apex.id = relations.from_asset_id)
INNER JOIN assets AS servers ON relations.to_asset_id = servers.id)
WHERE apex.type = 'FQDN' AND servers.type = 'FQDN' – Only specific assets types
AND relations.type = 'ns_record'
AND apex.content->>'name' LIKE '%owasp.org' – Filter by namespace�
Extra Example: Dangling PTR Records
sqlite3 amass.sqlite "SELECT ptrs.content->>'name',fqdns.content->>'name' FROM ((assets AS ptrs INNER JOIN relations ON ptrs.id = relations.from_asset_id) INNER JOIN assets AS fqdns ON relations.to_asset_id = fqdns.id) WHERE ptrs.type = 'FQDN' AND fqdns.type = 'FQDN' AND relations.type = 'ptr_record' AND fqdns.content->>'name' LIKE '%owasp.org' AND fqdns.id NOT IN (SELECT from_asset_id FROM relations WHERE type IN ('a_record','aaaa_record','cname_record'))"
SELECT ptrs.content->>'name',fqdns.content->>’name’ – Request the JSON data�FROM ((assets AS ptrs �INNER JOIN relations ON ptrs.id = relations.from_asset_id) �INNER JOIN assets AS fqdns ON relations.to_asset_id = fqdns.id) �WHERE ptrs.type = 'FQDN' AND fqdns.type = ‘FQDN’ – Only specific assets types�AND relations.type = 'ptr_record'
AND fqdns.content->>’name’ LIKE ‘%owasp.org’ – Filter by namespace�AND fqdns.id NOT IN (SELECT from_asset_id FROM relations �WHERE type IN (‘a_record’,’aaaa_record’,’cname_record’)) – Dangling records?�
Extra Example: Orgs Managing the CIDRs
sqlite3 amass.sqlite "SELECT nets.content->>'cidr',asns.content->>’number’,orgs.content->>'name' FROM ((((assets AS nets INNER JOIN relations AS rels1 ON nets.id = rels1.to_asset_id) INNER JOIN assets AS asns ON rels1.from_asset_id = asns.id) INNER JOIN relations AS rels2 ON asns.id = rels2.from_asset_id) INNER JOIN assets AS orgs ON rels2.to_asset_id = orgs.id) WHERE rels1.type = 'announces' AND rels2.type = 'managed_by' AND orgs.type = 'RIROrg' AND asns.type = 'ASN' AND nets.type = 'Netblock' AND nets.content->>'cidr' IN ('172.67.0.0/16','2606:4700:10::/44','104.22.16.0/20','13.225.60.0/22')"
SELECT nets.content->>’cidr’,asns.content->>’number’,orgs.content->>’name’ -- Requesting org names
FROM ((((assets AS nets
INNER JOIN relations AS rels1 ON nets.id = rels1.to_asset_id) -- Connect Netblocks and CIDRs
INNER JOIN assets AS asns ON rels1.from_asset_id = asns.id)
INNER JOIN relations AS rels2 ON asns.id = rels2.from_asset_id) -- Connect Netblocks to IP addresses
INNER JOIN assets AS orgs ON rels2.to_asset_id = orgs.id)
WHERE rels1.type = ‘announces’ AND rels2.type = 'managed_by' -- Check for the correct types of relations
AND orgs.type = 'RIROrg' AND asns.type = 'ASN' AND nets.type = 'Netblock' -- Check for the correct asset types
AND nets.content->>'cidr' IN ('172.67.0.0/16',’2606:4700:10::/44’,’104.22.16.0/20’,’13.225.60.0/22’)
Amass Data Source Scripting
How Do They Work?
How Do They Work Cont?
The Events That Are Supported
Let’s Take a Look at One
Writing a Simple Script
name = "Simple"
type = "ext"
function vertical(ctx, domain)
local path = output_dir(ctx) .. "/names.txt"
local f = io.open(path, "r")
send_names(ctx, f:read("*all"))
f:close()
end
Script that Executes a Tool
name = "Assetfinder"
type = "ext"
function vertical(ctx, domain)
local cmd = output_dir(ctx) .. "/assetfinder --subs-only " .. domain
local data = assert(io.popen(cmd))
for line in data:lines() do
new_name(ctx, line)
end
data:close()
end
Basic Testing Techniques
Integration with a Data Source
Integration with a Data Source Cont
Wrapping Up
Improve Recon Processes
Hub and Spoke Approach
Managing Output from Tools
OAM
Continuous Reconnaissance
Extract the information in scope of the current recon task / tool for use as seed data
Retrieval
Newly discovered and/or modified assets are quickly shared with appropriate systems and practitioners
Notify
The tool collects the intelligence it’s designed to gather and prepares it for storage
Discovery
Contribute findings to the OAM database for easy access to other discovery and analysis tools
Store
OAM
01
02
03
04
Imagine the OAM with New Asset Types
IT Assets
Non-IT Assets
The OAM is Community-Driven
Thank you
Questions?