● Notice //GitHub (@shehzade) is temporarily suspended pending an automated false-positive review. All tools are hosted on this Code Mirror and available on request (contact); GitHub links will return once reinstated.
PUBLIC MIRRORPython

AI-driven badge OSINT, credential harvesting, and calibrated CR80 card cloning for physical red teams.

$ git clone https://shehzade.io/repos/lanyard.git Copy
Source ZIP (1.35 MB)
Code 35 Commits 2
feat: add dashboard and CR80 showcases to README, refine search queries, and improve session resilience
Abdullah Ansari · 2026-11-06 20:10 · 290db81e74f9ee18b3b58421e076e56aefb27d37
+31 -6 ← All Commits
README.md
......@@ -2,6 +2,10 @@
22
33 AI-driven badge OSINT, credential harvesting, and calibrated CR80 card cloning for physical red teams.
44
5+<p align="center">
6+ <img src="assets/dashboard-demo.png" alt="Lanyard Badge Intelligence Dossier" width="100%">
7+</p>
8+
59 ## Table of Contents
610
711 - [Description](#description)
......@@ -199,6 +203,15 @@ Open `http://127.0.0.1:8088/report.html` in your browser. The gallery includes:
199203 - Pan and zoom controls for reading dense text, barcodes, and chip serials.
200204 - Filter controls for badge type (employee, contractor, visitor).
201205
206+#### Calibrated CR80 Card Reconstruction (300 DPI)
207+
208+Lanyard detects badge boundary corners and applies a perspective homography matrix to output flat, print-ready PVC cards (85.6mm × 54.0mm):
209+
210+| Shi Choong (Intel) | Wendy Ngoh (Intel) | Kosta Briger (Intel) |
211+| :---: | :---: | :---: |
212+| <img src="assets/cr80-shi-choong.png" width="280" alt="Shi Choong CR80 Badge"> | <img src="assets/cr80-wendy-ngoh.png" width="280" alt="Wendy Ngoh CR80 Badge"> | <img src="assets/cr80-kosta-briger.png" width="280" alt="Kosta Briger CR80 Badge"> |
213+| *Extracted: Name, Photo, Corporate Branding* | *Extracted: Name, Photo, RFID Geometry* | *Extracted: Dual-Language Typography, Badge Reel* |
214+
202215 ### 4. Exporting Reports
203216
204217 Regenerate HTML, CSV, and JSONL deliverables from an existing `lanyard.db`:
assets/cr80-kosta-briger.png
Binary files /dev/null and b/assets/cr80-kosta-briger.png differ
assets/cr80-shi-choong.png
Binary files /dev/null and b/assets/cr80-shi-choong.png differ
assets/cr80-wendy-ngoh.png
Binary files /dev/null and b/assets/cr80-wendy-ngoh.png differ
assets/dashboard-demo.png
Binary files /dev/null and b/assets/dashboard-demo.png differ
lanyard/browser/driver.py
......@@ -32,7 +32,8 @@ def get_browser_context(headless: bool = True) -> Generator[BrowserContext, None
3232 finally:
3333 try:
3434 current_cookies = context.cookies()
35- if current_cookies:
35+ has_auth = any(c.get("name") in ("li_at", "li_rm") for c in current_cookies)
36+ if current_cookies and has_auth:
3637 save_cookies(current_cookies)
3738 except Exception:
3839 pass
lanyard/cli.py
......@@ -249,11 +249,15 @@ def hunt(
249249 queries = [f'"{company}" ({keyword.strip()})']
250250 elif multi_query:
251251 queries = [
252- f'"{company}" (badge OR "security pass" OR "access card" OR "ID badge")',
253- f'"{company}" ("first day" OR "day 1" OR "day one" OR "new role" OR "welcome to")',
254- f'"{company}" ("new intern" OR "internship" OR "joining" OR "first week")',
255- f'"{company}" ("last day" OR "leaving" OR "farewell" OR "final day")',
256- f'"{company}" (lanyard OR "employee badge" OR "work badge")',
252+ f'{company} "ID badge"',
253+ f'{company} "employee badge"',
254+ f'{company} "first day" badge',
255+ f'{company} "last day" badge',
256+ f'"As I depart {company}"',
257+ f'{company} "turned in my badge"',
258+ f"{company} lanyard",
259+ f"{company} intern badge",
260+ f'{company} "got my badge"',
257261 ]
258262 else:
259263 search_kw = " OR ".join(f'"{k}"' for k in cfg.default_keywords[:3])
lanyard/sources/linkedin.py
......@@ -16,6 +16,9 @@ class LinkedInScraper:
1616
1717 def is_authenticated(self, page: Page) -> bool:
1818 url = page.url.lower()
19+ if "remember-me" in url or "ssr-login" in url:
20+ time.sleep(4.0)
21+ url = page.url.lower()
1922 return not ("login" in url or "checkpoint" in url or "authwall" in url)
2023
2124 def warm_session(self, page: Page) -> bool:
......@@ -26,6 +29,8 @@ class LinkedInScraper:
2629 timeout=30000,
2730 )
2831 time.sleep(2.5)
32+ if "remember-me" in page.url.lower() or "ssr-login" in page.url.lower():
33+ time.sleep(4.0)
2934 return self.is_authenticated(page)
3035 except Exception:
3136 return False
......@@ -50,6 +55,8 @@ class LinkedInScraper:
5055 console.print(f"[cyan][*] Navigating to search feed: [bold]{query}[/bold][/cyan]")
5156 page.goto(search_url, wait_until="domcontentloaded", timeout=45000)
5257 time.sleep(3.5)
58+ if "remember-me" in page.url.lower() or "ssr-login" in page.url.lower():
59+ time.sleep(4.0)
5360
5461 if not self.is_authenticated(page):
5562 console.print("[dim yellow][*] Session warming up via /feed...[/dim yellow]")