🎯Kimga? OWASP top-10 ni biladigan, Burp/nuclei/ffuf bilan qo'lda ishlagan, OPSEC asoslarini tushungan va kamida bir marta mid+ engagement o'tkazgan muhandislar uchun. Asosiy vibehacking.md — bu g'oya haqida. Bu dars — kontekst oynani yoqib yubormay va WAFlarni uyg'otmay turib agentlarni real yuk ostida qanday haydash haqida.
Agar L-1 ni o'tgan bo'lsangiz — "parallel pentest" tushunchasini unuting. Buning o'rniga bog'liqlik grafini (dependency graph) tasavvur qiling: tugunlar — bu kirish/chiqish kontrakti oldindan belgilangan subagentlar, qirralar — TaskCreate dagi blocked_by zanjirlari. Asosiy agent — bu dispetcher emas, balki graf rejalashtiruvchisi plyus aggregatsiya uchun sink.
DAG · Fan-out / Fan-in
1. Subagent orkestratsiyasi — fan-out / fan-in / DAG
Asosiy wave shabloni (Wave 1 → 2 → 3) — bu narvon. Real engagement esa DAG, unda yarim tugunlar bir-biriga bog'liq.
planner
│
┌────────────┼────────────┐
▼ ▼ ▼
recon-dns recon-ct recon-asn (Wave 1, fan-out)
└────────────┼────────────┘
▼
merge-targets (Wave 1.5, fan-in barrier)
│
┌────────────┼────────────┐
▼ ▼ ▼
web-fp tls-audit headers (Wave 2)
└────────────┼────────────┘
▼
service-classifier (Wave 2.5, inson aralashuvi)
│
┌────────┬────────┼────────┬────────┐
▼ ▼ ▼ ▼ ▼
graphql swagger actuator portainer minio (Wave 3, parallel guruhlar)
Graf qoidalari:
blocked_by qat'iy. web-fp merge-targets targets.json ni qaytarmaguncha boshlanmaydi. Bloksiz agentlar ishni dublikat qiladi yoki eski target listda ishlaydi.
- Fan-in barrierlari majburiy. Har wavedan keyin —
merge-* agent orqali sinxronizatsiya. Barriersiz dispetcher to'liq bo'lmagan dataga qarab "yarim qaror" qabul qilishni boshlaydi.
- Har bir tugun idempotent. Qayta ishga tushirish hech narsani buzmasligi kerak. Ya'ni: har subagent
loot/<wave>/<host>/<agent>.json ga to'liq overwrite bilan yozadi, append emas.
- Parallelizm = 4–6, undan ko'p emas. Asosiy agentning kontekst oynasi cheklangan: bir vaqtda 12+ subagent — bu fan-in da 12+ summary, asosiy agent aggregatsiyada bo'g'iladi. 5 tadan ikki wave 10 dan bitta wavedan yaxshi.
Subagent turlarini qachon ishlatish:
🧭
planner
Graf rejalashtiruvchisi
Faqat fazaning boshida
Scope kardinal o'zgarganda (yangi domen, scope kengayishi)
Har wave da "aniq in-scope siz yangi domenlar" ni explicit flag qiladi
DNS / CT / whois / ASN / OSINT
Targetga bironta HTTP yo'q — nol footprint
Probelar, fingerprint, headerlar, oldindan ma'lum yo'llar
Rate-limit promptda aniq
Faqat oxirida
Faqat internal.md stabillashgandan keyin
graphql introspection, actuator dump kabi nuqtali ish
Qisqa prompt, bir maqsad — 3 curl, 200 so'z
⚠️Middle-darajadagi xato: web agentni ad-hoc o'rniga ishlatish. web "tekshirib chiqishni" boshlaydi, kontekst yondiradi. Ad-hoc esa 3 ta curl qilib 200 so'z qaytaradi.
Prompt anatomiyasi
2. Prompt engineering — security promptning anatomiyasi
Dev-vazifalardan farqli, security prompt erkinlikni kechirmaydi. Har "o'zing o'ylab top" — bu targetning logiga qo'shimcha trace va sizning kontekstingizga +5K token.
Kanonik anatomiyasi:
[CONTEXT] — kim sen, target, scope, nima allaqachon ma'lum
[COMMANDS] — verbatim komandalar yoki curl/dig ekvivalenti
[CONSTRAINTS]— rps, timeout, max bytes, retry policy
[OPSEC] — tripwirelar, stop conditions, NIMA qilmaslik kerak
[OUTPUT] — format, uzunlik, fayl yo'li, JSON schema
🔧Nega bu prompt? Past variant agentga "qiziq narsa" topishni topshiradi — u butun surfaceni "tekshirishni" boshlaydi, kontekstni va target loglarini yondiradi. Yuqori variant har qadamni verbatim beradi: nusxa-joylab ishlatish uchun ham, deterministik output uchun ham.
# yomon — middle uslubi
"app.example.tld da recon qil, qiziq narsa bormi ko'r"
# yaxshi — L+1
[CONTEXT] target: app.example.tld, scope: *.example.tld, auth ref: TKT-1234
[COMMANDS]
1) dig +short A,AAAA,MX,NS,TXT app.example.tld
2) for s in $(cat loot/wave1/subs.txt); do
dig +short A "$s"
done
3) curl -s "https://crt.sh/?q=%25.example.tld&output=json" | jq -r '.[].name_value' | sort -u
[CONSTRAINTS] sleep 0.3 so'rovlar orasida; max wallclock 4 daqiqa
[OPSEC] faqat passiv; *.example.tld ga HTTP yo'q; avval VPN check: curl -s ifconfig.me
[OUTPUT] loot/wave1/recon-dns.json — schema: {subs: [...], a_records: {...}, anomalies: [...]}, ≤ 350 so'z prose
Conditional probelar — promptdagi shoxlanish
L+1 ning eng kam baholangan usuli. "N ta so'rov qil" o'rniga — "1 ta so'rov qil, javobga qarab keyingi qadamni tanla". Ham kontekst, ham shovqinni tejaydi.
🌳Nega bu prompt? Bu yerda promptga shoxlanish mantiqi o'rnatilgan: agentni 200 ta probe qilishga emas, bitta so'rov qilib javobga qarab to'xtashga majbur qiladi. PoC sentinel — .git/ topilsa STOP, wordlist yondirilmaydi.
[COMMANDS]
1) curl -s -o /dev/null -w "%{http_code}" "https://${T}/.git/HEAD" --max-time 6
2) IF status == 200 AND body "ref: " bilan boshlansa:
→ curl -s "https://${T}/.git/config" --max-time 6 → loot/wave4/${T}/config
→ STOP. .env, .DS_Store ni urinib ko'rma. PoC ni yozib qo'y, dispetcherga xabar ber.
ELIF status == 403:
→ "blocked but exists?" deb belgila, davom etma.
ELSE:
→ ro'yxatdagi keyingi probe
Asosiy g'oya: agent o'zi davom etish kerakligini hal qiladi. Promptga o'rnatilgan PoC sentinel. Bu bo'lmasa, agent .git/ topgandan keyin ham butun wordlist ni yondiradi.
Output cheklashlar — kontekst nazorati
≤ 400 so'z qat'iy, aks holda agent essay yozadi.
JSON schema aniq, aks holda reporter keyinroq parse qila olmaydi.
fayl yo'li majburiy, aks holda output asosiy agent oynasiga oqib ketadi.
final outputda chain-of-thought yo'q — alohida qator. Aks holda reasoning parent contextga oqib ketadi.
Inline PII masking
Maskingni reporterga qoldirmang — kech bo'ladi. Sizib chiqishi mumkin bo'lgan dataga tegishli har bir promptda:
[OPSEC] Outputda email/phone/passport/INN uchrasa — faylga yozishdan OLDIN mask qil:
email: u***@d***.tld (birinchi harf + tld)
phone: +998 XX *** ** XX (birinchi 2, oxirgi 2)
passport: AA*******
loot/ ga unmasked saqlama.
🔒REPORT_STANDARD bu hisobot darajasida talab qiladi, lekin agar PII loot/ ga tushsa — keyinroq compliance muammosi. Maskingni reporterga qoldirmang.
Toolchain
3. Toolchain integratsiyasi
Agentlar — bu toollarning o'rnini bosmaydi, ular ustidan orkestrator. Nimani va qanday ulash kerak.
Burp Collaborator
OOB (out-of-band) kanallar — blind zaifliklarda. Collaborator URL ni promptda aniq bering, agent o'zi Burp UI ga kirib chiqadi deb kutmang.
[CONTEXT] OOB channel: xyz123.oastify.com (Burp Collaborator, vakolatli)
[COMMANDS]
1) curl -s "https://${T}/api/import" -d '{"url":"http://xyz123.oastify.com/ssrf-probe-01"}'
2) sleep 8
3) Collaboratorni so'rash men o'zim qilaman, sen faqat probe-id ni yoz.
[OUTPUT] {probe_id: "ssrf-probe-01", target_endpoint: "...", payload_used: "..."}
Collaborator polling agentga bermang — bu interaktiv XML auth, agent buzilib qoladi. Probe-id ni yozib oling, o'zingiz polling qiling.
Nuclei — "ikkinchi miya"
Nuclei discovery uchun emas, verifikatsiya uchun. Qo'lda bir narsa topdingiz → tor tag filtri bilan nuclei ni yugurtirdingiz → mustaqil tasdiq oldingiz.
[COMMANDS]
nuclei -u "https://${T}" -tags exposure,misconfig -severity high,critical \
-rate-limit 5 -timeout 8 -no-color -j -o loot/wave3/${T}/nuclei.jsonl
[CONSTRAINTS] -rate-limit 5 majburiy, aks holda CF /24 ni banlaydi
[OUTPUT] jsonl ni parse qil, faqat matched entriesni final hisobotga, ≤ 10 finding
Filtersiz nuclei -t cves/ ni ishga tushirmang — bu 8000+ template va bir soatlik run.
ffuf — wordlist aniq yo'l orqali
[COMMANDS]
ffuf -w /Users/you/infosec/wordlists/quickhits.txt:FUZZ \
-u "https://${T}/FUZZ" \
-mc 200,401,403 -fs 0 -t 10 -p 0.2 -o loot/wave3/${T}/ffuf.json
"Wordlist olib kel" emas — to'liq yo'l. Aks holda agent /usr/share/wordlists/... ga boradi, sizda yo'q, qulaydi, retrylarni yondiradi. Va -t 10, default 40 emas — aks holda har qanday rate-limiter sizni darhol payqaydi.
NIMA agent orqali, NIMA qo'lda
Agent orqali EMAS — qo'lda
- SQLMap — uzoq, katta output, timing-sensitive
- Hashcat / john — xuddi shu + GPU
- Burp active scanner — sessiyani o'g'irlaydi, shovqinli
- Metasploit — interaktiv promptlar
Agent zo'r ishlaydi
- recon / fingerprint — fire-and-forget
- nuclei tor tag filtri bilan (verifikatsiya)
- ffuf qisqa wordlist + aniq yo'l
- conditional probelar — toza exit code
Qoida: agar tool real vaqt rejimida watch-and-tweak talab qilsa — bu qo'l. Agent fire-and-forget vazifalarda toza exit code bilan zo'r.
RTK hook va /usr/bin/curl
RTK muhitida curl hook tomonidan tutib qolinadi. Bitta so'rovda bu normal, lekin tight loopda (for i in $(seq 1 200); do curl ...) hook ba'zan bo'g'iladi va butun loop ECONNRESETga osilib qoladi. Yechim — aniq yo'l:
/usr/bin/curl --max-time 8 -sS "$url"
👻Ghost-constraint: promptga yozing — "10 dan ko'p iteratsiyali batch so'rovlar uchun /usr/bin/curl ishlat". Ko'plar "curl buzilgan" deydi — yo'q, bu RTK hook loopda bo'g'iladi.
Concurrency · Failure modes
4. Concurrency limitlar va failure modellari
Asosiy agentning kontekst oynasi
Har subagent 200–400 so'zlik summary qaytaradi ≈ 500–1000 token. 12 ta subagent = parent contextda 12 ta parallel quyruq. tool_use/tool_result obvyazka bilan x2. Jami 24K token faqat aggregatsiya fazasi uchun. 2–3 wavedan keyin oyna yonib ketadi.
Limit: 6 ta parallel, ideal — 4. Ko'proq kerak bo'lsa — ketma-ket ikki waveiga bo'ling. Vaqtni deyarli yo'qotmaysiz (har subagent ~5–10 minut), lekin asosiy agent toza qoladi.
Uzoq fonli vazifalarda ECONNRESET
🔌ECONNRESET tripwire: Claude Code dagi background vazifalar ba'zan 30+ minutdan keyin upstream API ulanishini yo'qotadi. Belgi: subagent "qotib qoladi", progress yo'q.
- Watchdog 600s no-progress — asosiy agent har subagent task uchun deadline qo'yadi. 10 minutda qaytmasa → kill, yangi prompt bilan retry.
- Hammasini backgroundga qo'ymang. Backgroundga faqat haqiqatan uzoq ishlar (to'liq nuclei template set). Oddiy probelar foregroundda, tezroq tugaydi.
Agent "qotib" qolganda
Belgilar: TaskCreate running da, oxirgi tool_use 5+ daqiqa oldin, logda yangi narsa yo'q. Sabablari:
- ECONNRESET, subagent xabar bermagan
- RTK ostidagi tight curl loop
- nuclei egilgan repodan 5 MB template tortib turibdi
🔁Harakat: taskni kill qilish, promptni qayta yig'ish, retry. O'sha promptda retry qilmang — bu yana 10 minut yo'qotish.
Retry strategiyasi
| Belgi | Harakat |
| Subagent empty / "I cannot do this" qaytardi | aniqroq komandalar bilan retry, o'sha prompt emas |
| ECONNRESET | foreground retry, backgroundsiz |
| Ketma-ket 3+ retry barbod | to'xtang, vazifani revizyon qiling: target tushgan yoki scope noto'g'ri |
| Target hamma subagentlarga 429 qaytaryapti | 10 daqiqa pauza, rate-limit /2 bilan retry |
🛑3-fail qoidasi (engineering.md): 3 marta ketma-ket urinish = noto'g'ri ish qilyapsiz. To'xtang, vazifani revizyon qiling.
Pentest Task Tree
5. PTT-driven engagementlar (Pentest Task Tree)
PTT — bu pentest uchun TodoList, lekin kontrakt bilan: har tugunda status, owner (subagent type), evidence path, severity bor.
PTT (Wave 3, target: app.example.tld)
[x] graphql-introspection → ad-hoc subagent → loot/w3/graphql.json [info]
[x] swagger-discovery → web subagent → loot/w3/swagger.json [info]
[ ] actuator-heapdump → ad-hoc subagent → blocked_by: swagger [pending]
[!] portainer-noauth → web subagent → loot/w3/portainer.md [HIGH, tripwire]
[ ] minio-anonymous-list → ad-hoc subagent → [pending]
Qachon yangilash:
- Har wavedan keyin — fan-in barrier statuslarni yangilaydi.
- Tripwire ishga tushganda — eskalatsiya uchun
[!] tugunlar qo'shing.
- Scope kengayganda — faqat tegishli sub-tree ni qayta yig'ing, butun PTT ni emas.
Live PTT vs review-end PTT:
- Live PTT — asosiy agent uni har promptda ko'radi (context block sifatida uzatamiz). Wavelar 5+ bo'lsa va joyni yo'qotish oson bo'lsa foydali.
- Review-end PTT — oxirida yig'iladi,
internal.md ga tushadi. Audit trail.
Men ikkalasini ham yuritaman, lekin live versiyasini joriy wave + oldingi wavega kesib qo'yaman. Butun tarixni har promptga uzatish — bu bekorga +3K token.
Wave architecture
6. Wave architecture (chuqur)
Wave 1: faqat passiv — nol footprint
Faqat ochiq manbalar, targetga bironta so'rov yo'q:
recon-dns → ochiq resolverlarga dig (8.8.8.8, 1.1.1.1)
recon-ct → crt.sh, certspotter
recon-asn → bgp.he.net, hackertarget aslookup
recon-osint → GitHub dorks, wayback machine, archive.org
recon-leaks → DeHashed-style presence check, credential lookupsiz
Target sizning mavjudligingizni umuman bilmasligi kerak. Agar Wave 1 dan qiziq narsa chiqmasa — scope tor, mijoz bilan gaplashing, ko'r-ko'rona Wave 2 ga o'tmang.
Wave 2: yengil aktiv — fingerprint
web-headers → topilgan barcha sublarga curl -I, Server/X-Powered-By/Set-Cookie ni parse qil
web-tls → testssl.sh --quick (to'liq EMAS)
web-fp → whatweb yoki ekvivalent, lite-mode
web-robots → robots.txt + sitemap.xml + /.well-known/security.txt
Shovqin — host boshiga bitta GET, googlebotdan farqlanmaydi. Wordlistlar yo'q, ?id=1' yo'q.
Wave 3+: service turi bo'yicha targetlangan
Wave 2 dan keyin sizda hostlar klassifikatsiyasi bor. Endi xizmat guruhlari bo'yicha parallellaymiz, hostlar bo'yicha emas:
group-orchestration: portainer, k8s-dashboard, rancher, docker-api
group-monitoring: grafana, prometheus, kibana, alertmanager
group-api: graphql, swagger, openapi, actuator
group-cms: wp-login, drupal, joomla, ghost
group-storage: minio, s3-public, ftp, smb
Har guruh = bitta ad-hoc subagent, 4–6 guruh parallel. Har agent faqat o'z guruhini biladi, chalg'imaydi.
Wavelar orasida — sync + reassess
Middlening eng tez-tez xatosi: "wavelar konveyer kabi ketadi". Yo'q. Har wavedan keyin:
- fan-in: hamma subagentlar tugadi
- asosiy agent summarylarni o'qiydi
- PTT ni yangilaydi — nima topildi, nima tushib qoldi, nima qo'shildi
- scope ni qayta baholaydi — sister-domain topildimi? STOP, kelishuv
- faqat shundan keyin — keyingi wave
3–4 qadamlarsiz siz scope tashqarisidagi hostlarda Wave 5 olasiz.
Adversarial
7. Adversarial scenariolar
Reflective WAF (Cloudflare, Cloudbric, Imperva)
Belgi: har "g'alati" queryga 403, normalga 200. Yondashuvlar:
- WAFga urmang, originga uring. Originni topish: censys/shodan favicon hash, unikal header, certificate SAN orqali.
- Sekinlashtirish —
-rate-limit 1, sleep 1.5. WAF reactive smoothing window-based detectionda ishlaydi, sekin fuzz ko'pincha o'tadi.
- Headers rotatsiyasi — nuclei
-H orqali har xil User-Agent, qonuniy Accept-Language. Panacea emas, lekin score kamayadi.
🚫Qilmang: ?x=1' OR/**/1=1 kabi aniq bypass payloadlar. WAF ularni birinchi tutadi, plyus bot-signal. Promptda: [OPSEC] Cloudflare aniqlandi, rate-limit ≤1 rps, aniq payload mutatsiyasiz, certificate SAN orqali origin discovery.
Aggressive rate-limit (hammaga 429)
- Eksponensial backoff promptda:
if 429: sleep $((2**retry)), max retry 3
- Sublar boyicha taqsimlash — agar limit per-host bo'lsa, 5 ta sub aylanasida bittadan so'rov.
- 429 ko'pincha "target bizni ko'rayapti" signali. 30 daqiqa pauza, keyin yengilroq yondashuv.
GeoIP block (mas. UZ-egress kerak)
Mahalliy xizmatlar ko'pincha xorijiy IPlarni bloklaydi. Mahalliy exit-node bilan VPN. Wave 1 dagi tekshiruv:
curl -s ifconfig.io/country_code # kutilgan davlat bo'lishi kerak
curl -s -o /dev/null -w "%{http_code}" "https://${T}" # 200 bo'lishi kerak, 451 emas
Agar maxsus geo-egress kerak bo'lsa va VPN provayderda yo'q bo'lsa — vakolatli broker orqali residential proxy, "forumdan sotib olgan" emas. Bu yuridik savol, texnik emas.
Engagement o'rtasida legal scope
Senariy: Wave 2 da api.sister-company.tld topdingiz — target bilan bog'langan, lekin scopeda yo'q. Harakat:
1
STOP
Bu aktivga tegmaymiz. Boshqa probelar yo'q.
2
internal.md ga yozing
[SCOPE-DOUBT] api.sister-company.tld CT log orqali topildi, TKT-1234 scope da emas
3
Mijozga eskalatsiya
Yozma scope kengayishi yoki aniq "scope tashqari, tegmang".
4
Javobdan keyin
Davom etamiz yoki daraxtdan olib tashlaymiz.
⚖️Bu paranoiya emas, bu compliance. Scope tashqari harakat = shartnoma buzilishi = sizning litsenziyangiz / obro'yingiz.
Reporting
8. Reporting workflow
Reporter — alohida subagent, qo'shimcha ehtiyotkorlik bilan.
CRITICAL ga 3x verifikatsiya
Har CRITICAL finding 3 ta mustaqil tasdiqdan o'tadi:
- Findingni topgan subagentdan asl PoC.
- Reporter tomonidan reproduce — boshqa subagent, boshqa prompt, o'sha kutilgan output.
- Manual replay — siz shaxsan hisobotdagi komandani bajarasiz, solishtirasiz.
Agar 3-qadam mos kelmasa — finding olib tashlanadi aniqlangunga qadar. Real CRITICAL ni o'tkazib yuborgan yaxshi, yolg'onini chiqarib yuborgandan ko'ra.
Pandoc + pdf_style.css
pandoc tech.md -o tech.pdf \
--css=/Users/you/infosec/pdf_style.css \
--pdf-engine=weasyprint \
--metadata title="Texnik hisobot"
Reporter shu pipeline uchun markdown yozadi, o'zi PDF chizishga urinmaydi. Agentning "LaTeX da chiroyli PDF qilaman" urinishlari — bug-zavodi.
Majburiy 0-bandlik "Ish xarakteri haqida"
exec.pdf va tech.pdf da bir xil. Reporter bu bandlik uchun tayyor shablonni oladi (~/infosec/REPORT_STANDARD.md dan), substitutsiya qiladi: sanalar, scope, ticket-id, hujumchi profili.
🚫Ta'qiqlangan iboralar: "oq haker" (noaniq, profil emas) · "pentester" (bu xodim, model emas) · "internetga ega har kim" (L-darajaligi emas).
Faqat aniq L-profil:
| L | Kim | Kirish |
| L1 | Anonim internet | Faqat ochiq internet, credlar yo'q |
| L2 | Authenticated user | Oddiy akkaunt, asosiy rol |
| L3 | Privileged user | Manager, o'qituvchi, operator |
| L4 | Admin | To'liq admin paneli huquqlari |
Reporter promptda: "faqat L1–L4 ishlat, profilni finding kontekstidan tanla, 1 jumla bilan asosla".
PII masking avtomatlashtirish
Reporter ikkita sed skript oladi:
s/[a-zA-Z0-9._%+-]\+@\([a-zA-Z0-9.-]\+\)\.\([a-z]\{2,\}\)/u***@\1.\2/g
s/+998 \([0-9]\{2\}\) [0-9]\{3\} [0-9]\{2\} \([0-9]\{2\}\)/+998 \1 *** ** \2/g
tech.md va exec.md yozishdan oldin run. Outputda PII qoldig'i bo'lsa — reporterga qaytarish, aniq qatorga ko'rsatkich bilan.
Token byudjeti
9. Cost / token byudjeti
Vibe-hacking dagi asosiy token yutuvchilar:
| Manba | Sarf | Yechim |
chrome-devtools take_screenshot | shot uchun ~125K | snapshot (DOM-text); screenshot faqat final vizual verifikatsiya uchun |
Har promptga to'liq internal.md | chiziqli o'sadi | to'liq fayl emas, faqat tegishli tilim (oxirgi wave + tripwirelar) |
| nuclei JSONL output | host boshiga 50K gacha | upstream parse, parentga faqat matched entries |
| Verbose curl loglar | -v bilan so'rov boshiga ~2K | -sS + -w "%{http_code} %{size_download}\n" |
| 50 tugunli TodoList | har o'tish 3–5K | live PTT ni joriy fazaga kesish |
cat large-file.json | to'liq fayl | jq filtri yoki sed -n 'N,Mp' |
Ishlaydigan mikro-optimizatsiyalar:
- loot/ fayllarni promptga inline qo'ymang — yo'lni bering, agentlar o'zi o'qiydi.
- JSON output, prose emas — JSON axborot birligiga 2–3x zichroq token bo'yicha, plyus deterministik parsing.
- Done-tasklarni yoping — PTT dagi
[x] status keyingi promptlarga evidence blokini sudramasin.
Byudjet: tipik 1-soatlik engagementga ~150–200K parent token, ~500K subagentlar bo'yicha jami sarflanadi. Bundan oshsak — qayerdadir promptlar juda umumiy.
Case study
10. Failure case study (anonimlashtirilgan)
Anonimlashtirilgan runO'quv stand-in target, ~30 sub, Django stack, tipik SaaS. 11 wavelik DAG run, real vaqt — 2 soat parent-context + ~6 soat subagent runtime (parallel ketdi).
Topilganlar:
- 4× CRITICAL: ochiq
.git/, ochiq Django debug page SECRET_KEY bilan, anonymous list+download bilan MinIO, authsiz Portainer.
- 7× HIGH: hamma joyda yo'q security headerlar, GraphQL introspection ochiq, write operatsiyalar bilan authsiz Swagger, JS bundleda hardcoded credlar, oldindan aytsa bo'ladigan ID orqali API IDOR, weak JWT secret (offline crackable), admin segmentda ochiq registratsiya.
- ~20 MEDIUM/LOW.
✓ Nima ishladi
Wave 1 passiv 70% surface ni berdi — targetga so'rov yo'q (CT + GitHub dorks + wayback). .git/HEAD conditional probe ~2 soat wordlist slop ni tejadi. service-classifier 30 hostni 5 guruhga ajratdi. 3x verification reporter 1 ta yolg'on CRITICAL ni tutdi (server-side stripping, real leak emas).
✕ Nima buzildi
Wave 4 da 9 subagent parallel — main agent fan-in da bo'g'ildi (saboq: max 6). Backgroundli nuclei 40 minut yugurdi, watchdog ishga tushmadi — progress kichik edi (saboq: "yangi ma'noli signal yo'q" ga). RTK hook ostida tight curl loop osildi → /usr/bin/curl. Sister-domain darhol payqalmadi — 20 min scope-out probelar.
Texnik saboqlar:
- DAG > chiziqli wavelar, lekin fan-in barrierlari majburiy.
- Conditional probelar shovqinni katta darajaga kamaytiradi.
- Watchdog "yangi signal yo'q" ga, "output yo'q" ga emas.
- Reporter har doim 3x verify bilan, aks holda CRITICAL inflyatsiyasi.
- Scope drift faqat plannerda explicit auto-flag bilan tutiladi.
TL;DR
11. Senior daraja uchun TL;DR
- Graf, narvon emas.
blocked_by — sizning do'stingiz.
- 4–6 parallel, hech qachon 12+ emas.
- Conditional probelar > chiziqli wordlistlar.
- Prompt = CONTEXT/COMMANDS/CONSTRAINTS/OPSEC/OUTPUT. Erkinlik yo'q.
- Batch looplari uchun
/usr/bin/curl.
- Nuclei verifikatsiyaga, discoveryga emas.
- SQLMap/hashcat/Burp — qo'lda, agentsiz.
- CRITICAL ga reporter 3x verifikatsiya bilan, aks holda exec.pdf da yolg'on alarm va obro' qarzi.
- Sister-domain → STOP → eskalatsiya. Compliance > findings.
- Watchdog 600s no-meaningful-progress, boshqa prompt bilan retry.
L+1 vibe-hacking — bu "ko'proq agent" emas. Bu kamroq agent, o'tkirroq promptlar, qattiqroq barrierlar. Tezlik aniqlikdan keladi, parallelizmdan emas.
🎯Для кого? Для тех, кто уже знает OWASP top-10, гонял Burp/nuclei/ffuf руками, понимает OPSEC и хотя бы раз делал mid+ engagement. Базовый vibehacking.md — про идею. Этот урок — про то, как реально водить агентов под нагрузкой, не сжигая контекст и не паля себя на WAF.
Если вы уже прошли L-1 — забудьте «параллельный пентест». Думайте: граф зависимостей, где узлы — это субагенты с заранее заданным контрактом ввода/вывода, а рёбра — blocked_by цепочки в TaskCreate. Главный агент — это не диспетчер, это планировщик графа + sink для агрегации.
DAG · Fan-out / Fan-in
1. Subagent orchestration — fan-out / fan-in / DAG
Базовый шаблон волн (Wave 1 → 2 → 3) — это лестница. Реальный engagement — это DAG, в котором половина задач блокируется одна на другой.
planner
│
┌────────────┼────────────┐
▼ ▼ ▼
recon-dns recon-ct recon-asn (Wave 1, fan-out)
└────────────┼────────────┘
▼
merge-targets (Wave 1.5, fan-in barrier)
│
┌────────────┼────────────┐
▼ ▼ ▼
web-fp tls-audit headers (Wave 2)
└────────────┼────────────┘
▼
service-classifier (Wave 2.5, человеческое касание)
│
┌────────┬────────┼────────┬────────┐
▼ ▼ ▼ ▼ ▼
graphql swagger actuator portainer minio (Wave 3, parallel groups)
Правила графа:
blocked_by строго: web-fp не стартует пока merge-targets не вернул targets.json. Без блока агенты дублируют работу или работают по протухшему target list.
- fan-in barriers — обязательны. После каждой волны — sync через
merge-* агента. Без барьера диспетчер начинает читать неполные данные и принимать решения «по половинке».
- Каждый узел — идемпотентен. Перезапуск не должен ничего ломать. Это значит: каждый субагент пишет в
loot/<wave>/<host>/<agent>.json с полным перезаписыванием своего файла, а не дописыванием.
- Параллелизм = 4–6, не больше. Контекст-окно главного агента не резиновое: 12+ субагентов одновременно — это 12+ потоков summary в финале, и main agent захлёбывается на агрегации. Лучше две волны по 5, чем одна на 10.
Когда какой тип субагента использовать:
Только в начале фазы
При кардинальном изменении scope (новый домен, расширение разрешения)
На каждой волне явно флагает «новые домены без чёткого in-scope»
DNS / CT / whois / ASN / OSINT
Ни одного HTTP к таргету — нулевой footprint
Probes, fingerprint, headers, заранее известные пути
Лимит rps — в промпте
Только в конце
Только когда internal.md стабилизирован
graphql introspection, actuator dump — точечная работа
Короткий промпт, одна цель — 3 curl-а, 200 слов
⚠️Ошибка middle-уровня: пускать web агента туда, где должен быть ad-hoc. web начинает «обследовать», тратит контекст. Ad-hoc делает 3 curl-а и возвращает 200 слов.
Анатомия промпта
2. Prompt engineering — анатомия security-промпта
В отличие от dev-задач, security-промпт не прощает свободы. Каждый «подумай сам» — это лишний trace в логах таргета и +5K токенов в вашем контексте.
Каноническая анатомия:
[CONTEXT] — кто ты, что таргет, какой scope, что уже известно
[COMMANDS] — verbatim команды или эквивалент в curl/dig
[CONSTRAINTS]— лимиты: rps, timeout, max bytes, retry policy
[OPSEC] — tripwires, stop conditions, что НЕ делать
[OUTPUT] — формат, длина, путь к файлу, схема JSON
🔧Зачем этот промпт? Плохой вариант поручает агенту найти «что-то интересное» — он начнёт «обследовать» весь surface, сжигая контекст и логи таргета. Хороший даёт каждый шаг verbatim: и для copy-paste, и для детерминированного вывода.
# плохо — middle-стиль
"Сделай recon на app.example.tld, посмотри что там интересного"
# хорошо — L+1
[CONTEXT] target: app.example.tld, scope: *.example.tld, in-scope auth: TKT-1234
[COMMANDS]
1) dig +short A,AAAA,MX,NS,TXT app.example.tld
2) for s in $(cat loot/wave1/subs.txt); do
dig +short A "$s"
done
3) curl -s "https://crt.sh/?q=%25.example.tld&output=json" | jq -r '.[].name_value' | sort -u
[CONSTRAINTS] sleep 0.3 между запросами; max wallclock 4 минуты
[OPSEC] passive only; никаких HTTP к *.example.tld; VPN check сначала: curl -s ifconfig.me
[OUTPUT] loot/wave1/recon-dns.json — schema: {subs: [...], a_records: {...}, anomalies: [...]}, ≤ 350 слов prose
Conditional probes — ветвление в промпте
Самый недооценённый приём L+1. Вместо «сделай N запросов» — «сделай 1 запрос, по результату реши следующий шаг». Это экономит контекст и снижает шум.
🌳Зачем этот промпт? Сюда встроена логика ветвления: агент не делает 200 probe, а делает один запрос и по ответу решает остановиться. PoC-сторожок — нашёл .git/ → STOP, wordlist не сжигается.
[COMMANDS]
1) curl -s -o /dev/null -w "%{http_code}" "https://${T}/.git/HEAD" --max-time 6
2) IF status == 200 AND body начинается с "ref: ":
→ curl -s "https://${T}/.git/config" --max-time 6 → loot/wave4/${T}/config
→ STOP, не пробовать .env, .DS_Store. Зафиксируй PoC, репорт диспетчеру.
ELIF status == 403:
→ отметить «blocked but exists?», не продолжать.
ELSE:
→ следующий probe из списка
Принцип: агент сам решает, надо ли ему продолжать. Это PoC-сторожок, встроенный в промпт. Без него агент будет фигачить весь wordlist, даже когда уже нашёл .git/.
Output constraints как контроль контекста
≤ 400 слов — жёстко, иначе агент пишет эссе.
JSON schema явно — иначе reporter потом не распарсит.
путь к файлу — иначе вывод льётся в stdout главного агента и съедает его окно.
no chain-of-thought в финальном выводе — отдельная строка. Иначе reasoning летит в parent context.
PII masking inline
Не оставлять маскировку на reporter — поздно. В каждом промпте, который потенциально касается утечек:
[OPSEC] Если в выводе встречаются email/phone/passport/ИНН — маскировать ДО записи в файл:
email: u***@d***.tld (первая буква + домен tld)
phone: +998 XX *** ** XX (первые 2, последние 2)
passport: AA*******
Никогда не сохранять unmasked в loot/.
🔒REPORT_STANDARD требует это на уровне отчёта, но если PII попадёт в loot/ — у вас compliance-ад потом. Не оставляйте маскировку на reporter.
Toolchain
3. Toolchain integration
Агенты — не замена инструментам, а оркестратор поверх. Что подключать и как.
Burp Collaborator
Out-of-band каналы — там, где blind-уязвимости. Передавайте collaborator URL в промпт явно, не ждите что агент сам зайдёт в Burp.
[CONTEXT] OOB channel: xyz123.oastify.com (Burp Collaborator, авторизованный)
[COMMANDS]
1) curl -s "https://${T}/api/import" -d '{"url":"http://xyz123.oastify.com/ssrf-probe-01"}'
2) sleep 8
3) Опросить collaborator вручную (это сделаю я после твоего отчёта). Просто запиши probe-id.
[OUTPUT] {probe_id: "ssrf-probe-01", target_endpoint: "...", payload_used: "..."}
Collaborator-polling агенту не отдавать — это парсинг XML с интерактивным auth, агент будет ломаться. Отдайте probe-id, опрашивайте вы.
Nuclei как «второй мозг»
Nuclei — не для discovery, а для верификации. Нашли что-то руками → прогнали nuclei с узким templates filter → получили независимое подтверждение.
[COMMANDS]
nuclei -u "https://${T}" -tags exposure,misconfig -severity high,critical \
-rate-limit 5 -timeout 8 -no-color -j -o loot/wave3/${T}/nuclei.jsonl
[CONSTRAINTS] -rate-limit 5 — обязательно, иначе CF забанит /24
[OUTPUT] парсить .jsonl, в финальный отчёт — только matched, ≤ 10 находок
Не запускайте nuclei -t cves/ без фильтра — это 8000+ темплейтов и часовой ран.
ffuf — wordlist через явный путь
[COMMANDS]
ffuf -w /Users/you/infosec/wordlists/quickhits.txt:FUZZ \
-u "https://${T}/FUZZ" \
-mc 200,401,403 -fs 0 -t 10 -p 0.2 -o loot/wave3/${T}/ffuf.json
Не «возьми wordlist» — полный путь. Иначе агент возьмёт /usr/share/wordlists/... которого у вас нет, упадёт, потратит ретраи. И -t 10, не дефолт 40 — иначе вас увидит любой rate-limit.
Что через агента, а что руками
НЕ через агента — руками
- SQLMap — long-running, огромный output, timing-зависимый
- Hashcat / john — то же + GPU
- Burp active scanner — крадёт сессию, шумит
- Metasploit — interactive prompts
Агент работает отлично
- recon / fingerprint — fire-and-forget
- nuclei с узким tag-фильтром (верификация)
- ffuf с коротким wordlist + явный путь
- conditional probes — чистый exit-код
Правило: если инструмент требует watch-and-tweak в реальном времени — это руками. Агент хорош в fire-and-forget с чётким exit-кодом.
RTK hook и /usr/bin/curl
В RTK-окружении curl перехватывается хуком. В одиночных запросах это нормально, но в tight loop (for i in $(seq 1 200); do curl ...) хук иногда давится и весь loop вешается на ECONNRESET. Обход — явный путь:
/usr/bin/curl --max-time 8 -sS "$url"
👻Ghost-constraint: в промпт прописать — «использовать /usr/bin/curl для batch-запросов >10 итераций». Многие пишут «curl сломан» — нет, это RTK-хук давится в loop.
Concurrency · Failure modes
4. Concurrency limits & failure modes
Контекст-окно главного агента
Каждый субагент возвращает summary 200–400 слов = ~500–1000 токенов. 12 субагентов одновременно = 12 параллельных хвостов в parent context. Плюс tool_use/tool_result обвязка ≈ x2. Итого 24K токенов только за фазу агрегации. Это выжигает окно за 2–3 волны.
Лимит: 6 параллельных, идеально 4. Хочется больше — разбейте на две волны последовательно. Время не сильно теряете (subagent runtime ~5–10 мин у каждого), а главный агент остаётся свежим.
ECONNRESET в долгих фоновых
🔌ECONNRESET-tripwire: background-задачи в Claude Code иногда теряют коннект к нижележащему API через 30+ минут. Симптом: subagent «застывает», прогресса нет.
- watchdog 600s no-progress — главный агент ставит дедлайн на каждый subagent task. Не вернулся за 10 мин — kill, retry с новым промптом.
- Не запускать всё в background. В background — только реально долгие (nuclei full template set). Обычные probes — foreground, быстрее завершаются.
Когда агент «застывает»
Признаки: TaskCreate в running, последний tool_use 5+ минут назад, в логах ничего нового. Это:
- ECONNRESET, которое subagent не отрапортовал
- tight loop в
curl под RTK
- подвисший nuclei (template скачивает 5 МБ из кривого репо)
🔁Действие: kill task, пересобрать промпт, retry. Не retry с тем же промптом — это просто потратит ещё 10 минут.
Retry-стратегия
| Симптом | Действие |
| Subagent вернул empty / «I cannot do this» | retry с более конкретными командами, не с тем же промптом |
| ECONNRESET | retry foreground, без background |
| 3+ retry подряд провалились | стоп, ревизия задачи: возможно цель недоступна или scope не тот |
| Таргет начал отдавать 429 на все subagents | пауза 10 минут, retry с rate-limit /2 |
🛑Правило 3-х fail (engineering.md): 3 попытки подряд = вы делаете не то. Стоп, ревизия задачи.
Pentest Task Tree
5. PTT-driven engagements (Pentest Task Tree)
PTT — это TodoList для пентеста, но с контрактом: каждый узел имеет статус, owner (subagent type), evidence path, severity.
PTT (Wave 3, target: app.example.tld)
[x] graphql-introspection → ad-hoc subagent → loot/w3/graphql.json [info]
[x] swagger-discovery → web subagent → loot/w3/swagger.json [info]
[ ] actuator-heapdump → ad-hoc subagent → blocked_by: swagger [pending]
[!] portainer-noauth → web subagent → loot/w3/portainer.md [HIGH, tripwire]
[ ] minio-anonymous-list → ad-hoc subagent → [pending]
Когда обновлять:
- После каждой волны — fan-in barrier обновляет статусы.
- При срабатывании tripwire — добавить
[!] узлы для эскалации.
- При расширении scope — пересобрать поддерево, не весь PTT.
Live PTT vs review-end PTT:
- Live PTT — главный агент видит его в каждом prompt'е (передаём как context block). Полезно когда волн >5 и легко потерять место.
- Review-end PTT — собирается в конце, идёт в
internal.md. Audit trail.
Я веду оба, но live-версию режу до текущей волны + предыдущая. Передавать весь PTT с историей в каждый промпт — это +3K токенов на ровном месте.
Wave architecture
6. Wave architecture (deep)
Wave 1: passive only — нулевой footprint
Только публичные источники, ни одного запроса к таргету:
recon-dns → dig к публичным резолверам (8.8.8.8, 1.1.1.1)
recon-ct → crt.sh, certspotter
recon-asn → bgp.he.net, hackertarget aslookup
recon-osint → GitHub dorks, wayback machine, archive.org
recon-leaks → DeHashed-like, без credential lookup, только presence
Цель таргета вообще не должна знать что вы существуете. Если из Wave 1 ничего интересного — scope узкий, обсуждать с заказчиком, не лезть в Wave 2 наобум.
Wave 2: light active — fingerprint
web-headers → curl -I по всем найденным сабам, парсим Server/X-Powered-By/Set-Cookie
web-tls → testssl.sh --quick (НЕ полный)
web-fp → whatweb или эквивалент, lite-mode
web-robots → robots.txt + sitemap.xml + /.well-known/security.txt
Шум — single GET per host, неотличим от googlebot. Никаких wordlists, никаких ?id=1'.
Wave 3+: targeted by service type
После Wave 2 у вас классификация хостов. Теперь параллелим по группам сервисов, не по хостам:
group-orchestration: portainer, k8s-dashboard, rancher, docker-api
group-monitoring: grafana, prometheus, kibana, alertmanager
group-api: graphql, swagger, openapi, actuator
group-cms: wp-login, drupal, joomla, ghost
group-storage: minio, s3-public, ftp, smb
Каждая группа = один ad-hoc subagent, 4–6 групп параллельно. Каждый агент знает только свою группу, не отвлекается на чужое.
Между волнами — sync + reassess
Это самая частая ошибка middle: «волны идут конвейером». Нет. После каждой волны:
- fan-in: все subagents завершились
- главный агент читает summaries
- обновляет PTT — что нашли, что отвалилось, что добавилось
- переоценивает scope — нашли sister-domain? STOP, согласовываем
- только потом — следующая волна
Без шага 3–4 вы получите Wave 5 на хостах, которые вообще не in-scope.
Adversarial
7. Adversarial scenarios
WAF reflective (Cloudflare, Cloudbric, Imperva)
Симптом: 403 на каждый «странный» query, 200 на нормальные. Подходы:
- Не бить в WAF, бить в origin. Поиск origin: censys/shodan по favicon hash, по unique header, по certificate SAN.
- Замедление —
-rate-limit 1, sleep 1.5. WAF reactive smoothing работает на window-based detection, медленный fuzz часто проходит.
- Headers-rotation — через nuclei
-H с разными User-Agent, легитимными Accept-Language. Не панацея, но снижает score.
🚫Что НЕ делать: bypass-нагрузки типа ?x=1' OR/**/1=1. WAF их ловит первыми, плюс это bot-signal. В промпте: [OPSEC] Cloudflare detected, rate-limit ≤1 rps, no obvious payload mutation, prefer origin discovery via certificate SAN.
Aggressive rate-limit (429 на всё)
- Backoff exponential в промпте:
if 429: sleep $((2**retry)), max retry 3
- Распределение по сабам — если limit per-host, кручусь по 5 сабам кругом, по одному запросу на каждом.
- Часто 429 — сигнал «таргет видит нас». Пауза 30 минут, потом lighter approach.
GeoIP block (нужен UZ-egress, например)
Местные сервисы часто блокируют не-UZ IP. VPN с UZ exit-node. Проверка в Wave 1:
curl -s ifconfig.io/country_code # должно быть UZ
curl -s -o /dev/null -w "%{http_code}" "https://${T}" # должно быть 200, не 451
Если нужен UZ-egress, а у VPN провайдера его нет — residential proxy через авторизованного брокера, не «купил на форуме». Это юридический вопрос, не технический.
Legal scope mid-engagement
Сценарий: в Wave 2 нашли api.sister-company.tld который явно связан с таргетом, но в scope не входит. Действие:
1
STOP
На этом активе. Не продолжать probes.
2
Зафиксировать в internal.md
[SCOPE-DOUBT] api.sister-company.tld found via CT log, not in TKT-1234 scope
3
Эскалация заказчику
Расширение scope письменно или явное «вне scope, не трогать».
4
После ответа
Продолжаем или удаляем находку из дерева.
⚖️Это не paranoia, это compliance. Outside-scope action = breach of contract = ваша лицензия / репутация.
Reporting
8. Reporting workflow
Reporter — отдельный субагент с особой осторожностью.
3x verification на CRITICAL
Каждый CRITICAL финдинг проходит 3 независимых подтверждения:
- Original PoC от subagent, нашедшего находку.
- Reproduce от reporter — другой subagent, другой промпт, тот же expected output.
- Manual replay — вы лично выполняете команду из отчёта, сравниваете.
Если шаг 3 не сходится — финдинг снимается до выяснения. Лучше пропустить CRITICAL, чем написать ложный.
Pandoc + pdf_style.css
pandoc tech.md -o tech.pdf \
--css=/Users/you/infosec/pdf_style.css \
--pdf-engine=weasyprint \
--metadata title="Технический отчёт"
Reporter генерит markdown под этот pipeline, не пытается рисовать PDF сам. Любые попытки агента «сделать красивый PDF» через LaTeX — это bug-генератор.
Mandatory section 0 «О характере работ»
Идентичен в exec.pdf и tech.pdf. Reporter получает готовый шаблон этого раздела (из ~/infosec/REPORT_STANDARD.md), вставляет с подстановками: даты, scope, ticket-id, профиль атакующего.
🚫Запрещены формулировки: «белый хакер» (расплывчато, не профиль) · «пентестер» (это сотрудник, не модель) · «любой с интернетом» (не L-уровень).
Только конкретный L-профиль:
| L | Кто | Доступы |
| L1 | Anonymous internet | Только публичный интернет, no creds |
| L2 | Authenticated user | Обычный аккаунт, базовые роли |
| L3 | Privileged user | Менеджер, тренер, оператор |
| L4 | Admin | Полные права на админ-панель |
В промпте reporter: «использовать только L1–L4, профиль выбрать из контекста финдинга, обосновать в 1 предложении».
PII masking automation
Reporter получает два sed-скрипта:
s/[a-zA-Z0-9._%+-]\+@\([a-zA-Z0-9.-]\+\)\.\([a-z]\{2,\}\)/u***@\1.\2/g
s/+998 \([0-9]\{2\}\) [0-9]\{3\} [0-9]\{2\} \([0-9]\{2\}\)/+998 \1 *** ** \2/g
Прогон до записи tech.md и exec.md. Любые остатки PII в выходе — обратно к reporter с pointer на конкретную строку.
Token budget
9. Cost / token budget
Главные пожиратели токенов в vibe-hacking:
| Источник | Расход | Решение |
chrome-devtools take_screenshot | ~125K за shot | snapshot (DOM-text), screenshot только для финальной визуальной верификации |
Полный internal.md в каждом промпте | растёт линейно | не передавать целиком, только релевантный срез (последняя волна + tripwires) |
| nuclei JSONL output | до 50K на хост | парсить заранее, в parent отдавать только matched entries |
| Verbose curl logs | ~2K на запрос с -v | -sS + -w "%{http_code} %{size_download}\n" |
| TodoList с 50 узлами | 3–5K на проход | держать live-PTT обрезанным до текущей фазы |
cat large-file.json | весь файл | jq с фильтром, или sed -n 'N,Mp' |
Микро-оптимизации, которые реально работают:
- Не передавать loot/ файлы инлайн в промпт — отдавать путь, агент сам читает.
- Output JSON, не prose — JSON в 2–3x плотнее по токенам на единицу информации, плюс детерминированный парсинг.
- Закрывать done-tasks — статус
[x] в PTT не должен тащить за собой evidence-блок в каждый последующий промпт.
Бюджетирование: на типовой 1-часовой engagement я закладываю ~150–200K токенов parent context, ~500K суммарно по subagents. Если выходим за это — где-то промпты слишком общие.
Case study
10. Failure case study (анонимизировано)
Анонимизированный runУчебный stand-in таргет, ~30 поддоменов, Django-стек, типичный SaaS. Запуск 11-волновой DAG, реальное время — 2 часа парент-контекста + ~6 часов суммарного subagent runtime (паралелились).
Что нашли:
- 4× CRITICAL: exposed
.git/, exposed Django debug page с SECRET_KEY, MinIO с anonymous list+download, Portainer без auth.
- 7× HIGH: missing security headers сериями, GraphQL introspection enabled, Swagger без auth с write-операциями, hardcoded creds в JS-bundle, IDOR в API через предсказуемый ID, weak JWT secret (offline crackable), open registration в admin-сегменте.
- ~20 MEDIUM/LOW.
✓ Что сработало
Wave 1 passive дал 70% surface — без единого запроса к таргету (CT + GitHub dorks + wayback). Conditional probe на .git/HEAD сэкономил ~2 часа slop по wordlist. service-classifier разделил 30 хостов на 5 групп. Reporter с 3x verification поймал 1 ложный CRITICAL (server-side stripping, не реальная утечка).
✕ Что сломалось
На Wave 4 запустили 9 subagents — main agent захлебнулся при fan-in (урок: 6 max). nuclei в background ушёл на 40 минут, watchdog не сработал — прогресс был крошечный (урок: по «no meaningful output»). Под RTK-хуком завис tight curl loop → /usr/bin/curl. Sister-domain не сразу заметили — 20 минут out-of-scope probes.
Технические уроки:
- DAG > линейные волны, но fan-in barriers обязательны.
- Conditional probes снижают шум на порядок.
- Watchdog не на «нет вывода», а на «нет нового сигнала».
- Reporter всегда с 3x verify, иначе CRITICAL-инфляция.
- Scope-drift ловится только явным auto-flag в planner.
TL;DR
11. TL;DR для senior-уровня
- Думайте графом, не лестницей.
blocked_by — ваш друг.
- 4–6 параллельных, никогда 12+.
- Conditional probes > линейный wordlist.
- Промпт = CONTEXT/COMMANDS/CONSTRAINTS/OPSEC/OUTPUT. Без свободы.
/usr/bin/curl для batch loops.
- nuclei для верификации, не для discovery.
- SQLMap/hashcat/Burp — руками, не через агента.
- Reporter с 3x verification на CRITICAL, иначе ложный alarm в exec.pdf и репутационный долг.
- Sister-domain → STOP → эскалация. Compliance > findings.
- watchdog 600s no-meaningful-progress, retry с другим промптом.
Vibe-hacking на L+1 — это не «больше агентов». Это меньше агентов, точнее промпты, жёстче барьеры. Скорость даёт точность, не параллелизм.
🎯Who for? Engineers who already know OWASP top-10, drove Burp/nuclei/ffuf manually, understand OPSEC, and have run at least one mid+ engagement. The base vibehacking.md is about the idea. This lesson is about actually driving agents under load without burning your context window or tripping every WAF on the way.
If you're past L-1, drop the "parallel pentest" framing. Think dependency graph, where nodes are subagents with predefined input/output contracts and edges are blocked_by chains in TaskCreate. The main agent is not a dispatcher — it's a graph scheduler plus an aggregation sink.
DAG · Fan-out / Fan-in
1. Subagent orchestration — fan-out / fan-in / DAG
The base wave template (Wave 1 → 2 → 3) is a ladder. A real engagement is a DAG where half the nodes block on each other.
planner
│
┌────────────┼────────────┐
▼ ▼ ▼
recon-dns recon-ct recon-asn (Wave 1, fan-out)
└────────────┼────────────┘
▼
merge-targets (Wave 1.5, fan-in barrier)
│
┌────────────┼────────────┐
▼ ▼ ▼
web-fp tls-audit headers (Wave 2)
└────────────┼────────────┘
▼
service-classifier (Wave 2.5, human touch)
│
┌────────┬────────┼────────┬────────┐
▼ ▼ ▼ ▼ ▼
graphql swagger actuator portainer minio (Wave 3, parallel groups)
Graph rules:
blocked_by is strict. web-fp does not start until merge-targets returns targets.json. Skip it and you get duplicate work or runs against a stale target list.
- Fan-in barriers are mandatory. After every wave, sync via a
merge-* agent. Without the barrier, the dispatcher reads partial data and makes "half-decisions".
- Every node must be idempotent. Re-running it cannot break anything. Each subagent writes to
loot/<wave>/<host>/<agent>.json with full overwrite of its own file, never append.
- Parallelism = 4–6, never more. The main agent's context window is finite: 12+ concurrent subagents means 12+ summaries in the fan-in step, and the main agent chokes on aggregation. Two waves of five beat one wave of ten.
Subagent type cheat sheet:
Only at phase start
On hard scope changes (new domain, scope expansion approved)
Explicitly flags "new domains without clear in-scope" every wave
DNS / CT / whois / ASN / OSINT
No HTTP to target — zero footprint
Probes, fingerprint, headers, known paths
Rate-limit goes in the prompt
Only at the end
Only after internal.md is stable
graphql introspection, actuator dump — single targeted job
Short prompt, one objective — 3 curls, 200 words
⚠️Mid-level mistake: using a web agent where you actually want ad-hoc. web agents start "exploring" and burn tokens. Ad-hoc fires three curls and returns 200 words.
Prompt anatomy
2. Prompt engineering — anatomy of a security prompt
Unlike dev tasks, security prompts do not forgive freedom. Every "figure it out" produces extra noise in the target's logs and +5K tokens in your context.
Canonical anatomy:
[CONTEXT] — who you are, target, scope, what is already known
[COMMANDS] — verbatim commands or curl/dig equivalents
[CONSTRAINTS]— rps, timeout, max bytes, retry policy
[OPSEC] — tripwires, stop conditions, what NOT to do
[OUTPUT] — format, length, file path, JSON schema
🔧Why this prompt? The bad one tells the agent to find "something interesting" — it starts "exploring" the whole surface, burning context and target logs. The good one gives every step verbatim: for copy-paste and for deterministic output.
# bad — middle level
"Do recon on app.example.tld, see what looks interesting"
# good — L+1
[CONTEXT] target: app.example.tld, scope: *.example.tld, auth ref: TKT-1234
[COMMANDS]
1) dig +short A,AAAA,MX,NS,TXT app.example.tld
2) for s in $(cat loot/wave1/subs.txt); do
dig +short A "$s"
done
3) curl -s "https://crt.sh/?q=%25.example.tld&output=json" | jq -r '.[].name_value' | sort -u
[CONSTRAINTS] sleep 0.3 between requests; max wallclock 4 min
[OPSEC] passive only; no HTTP to *.example.tld; VPN check first: curl -s ifconfig.me
[OUTPUT] loot/wave1/recon-dns.json — schema: {subs: [...], a_records: {...}, anomalies: [...]}, ≤ 350 words prose
Conditional probes — branching inside the prompt
The most underrated L+1 trick. Instead of "do N requests" — "do one request, decide the next step from the response". Saves both context and noise.
🌳Why this prompt? It bakes branching logic into the prompt: the agent doesn't fire 200 probes, it fires one and decides to stop based on the response. PoC sentinel — found .git/ → STOP, no wordlist burn.
[COMMANDS]
1) curl -s -o /dev/null -w "%{http_code}" "https://${T}/.git/HEAD" --max-time 6
2) IF status == 200 AND body starts with "ref: ":
→ curl -s "https://${T}/.git/config" --max-time 6 → loot/wave4/${T}/config
→ STOP. Do not try .env, .DS_Store. Record PoC, report to dispatcher.
ELIF status == 403:
→ mark "blocked but exists?", do not continue.
ELSE:
→ next probe in list
Principle: the agent decides whether to continue itself. PoC sentinel baked into the prompt. Without it, agents will grind through the whole wordlist even after they already found .git/.
Output constraints as context control
≤ 400 words hard, otherwise the agent writes an essay.
JSON schema explicit, otherwise reporter cannot parse later.
file path mandatory, otherwise output streams into the parent's window.
no chain-of-thought in final output — separate line. Otherwise reasoning leaks into parent context.
Inline PII masking
Do not leave masking to the reporter — too late. In every prompt that touches potentially leaked data:
[OPSEC] If output contains email/phone/passport/national-ID — mask BEFORE writing to file:
email: u***@d***.tld (first letter + tld)
phone: +CC NN *** ** NN (first 2, last 2)
passport: AA*******
Never store unmasked content in loot/.
🔒REPORT_STANDARD enforces this at report level, but if PII lands in loot/ you have a compliance fire later. Don't leave masking to the reporter.
Toolchain
3. Toolchain integration
Agents are not a replacement for tools, they orchestrate over them. What to wire in and how.
Burp Collaborator
Out-of-band channels for blind vulns. Pass the collaborator URL explicitly in the prompt, do not expect the agent to walk into the Burp UI.
[CONTEXT] OOB channel: xyz123.oastify.com (Burp Collaborator, authorized)
[COMMANDS]
1) curl -s "https://${T}/api/import" -d '{"url":"http://xyz123.oastify.com/ssrf-probe-01"}'
2) sleep 8
3) Do not poll collaborator — I'll do that after your report. Just record probe-id.
[OUTPUT] {probe_id: "ssrf-probe-01", target_endpoint: "...", payload_used: "..."}
Do not give collaborator polling to an agent — it's interactive XML auth, agents misbehave on it. Record probe-id, you poll.
Nuclei as "second brain"
Nuclei is not for discovery, it's for verification. Found something manually → run nuclei with a narrow tag filter → independent confirmation.
[COMMANDS]
nuclei -u "https://${T}" -tags exposure,misconfig -severity high,critical \
-rate-limit 5 -timeout 8 -no-color -j -o loot/wave3/${T}/nuclei.jsonl
[CONSTRAINTS] -rate-limit 5 mandatory, otherwise CF bans the /24
[OUTPUT] parse jsonl, only matched entries to final report, ≤ 10 findings
Do not run nuclei -t cves/ without filters — that's 8000+ templates and an hour of runtime.
ffuf — wordlist via explicit path
[COMMANDS]
ffuf -w /Users/you/infosec/wordlists/quickhits.txt:FUZZ \
-u "https://${T}/FUZZ" \
-mc 200,401,403 -fs 0 -t 10 -p 0.2 -o loot/wave3/${T}/ffuf.json
Not "use a wordlist" — full path. Otherwise the agent reaches for /usr/share/wordlists/... which doesn't exist on your box, fails, burns retries. And -t 10, not the default 40 — otherwise any rate limiter spots you instantly.
What goes through an agent, what stays manual
NOT through an agent — by hand
- SQLMap — long-running, huge output, timing-sensitive
- Hashcat / john — same plus GPU
- Burp active scanner — steals the session, noisy
- Metasploit — interactive prompts
Agent shines here
- recon / fingerprint — fire-and-forget
- nuclei with a narrow tag filter (verification)
- ffuf with a short wordlist + explicit path
- conditional probes — clean exit code
Rule: if a tool needs watch-and-tweak in real time — do it manually. Agents shine at fire-and-forget with a clean exit code.
RTK hook and /usr/bin/curl
In an RTK environment curl is intercepted by a hook. Single requests are fine, but in a tight loop (for i in $(seq 1 200); do curl ...) the hook occasionally chokes and the whole loop hangs on ECONNRESET. Workaround — explicit path:
/usr/bin/curl --max-time 8 -sS "$url"
👻Ghost-constraint: bake into prompts — "use /usr/bin/curl for batch >10 iterations". Many engineers say "curl is broken", no, it's the RTK hook choking in a loop.
Concurrency · Failure modes
4. Concurrency limits & failure modes
Main agent's context window
Every subagent returns a 200–400 word summary ≈ 500–1000 tokens. 12 concurrent subagents = 12 parallel tails in parent context. With tool_use/tool_result wrappers x2. That's 24K tokens just for the aggregation phase. Burns the window in 2–3 waves.
Limit: 6 concurrent, ideally 4. Want more? Split into two sequential waves. You barely lose wall time (each subagent runs ~5–10 min), and the main agent stays fresh.
ECONNRESET on long background tasks
🔌ECONNRESET tripwire: background tasks in Claude Code occasionally drop the upstream API connection after 30+ minutes. Symptom: subagent "freezes", no progress.
- Watchdog 600s no-progress — main agent enforces a deadline per subagent task. No return in 10 min → kill, retry with a new prompt.
- Don't put everything in background. Background only for genuinely long jobs (full nuclei template set). Regular probes go foreground, finish faster.
When an agent is "stuck"
Signs: TaskCreate stays running, last tool_use 5+ minutes ago, nothing new in logs. Causes:
- ECONNRESET the subagent never reported
- tight
curl loop under RTK
- nuclei stuck pulling 5 MB from a flaky template repo
🔁Action: kill task, rebuild prompt, retry. Never retry the same prompt — it just wastes another 10 minutes.
Retry strategy
| Symptom | Action |
| Subagent returned empty / "I cannot do this" | retry with more concrete commands, not the same prompt |
| ECONNRESET | retry foreground, no background |
| 3+ retries failed in a row | stop, revisit task: target may be down or scope wrong |
| Target starts 429ing every subagent | 10 min pause, retry with rate-limit /2 |
🛑3-fail rule (engineering.md): 3 attempts in a row = you're doing the wrong thing. Stop, revise the task.
Pentest Task Tree
5. PTT-driven engagements (Pentest Task Tree)
PTT is the TodoList for a pentest, but with a contract: each node has status, owner (subagent type), evidence path, severity.
PTT (Wave 3, target: app.example.tld)
[x] graphql-introspection → ad-hoc subagent → loot/w3/graphql.json [info]
[x] swagger-discovery → web subagent → loot/w3/swagger.json [info]
[ ] actuator-heapdump → ad-hoc subagent → blocked_by: swagger [pending]
[!] portainer-noauth → web subagent → loot/w3/portainer.md [HIGH, tripwire]
[ ] minio-anonymous-list → ad-hoc subagent → [pending]
When to update:
- After every wave — the fan-in barrier updates statuses.
- On tripwire firing — add
[!] nodes for escalation.
- On scope expansion — rebuild the affected subtree, not the whole PTT.
Live PTT vs review-end PTT:
- Live PTT — main agent sees it in every prompt (passed as a context block). Useful when waves >5 and it's easy to lose your place.
- Review-end PTT — assembled at the end, lands in
internal.md. Audit trail.
I keep both, but trim the live version to current wave + previous. Pushing the full historical PTT into every prompt is +3K tokens for nothing.
Wave architecture
6. Wave architecture (deep)
Wave 1: passive only — zero footprint
Public sources only, not a single request to target:
recon-dns → dig against public resolvers (8.8.8.8, 1.1.1.1)
recon-ct → crt.sh, certspotter
recon-asn → bgp.he.net, hackertarget aslookup
recon-osint → GitHub dorks, wayback machine, archive.org
recon-leaks → DeHashed-style presence checks, no credential lookup
Target should not know you exist. If Wave 1 returns nothing interesting — scope is narrow, talk to the client, don't go into Wave 2 blindly.
Wave 2: light active — fingerprint
web-headers → curl -I across all discovered subs, parse Server/X-Powered-By/Set-Cookie
web-tls → testssl.sh --quick (NOT full)
web-fp → whatweb or equivalent, lite mode
web-robots → robots.txt + sitemap.xml + /.well-known/security.txt
Noise level — single GET per host, indistinguishable from googlebot. No wordlists, no ?id=1'.
Wave 3+: targeted by service type
After Wave 2 you have host classification. Now parallelize by service group, not by host:
group-orchestration: portainer, k8s-dashboard, rancher, docker-api
group-monitoring: grafana, prometheus, kibana, alertmanager
group-api: graphql, swagger, openapi, actuator
group-cms: wp-login, drupal, joomla, ghost
group-storage: minio, s3-public, ftp, smb
Each group = one ad-hoc subagent, 4–6 groups in parallel. Each agent knows only its group, doesn't get distracted.
Between waves — sync + reassess
Most common middle-level mistake: "waves run as conveyor". They don't. After each wave:
- fan-in: all subagents finished
- main agent reads summaries
- updates PTT — what was found, what dropped off, what was added
- reassesses scope — found a sister domain? STOP, escalate
- only then — next wave
Skip steps 3–4 and you'll run Wave 5 against hosts that were never in scope.
Adversarial
7. Adversarial scenarios
Reflective WAF (Cloudflare, Cloudbric, Imperva)
Symptom: 403 on every "weird" query, 200 on normal ones. Approaches:
- Don't hit the WAF, hit the origin. Find origin via censys/shodan with favicon hash, unique header, certificate SAN.
- Slowdown —
-rate-limit 1, sleep 1.5. WAF reactive smoothing works on window-based detection, slow fuzz often slips through.
- Header rotation — nuclei
-H with varied User-Agent, legitimate Accept-Language. Not a panacea, but lowers score.
🚫Don't: push obvious bypass payloads like ?x=1' OR/**/1=1. WAF catches them first, plus it's a bot signal. In the prompt: [OPSEC] Cloudflare detected, rate-limit ≤1 rps, no obvious payload mutation, prefer origin discovery via certificate SAN.
Aggressive rate-limit (429 on everything)
- Exponential backoff in the prompt:
if 429: sleep $((2**retry)), max retry 3
- Spread across subdomains — if limit is per-host, rotate across 5 subs, one request each.
- 429 often means "target sees us". 30 min pause, then a lighter approach.
GeoIP block (need country-specific egress)
Local services often block foreign IPs. VPN with a local exit node. Check in Wave 1:
curl -s ifconfig.io/country_code # must be expected country
curl -s -o /dev/null -w "%{http_code}" "https://${T}" # must be 200, not 451
If you need a specific geo egress and your VPN provider lacks it — residential proxy via an authorized broker, not "bought on a forum". This is a legal question, not a technical one.
Mid-engagement legal scope
Scenario: in Wave 2 you find api.sister-company.tld clearly related to target but not in scope. Action:
1
STOP
On that asset. No further probes.
2
Record in internal.md
[SCOPE-DOUBT] api.sister-company.tld found via CT log, not in TKT-1234 scope
3
Escalate to client
Written scope expansion or explicit "out of scope, do not touch".
4
After the answer
Continue or remove from tree.
⚖️Not paranoia, this is compliance. Out-of-scope action = breach of contract = your license/reputation.
Reporting
8. Reporting workflow
Reporter is a separate subagent handled with extra care.
3x verification on CRITICAL
Every CRITICAL finding goes through 3 independent confirmations:
- Original PoC from the discovering subagent.
- Reproduction by the reporter — different subagent, different prompt, same expected output.
- Manual replay — you personally execute the command from the report and compare.
If step 3 doesn't match — finding is dropped until clarified. Better to skip a real CRITICAL than ship a false one.
Pandoc + pdf_style.css
pandoc tech.md -o tech.pdf \
--css=/Users/you/infosec/pdf_style.css \
--pdf-engine=weasyprint \
--metadata title="Technical report"
Reporter writes markdown for this pipeline, doesn't try to render PDF itself. Any "let me build a nice PDF in LaTeX" attempt is a bug factory.
Mandatory section 0 "About the engagement"
Identical in exec.pdf and tech.pdf. Reporter receives the prepared template for this section (from ~/infosec/REPORT_STANDARD.md), substitutes dates, scope, ticket-id, attacker profile.
🚫Banned wording: "white hat" (fuzzy, not a profile) · "pentester" (that's an employee, not a model) · "anyone with internet" (not an L-tier).
Only concrete L-profiles:
| L | Who | Access |
| L1 | Anonymous internet | Public internet only, no creds |
| L2 | Authenticated user | Regular account, basic role |
| L3 | Privileged user | Manager, instructor, operator |
| L4 | Admin | Full admin panel rights |
In the reporter prompt: "use only L1–L4, pick the profile from finding context, justify in one sentence".
PII masking automation
Reporter gets two sed scripts:
s/[a-zA-Z0-9._%+-]\+@\([a-zA-Z0-9.-]\+\)\.\([a-z]\{2,\}\)/u***@\1.\2/g
s/+[0-9]\{1,3\} \([0-9]\{2\}\) [0-9]\{3\} [0-9]\{2\} \([0-9]\{2\}\)/+XX \1 *** ** \2/g
Run before writing tech.md and exec.md. Any residual PII in output sends the report back to reporter with a pointer to the offending line.
Token budget
9. Cost / token budget
Top token sinks in vibe-hacking:
| Source | Cost | Mitigation |
chrome-devtools take_screenshot | ~125K per shot | snapshot (DOM text); screenshot only for final visual verification |
Full internal.md in every prompt | grows linearly | don't pass whole file, only the current slice (last wave + tripwires) |
| nuclei JSONL output | up to 50K per host | parse upstream, return only matched entries to parent |
| Verbose curl logs | ~2K per request with -v | -sS + -w "%{http_code} %{size_download}\n" |
| TodoList with 50 nodes | 3–5K per pass | trim live PTT to current phase |
cat large-file.json | full file | jq with filter or sed -n 'N,Mp' |
Micro-optimizations that actually pay:
- Don't inline loot/ files into prompts — pass paths, agents read themselves.
- Output JSON, not prose — JSON is 2–3x denser per unit of info, plus deterministic parsing.
- Close done-tasks —
[x] status in PTT shouldn't drag its evidence block into every later prompt.
Budget envelope: a typical 1-hour engagement is ~150–200K parent tokens, ~500K total across subagents. Going over means prompts are too vague somewhere.
Case study
10. Failure case study (anonymized)
Anonymized runStand-in training target, ~30 subdomains, Django stack, typical SaaS. Ran an 11-wave DAG, ~2 hours of parent-context wall time, ~6 hours of subagent runtime in parallel.
Found:
- 4× CRITICAL: exposed
.git/, exposed Django debug page with SECRET_KEY, MinIO with anonymous list+download, Portainer without auth.
- 7× HIGH: missing security headers across the board, GraphQL introspection enabled, Swagger without auth exposing write operations, hardcoded creds in JS bundle, IDOR in API via predictable IDs, weak JWT secret (offline crackable), open registration in admin segment.
- ~20 MEDIUM/LOW.
✓ What worked
Wave 1 passive yielded 70% of the surface — not a single request to target (CT + GitHub dorks + wayback). Conditional probe on .git/HEAD saved ~2 hours of wordlist slop. service-classifier split 30 hosts into 5 groups. Reporter with 3x verification caught one false CRITICAL (server-side stripping, not an actual leak).
✕ What broke
Wave 4 launched 9 subagents — main agent choked on fan-in (lesson: 6 max). Nuclei in background ran 40 min, watchdog didn't fire — progress was tiny (lesson: "no meaningful output"). RTK hook hung a tight curl loop → /usr/bin/curl. Sister-domain not spotted immediately — 20 min of out-of-scope probes.
Technical lessons:
- DAG > linear waves, but fan-in barriers are mandatory.
- Conditional probes drop noise by an order of magnitude.
- Watchdog on "no new signal", not on "no output".
- Reporter always with 3x verify, otherwise CRITICAL inflation.
- Scope drift only catches via explicit auto-flag in planner.
TL;DR
11. TL;DR for senior level
- Think graph, not ladder.
blocked_by is your friend.
- 4–6 concurrent, never 12+.
- Conditional probes > linear wordlists.
- Prompt = CONTEXT/COMMANDS/CONSTRAINTS/OPSEC/OUTPUT. No freedom.
/usr/bin/curl for batch loops.
- Nuclei for verification, not discovery.
- SQLMap/hashcat/Burp — manual, not via agent.
- Reporter with 3x verification on CRITICAL, otherwise false alarms in exec.pdf and reputation debt.
- Sister-domain → STOP → escalate. Compliance > findings.
- Watchdog 600s no-meaningful-progress, retry with a different prompt.
L+1 vibe-hacking is not "more agents". It's fewer agents, sharper prompts, harder barriers. Speed comes from precision, not parallelism.