Saltar al contenido principal

OSINT — Guided walkthrough

One target. A real one. We will send it nothing. And at the end, we will know: its subdomains, its technologies, its key employees, its leaks, and its five likely weak points.

Choosing the target for this walkthrough

We use a public bug bounty program, where the company explicitly authorizes testing inside a defined scope. You can do the same at home by picking a program on HackerOne or Bugcrowd. In the walkthrough that follows, we use a fictional name demo-corp.com to illustrate — but everything you see is reproducible on a real, authorized target.

What you will be able to do after this lesson​

  • Go from a bare domain name to an infrastructure map in 90 minutes.
  • Use crt.sh, subfinder, amass -passive, dnsx (passive mode), theHarvester, whatweb.
  • Tell live subdomains from dead ones without touching them.
  • Cross-check three independent sources before you conclude.
  • Produce a 2-page target sheet you can use as soon as module 4.

Step 0 — The starting point​

A single line:

Cible : demo-corp.com
Programme : bug bounty public, scope in-scope = *.demo-corp.com

That is all. We start from there.

Create the working folder:

mkdir -p ~/osint/demo-corp/{dns,subs,tech,people,leaks,archive}
cd ~/osint/demo-corp

Each family of sources gets its own subdirectory. Discipline from the first minute.


Step 1 — Basic DNS (2 minutes)​

The essential records, one by one:

for t in A AAAA MX NS TXT SOA CAA; do
echo "=== $t ==="
dig +short demo-corp.com $t
done | tee dns/racine.txt

Typical output:

=== A ===
104.21.44.15
172.67.187.219
=== AAAA ===
=== MX ===
1 aspmx.l.google.com.
5 alt1.aspmx.l.google.com.
=== NS ===
ada.ns.cloudflare.com.
kirk.ns.cloudflare.com.
=== TXT ===
"v=spf1 include:_spf.google.com include:mailgun.org -all"
"google-site-verification=abc123def456..."
"MS=ms84726311"
"stripe-verification=xyz789..."
=== SOA ===
ada.ns.cloudflare.com. dns.cloudflare.com. 2354...
=== CAA ===
0 issue "letsencrypt.org"
0 issue "digicert.com"

Reading it:

  • A: two IPs in the Cloudflare pool (104.21.x and 172.67.x). The site sits behind Cloudflare — you will not see the real IP without extra work.
  • MX: Google Workspace. No mail server of their own, directly.
  • NS: ada.ns.cloudflare.com — Cloudflare manages DNS as well. Consistent.
  • TXT:
    • SPF allows Google and Mailgun. Two SaaS services identified.
    • google-site-verification: they use Google Search Console.
    • MS=: they use Microsoft (often Bing Webmaster).
    • stripe-verification: they process Stripe payments. That means PCI-DSS lives somewhere.
  • CAA: only Let's Encrypt and DigiCert may issue. A good hygiene signal.

In 2 minutes, we already know: Cloudflare, Google Workspace, Mailgun, Stripe. We sent no packet to demo-corp.com.


Step 2 — DMARC and email posture (1 minute)​

dig +short _dmarc.demo-corp.com TXT

Output:

"v=DMARC1; p=none; rua=mailto:dmarc-reports@demo-corp.com"

Reading it: p=none. The DMARC policy is there but does nothing. Concrete result: an attacker can spoof emails that pretend to come from demo-corp.com; those emails will still reach the recipient even when they fail DMARC. That is a phishing vector to note for module 6.

Save that in dns/dmarc.txt.


Step 3 — Subdomains via crt.sh (3 minutes)​

Certificate Transparency, the best source for the first wave of subdomains. We query in JSON and clean with jq:

curl -s 'https://crt.sh/?q=%25.demo-corp.com&output=json' \
| jq -r '.[].name_value' \
| tr '[:upper:]' '[:lower:]' \
| sed 's/^\*\.//' \
| sort -u \
> subs/from-crt.txt

wc -l subs/from-crt.txt

Typical output:

127 subs/from-crt.txt

An excerpt:

admin.demo-corp.com
api.demo-corp.com
api-dev.demo-corp.com
api-staging.demo-corp.com
beta.demo-corp.com
blog.demo-corp.com
cdn.demo-corp.com
customer.demo-corp.com
demo-corp.com
dev.demo-corp.com
docs.demo-corp.com
help.demo-corp.com
internal.demo-corp.com
jenkins.demo-corp.com
jira.demo-corp.com
mail.demo-corp.com
monitoring.demo-corp.com
old-admin.demo-corp.com
partners.demo-corp.com
prod.demo-corp.com
sso.demo-corp.com
staging.demo-corp.com
status.demo-corp.com
support.demo-corp.com
vpn.demo-corp.com
web.demo-corp.com
www.demo-corp.com

Three lines that make an attacker smile:

  1. internal.demo-corp.com — a subdomain that should never have leaked out.
  2. jenkins.demo-corp.com — a CI/CD server, historically a mine of flaws.
  3. old-admin.demo-corp.com — the old admin panel. That one smells like something forgotten.

In 3 minutes, we went from 1 name to 127.


Step 4 — Cross-check with subfinder and amass -passive​

subfinder queries about twenty third-party APIs (Shodan, VirusTotal, SecurityTrails, DNSDumpster…) and returns a list. 100% passive.

subfinder -d demo-corp.com -silent -all -o subs/from-subfinder.txt
wc -l subs/from-subfinder.txt
143 subs/from-subfinder.txt

Same idea, amass in passive mode:

amass enum -passive -d demo-corp.com -o subs/from-amass.txt

Then we merge:

cat subs/from-*.txt | sort -u > subs/all-passive.txt
wc -l subs/all-passive.txt
178 subs/all-passive.txt

From 127 to 178. Each source brought a few finds. That is why we cross-check.


Step 5 — Passive resolution with dnsx​

Watch this: dnsx resolves names. Each resolution goes through a public DNS resolver (Google 8.8.8.8 by default). It is still passive for the target if you use a resolver that does not belong to the target. Check that the NS of demo-corp.com are not your resolvers (they are not, here: Cloudflare manages their DNS, but your dnsx will query Cloudflare Public 1.1.1.1 — a different service).

dnsx -l subs/all-passive.txt -a -resp -silent -r 1.1.1.1,8.8.8.8 \
-o subs/all-resolved.txt
head subs/all-resolved.txt

Output:

admin.demo-corp.com [104.21.44.15]
api.demo-corp.com [104.21.44.15]
api-dev.demo-corp.com [3.98.14.202]
api-staging.demo-corp.com [104.21.44.15]
internal.demo-corp.com [192.168.10.5]
jenkins.demo-corp.com [3.98.14.209]
old-admin.demo-corp.com [66.42.11.198]

Three lines that talk:

  • internal.demo-corp.com resolves to 192.168.10.5 — a private IP published in public DNS. The subdomain is meant to be reached over VPN, but its presence in DNS confirms the service exists. Flag it.
  • api-dev.demo-corp.com on 3.98.x.x — AWS ca-central-1 block, not behind Cloudflare. That is the real server, exposed. Priority target for the active phase.
  • old-admin.demo-corp.com on 66.42.x.x — Vultr. A forgotten server, outside the main CDN. Priority target as well.

Note these three lines. They will steer the whole of module 4.


Step 6 — Passive technology fingerprinting​

Watch this: whatweb and wappalyzer load the page — that is active. To stay strictly passive, we use:

  • BuiltWith (through the web UI from your browser — but your browser loads BuiltWith, not the target).
  • Shodan: shodan host <IP> returns what Shodan has already collected.
shodan host 3.98.14.202 > tech/shodan-api-dev.txt
cat tech/shodan-api-dev.txt

Typical output:

3.98.14.202
Hostnames: ec2-3-98-14-202.ca-central-1.compute.amazonaws.com
Country: Canada
City: Montréal
Organization: Amazon.com, Inc.

Open Ports: 22, 80, 443, 8080

22/tcp SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.5
80/tcp nginx/1.18.0
443/tcp nginx/1.18.0 — TLSv1.3 — CN=api-dev.demo-corp.com
8080/tcp Django DEBUG page found

The gold line: Django DEBUG page found. Shodan has already seen a Django debug mode exposed on :8080. A Django debug mode exposes:

  • The full stack on error.
  • Environment variables (so often secret keys).
  • Sometimes the complete SECRET_KEY.

We just found, without sending a packet to demo-corp.com, a probably critical flaw. We will confirm it in week 4.

Note the finding in tech/decouvertes.md:

- [CRITICAL] api-dev.demo-corp.com:8080 — Django in DEBUG (source: Shodan)
- [INFO] api-dev.demo-corp.com : Nginx 1.18, OpenSSH 8.2 on Ubuntu 20

Step 7 — Key people (theHarvester + LinkedIn)​

theHarvester -d demo-corp.com -b linkedin,bing,duckduckgo -l 500 \
-f people/harvester.html

theHarvester queries third-party APIs and LinkedIn (usually through Bing). Typical output:

[*] Emails found: 34
admin@demo-corp.com
contact@demo-corp.com
j.dupuis@demo-corp.com
m.tremblay@demo-corp.com
...

[*] Hosts found:
(Same, redundant with what we already have)

[*] LinkedIn profiles (via Bing):
Marie Tremblay - CTO at Demo Corp
Jean Dupuis - Senior DevOps at Demo Corp
Étienne Roy - Support Lead at Demo Corp
...

The email pattern jumps out at once: initiale.nom@demo-corp.com. From LinkedIn, you can guess the email of most of the client's employees, without ever asking them.

Consolidate into people/personnes.csv:

prenom,nom,titre,email,linkedin
Marie,Tremblay,CTO,m.tremblay@demo-corp.com,https://www.linkedin.com/in/marietremblay
Jean,Dupuis,Senior DevOps,j.dupuis@demo-corp.com,...
Étienne,Roy,Support Lead,e.roy@demo-corp.com,...

This file feeds module 6 (social engineering).


Step 8 — Public leaks​

# HaveIBeenPwned by domain — requires an API key (paid, but cheap).
curl -s -H "hibp-api-key: $HIBP_KEY" \
"https://haveibeenpwned.com/api/v3/breacheddomain/demo-corp.com" \
| jq . > leaks/hibp.json

# Preview:
jq -r 'keys[] as $k | "\($k) → \(.[$k] | join(", "))"' leaks/hibp.json | head

Output:

j.dupuis  → Adobe, Collection#1, MyFitnessPal
support → Collection#1
etienne.roy → LinkedIn, Dropbox

Two technical accounts (j.dupuis, etienne.roy) have already leaked in public dumps. If the client reuses passwords — and statistically, a pentester can count on that — we will be able to try password spraying in week 5.

Follow up with GitHub Search:

Look for repositories named demo-corp-*, scripts, backups, dotfiles. You regularly find:

  • A .env with secrets.
  • Scripts with hardcoded passwords.
  • Internal URLs.

In our walkthrough, a public dotfiles repo from a former employee contains a deploy.sh file with:

ssh deploy@old-admin.demo-corp.com -i ~/.ssh/deploy-key

Confirmed target: old-admin.demo-corp.com, account deploy. To exploit in week 4.


Step 9 — Archives (Wayback Machine)​

curl -s "https://web.archive.org/cdx/search/cdx?url=demo-corp.com/*&output=json&limit=200" \
| jq -r '.[1:] | .[] | .[2]' \
| sort -u > archive/urls-historiques.txt

wc -l archive/urls-historiques.txt

Output:

612 archive/urls-historiques.txt

A grep on revealing keywords:

grep -iE 'admin|backup|test|debug|swagger|api-doc|internal' archive/urls-historiques.txt
https://demo-corp.com/admin-old/
https://demo-corp.com/api/v1/debug
https://demo-corp.com/backup.sql (2019)
https://demo-corp.com/swagger.json

backup.sql in 2019 — probably deleted since, but we will check in module 4: the Wayback Machine does not guarantee deletion on the server.


Step 10 — Consolidate the sheet​

Open ~/osint/demo-corp/fiche-cible.md and fill it with what precedes. Priority targets go in bold.

# Target sheet — demo-corp.com  (2026-04-15)

## 1. Confirmed assets
- Root domain: demo-corp.com (Cloudflare DNS + proxy)
- Subdomains: 178 collected, including:
- **api-dev.demo-corp.com → AWS ca-central-1 (3.98.14.202), Django DEBUG on :8080**
- **old-admin.demo-corp.com → Vultr (66.42.11.198), presumed deploy account**
- internal.demo-corp.com → 192.168.10.5 (private), published by mistake
- jenkins.demo-corp.com → AWS
- jira.demo-corp.com → Atlassian Cloud (scope to confirm)

## 2. Scope to negotiate with the client
- internal.demo-corp.com (published private IP): add to the RoE?
- Atlassian and Google subdomains: out of scope.

## 3. Technologies
- Front (www): Next.js behind Cloudflare
- Prod API: unknown, filtered by Cloudflare
- Dev API: Django, Nginx 1.18, Ubuntu 20 (source: Shodan)
- Mail: Google Workspace
- Related SaaS: Stripe, Mailgun, Atlassian, Sentry

## 4. Likely flaws (to confirm in week 4)
- CRITICAL : Django DEBUG exposed on api-dev:8080
- HIGH : old-admin.demo-corp.com — inherited admin panel
- MEDIUM : DMARC p=none → spoofed phishing possible
- MEDIUM : backup.sql (2019) — still on the current server?

## 5. Key people (source: LinkedIn + theHarvester)
- Marie Tremblay, CTO — m.tremblay@demo-corp.com
- Jean Dupuis, Sr DevOps — j.dupuis@demo-corp.com [HIBP leak × 3]
- Étienne Roy, Support Lead — e.roy@demo-corp.com [HIBP leak × 2]
+ 21 others in people/personnes.csv

## 6. Public leaks
- HIBP: 2 high-value accounts with likely password reuse
- GitHub: a former employee's dotfiles cite old-admin.demo-corp.com and the deploy account

## 7. Next step (module 4)
1. Confirm Django DEBUG on api-dev:8080 — 30 min.
2. Map old-admin.demo-corp.com: ports, versions — 45 min.
3. Check /backup.sql on www — 5 min.
4. Careful password spraying on the 2 HIBP accounts — 30 min.

Wrap-up​

In 90 minutes, without sending a single packet to demo-corp.com:

  • We have 178 subdomains.
  • We have one almost certain critical flaw (Django DEBUG exposed).
  • We have two high-value secondary targets (old-admin, internal).
  • We have an attack plan for week 4.
  • We have leaked accounts we can use later.
  • The target knows nothing.

That is the power of well-run OSINT. It is the module that returns the most per minute invested.

Next lesson: your turn. You pick a bug bounty program and you do the same thing, on your own.