[SHEHZADE//ABDULLAH ANSARI]
Red Team Consultant @ Google | MS in Cybersecurity @ NYU
● 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)
feat: add dashboard and CR80 showcases to README, refine search queries, and improve session resilience
Abdullah Ansari · 2026-11-06 20:10 ·
290db81e74f9ee18b3b58421e076e56aefb27d37 README.md
| ... | ... | @@ -2,6 +2,10 @@ |
| 2 | 2 | |
| 3 | 3 | AI-driven badge OSINT, credential harvesting, and calibrated CR80 card cloning for physical red teams. |
| 4 | 4 | |
| 5 | +<p align="center"> | |
| 6 | + <img src="assets/dashboard-demo.png" alt="Lanyard Badge Intelligence Dossier" width="100%"> | |
| 7 | +</p> | |
| 8 | + | |
| 5 | 9 | ## Table of Contents |
| 6 | 10 | |
| 7 | 11 | - [Description](#description) |
| ... | ... | @@ -199,6 +203,15 @@ Open `http://127.0.0.1:8088/report.html` in your browser. The gallery includes: |
| 199 | 203 | - Pan and zoom controls for reading dense text, barcodes, and chip serials. |
| 200 | 204 | - Filter controls for badge type (employee, contractor, visitor). |
| 201 | 205 | |
| 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 | + | |
| 202 | 215 | ### 4. Exporting Reports |
| 203 | 216 | |
| 204 | 217 | 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 |
| 32 | 32 | finally: |
| 33 | 33 | try: |
| 34 | 34 | 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: | |
| 36 | 37 | save_cookies(current_cookies) |
| 37 | 38 | except Exception: |
| 38 | 39 | pass |
lanyard/cli.py
| ... | ... | @@ -249,11 +249,15 @@ def hunt( |
| 249 | 249 | queries = [f'"{company}" ({keyword.strip()})'] |
| 250 | 250 | elif multi_query: |
| 251 | 251 | 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"', | |
| 257 | 261 | ] |
| 258 | 262 | else: |
| 259 | 263 | search_kw = " OR ".join(f'"{k}"' for k in cfg.default_keywords[:3]) |
lanyard/sources/linkedin.py
| ... | ... | @@ -16,6 +16,9 @@ class LinkedInScraper: |
| 16 | 16 | |
| 17 | 17 | def is_authenticated(self, page: Page) -> bool: |
| 18 | 18 | 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() | |
| 19 | 22 | return not ("login" in url or "checkpoint" in url or "authwall" in url) |
| 20 | 23 | |
| 21 | 24 | def warm_session(self, page: Page) -> bool: |
| ... | ... | @@ -26,6 +29,8 @@ class LinkedInScraper: |
| 26 | 29 | timeout=30000, |
| 27 | 30 | ) |
| 28 | 31 | time.sleep(2.5) |
| 32 | + if "remember-me" in page.url.lower() or "ssr-login" in page.url.lower(): | |
| 33 | + time.sleep(4.0) | |
| 29 | 34 | return self.is_authenticated(page) |
| 30 | 35 | except Exception: |
| 31 | 36 | return False |
| ... | ... | @@ -50,6 +55,8 @@ class LinkedInScraper: |
| 50 | 55 | console.print(f"[cyan][*] Navigating to search feed: [bold]{query}[/bold][/cyan]") |
| 51 | 56 | page.goto(search_url, wait_until="domcontentloaded", timeout=45000) |
| 52 | 57 | time.sleep(3.5) |
| 58 | + if "remember-me" in page.url.lower() or "ssr-login" in page.url.lower(): | |
| 59 | + time.sleep(4.0) | |
| 53 | 60 | |
| 54 | 61 | if not self.is_authenticated(page): |
| 55 | 62 | console.print("[dim yellow][*] Session warming up via /feed...[/dim yellow]") |