<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jer Catallo</title>
    <description>The latest articles on DEV Community by Jer Catallo (@jer_catallo).</description>
    <link>https://dev.to/jer_catallo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3671368%2Fd44fc7ee-06ff-4bc6-a28b-b22473231f2f.png</url>
      <title>DEV Community: Jer Catallo</title>
      <link>https://dev.to/jer_catallo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jer_catallo"/>
    <language>en</language>
    <item>
      <title>IDOR: What Is It and How Does One URL Change Expose Every User's Data?</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Sun, 07 Jun 2026 12:12:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/what-is-idor-and-how-does-one-url-change-expose-every-users-data-1o31</link>
      <guid>https://dev.to/jer_catallo/what-is-idor-and-how-does-one-url-change-expose-every-users-data-1o31</guid>
      <description>&lt;h1&gt;
  
  
  What Is IDOR and How Does One URL Change Expose Every User's Data?
&lt;/h1&gt;

&lt;p&gt;IDOR, or Insecure Direct Object Reference, is one of the most common and easy-to-miss access control flaws in web applications. It happens when a server uses a value from the user, like a number in the URL, to fetch records without checking if that user is allowed to see them. No special tools needed, just a number change in the address bar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brief Introduction
&lt;/h2&gt;

&lt;p&gt;When your profile loads at a URL like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;http://&amp;lt;target&amp;gt;/profile?user_id=51
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server takes that &lt;code&gt;user_id&lt;/code&gt; value and returns the matching record. The problem starts when the server skips the ownership check: it does not verify if the logged-in user actually owns record &lt;code&gt;51&lt;/code&gt;. If you change it to &lt;code&gt;1&lt;/code&gt;, and the server returns someone else's profile, that is an IDOR.&lt;/p&gt;

&lt;p&gt;The impact goes beyond just viewing data. Depending on the endpoint, an attacker could also edit or delete records that do not belong to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;The techniques shown here are for authorized testing only. Running these tests against systems you do not own or have written permission to test is illegal in most countries and violates computer fraud laws. Always get proper authorization before testing, work within a defined scope, and handle any discovered data with care. Use lab environments like TryHackMe or HackTheBox to practice safely.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Identify the Target Endpoint
&lt;/h3&gt;

&lt;p&gt;After logging in, the profile page loads user data based on an ID parameter in the URL. This is the starting point for testing access control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i3f51yovehw0ekumvrd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i3f51yovehw0ekumvrd.png" alt=" " width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page shows the current user's information. The next step is to check whether the server validates ownership before returning data.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Test for IDOR by Modifying the User ID
&lt;/h3&gt;

&lt;p&gt;To test for IDOR, change the &lt;code&gt;user_id&lt;/code&gt; value in the URL to sequential numbers. If the server returns data for other users without any authorization check, the vulnerability is confirmed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /profile?user_id=51  # Original user
GET /profile?user_id=1     # Modified to another user's ID
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no server-side ownership check in place. The application returns the record for whatever ID is passed in.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Access Other Users' Data
&lt;/h3&gt;

&lt;p&gt;Changing the &lt;code&gt;user_id&lt;/code&gt; to different values returns different users' profiles, each with their full details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxacxhg0a0x8oq7rbza2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxacxhg0a0x8oq7rbza2.png" alt=" " width="769" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95xst9auqo955330ngwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F95xst9auqo955330ngwh.png" alt=" " width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvwezvieu5e5azc7hbde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvwezvieu5e5azc7hbde.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each request returns a full profile belonging to a different user. No error, no redirect, no access denied, just the data. This confirms the server is serving records based on the ID value alone, with no check on who is asking.&lt;/p&gt;




&lt;h3&gt;
  
  
  Remediation
&lt;/h3&gt;

&lt;p&gt;To fix this type of vulnerability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add server-side ownership checks: verify the requesting user matches the record owner before returning data&lt;/li&gt;
&lt;li&gt;Use indirect references, such as mapping tables, so internal IDs are never exposed directly in requests&lt;/li&gt;
&lt;li&gt;Apply the principle of least privilege across all endpoints&lt;/li&gt;
&lt;li&gt;Include access control testing in code reviews and security assessments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple server-side check looks like this (example/simulation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: server-side ownership check
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;requested_user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;  &lt;span class="c1"&gt;# Forbidden
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single check would block the entire attack shown above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;IDOR is a straightforward flaw with a serious impact. By changing one number in a URL, you can access data that belongs to any other user in the system. The root cause is always the same: the server trusts user input and skips the ownership check. Fixing it requires access control validation on every endpoint that serves user-specific data. Understanding how IDOR works, including its encoded and hashed variants, gives you the foundation to find and fix it during security reviews.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>todayilearned</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
    <item>
      <title>Cookie Tampering: How Attackers Modify Cookies to Break Into Web Apps (And How You Can Prevent It)</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Sat, 30 May 2026 11:43:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-cookie-tampering-lets-attackers-bypass-authentication-and-how-to-stop-it-5pd</link>
      <guid>https://dev.to/jer_catallo/how-cookie-tampering-lets-attackers-bypass-authentication-and-how-to-stop-it-5pd</guid>
      <description>&lt;p&gt;Cookies are a common way for web apps to remember who you are and what you can access. The server sets a cookie, sends it to your browser, and trusts it on every request. The problem starts when developers put sensitive data like login status or role flags directly in cookies without any protection. An attacker who can read or modify that cookie can just change the values and gain access they should not have.&lt;/p&gt;

&lt;p&gt;This blog covers three common cookie tampering techniques: plain-text manipulation, hashed cookie bypass, and base64 decoding. Each one shows a different way developers get this wrong, and what the right fix looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;This demonstration is for educational purposes only. Cookie tampering against systems you do not own or have explicit permission to test is illegal in most jurisdictions. Always practice these techniques in isolated lab environments or authorized penetration testing engagements. Unauthorized access to computer systems violates laws like the Computer Fraud and Abuse Act (CFAA) in the United States and similar legislation worldwide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Plain-Text Cookie Manipulation
&lt;/h3&gt;

&lt;p&gt;Some apps set cookies with values like &lt;code&gt;logged_in=true&lt;/code&gt; and &lt;code&gt;admin=false&lt;/code&gt; in plain text. The server sends these to your browser and then trusts them on every request with no further checks.&lt;/p&gt;

&lt;p&gt;The server sets these cookies after login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Set-Cookie: logged_in=true; Max-Age=3600; Path=/
Set-Cookie: admin=false; Max-Age=3600; Path=/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send a baseline request with no cookies to see the default response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://&amp;lt;target-ip&amp;gt;/cookie-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response: &lt;code&gt;Not Logged In&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now send the cookies exactly as the server set them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cookie: logged_in=true; admin=false"&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/cookie-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response: &lt;code&gt;Logged In As A User&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;admin&lt;/code&gt; cookie value from &lt;code&gt;false&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cookie: logged_in=true; admin=true"&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/cookie-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response: &lt;code&gt;Logged In As An Admin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;One value change. That is all it takes to become an admin. The server never checks whether the cookie was actually issued with those values. It just reads what you sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Keep authorization data on the server. Store a random session ID in the cookie and map that ID to server-side session storage. Never let the client decide its own role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Hashed Cookie Bypass
&lt;/h3&gt;

&lt;p&gt;Some apps try to add a layer of security by hashing cookie values instead of storing them in plain text. The idea is that hashes look random, so attackers cannot guess them. But hashing is deterministic: the same input always gives the same output. That means if you know what values the server is checking for, you can just compute the hash yourself.&lt;/p&gt;

&lt;p&gt;Here are some common hash outputs for the string &lt;code&gt;1&lt;/code&gt; as a reference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Original&lt;/th&gt;
&lt;th&gt;Hash Method&lt;/th&gt;
&lt;th&gt;Digest&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;MD5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;c4ca4238a0b923820dcc509a6f75849b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;SHA-256&lt;/td&gt;
&lt;td&gt;&lt;code&gt;6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;SHA-512&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed66510a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;SHA-1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;356a192b7913b04c54574d18c28d46e6395428ab&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Generate the MD5 hash for the string &lt;code&gt;admin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt; | &lt;span class="nb"&gt;md5sum&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;21232f297a57a5a743894a0e4a801fc3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Send that hash as the &lt;code&gt;role&lt;/code&gt; cookie value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cookie: role=21232f297a57a5a743894a0e4a801fc3"&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because hashes are consistent, you can generate any value the server expects. You can also use tools like CrackStation or rainbow tables to reverse common hashes back to their original strings. No server secret needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Use HMAC with a server-side secret key to sign cookie values. Add a per-session salt so that even the same role value produces a different signature each session. Plain hashing gives no real protection because the output is always predictable for known inputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Base64-Encoded Cookie Tampering
&lt;/h3&gt;

&lt;p&gt;Base64 is encoding, not encryption. Anyone can decode it with a single command. Yet some apps store session data in base64-encoded cookies and treat the contents as trusted.&lt;/p&gt;

&lt;p&gt;The server sets this cookie after login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Set-Cookie: session=eyJpZCI6MSwiYWRtaW4iOmZhbHNlfQ==; Max-Age=3600; Path=/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decode the value to see what is inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'eyJpZCI6MSwiYWRtaW4iOmZhbHNlfQ=='&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;{"id":1,"admin":false}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It is a JSON object with an &lt;code&gt;admin&lt;/code&gt; field. Change &lt;code&gt;false&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; and re-encode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'{"id":1,"admin":true}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;eyJpZCI6MSwiYWRtaW4iOnRydWV9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Send the modified cookie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cookie: session=eyJpZCI6MSwiYWRtaW4iOnRydWV9"&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server accepts the modified cookie and grants admin access. It never checked whether the cookie was actually signed or issued by the server. You decoded, edited, re-encoded, and sent it back. That is all it took.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Use signed tokens such as JWT with a strong secret key. Always validate the signature on the server before trusting any cookie data. If the cookie needs to contain sensitive fields, use encrypted tokens so the contents cannot be read or modified by the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Cookie tampering works because the server trusts data it should not trust. Plain-text cookies can be changed directly in the request. Hashed cookies are predictable because the same input always gives the same hash. Base64-encoded cookies are just obfuscated, not protected, and anyone can decode and modify them.&lt;/p&gt;

&lt;p&gt;The fix is always the same: keep authorization data on the server, sign everything with a secret key, and never trust what the client sends without verifying it first.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
      <category>tutorial</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Business Logic Flaws: How Attackers Skip Steps in Your App to Get What They Should Never Have</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Sat, 23 May 2026 12:37:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-business-logic-flaws-let-anyone-skip-payment-and-get-pro-access-for-free-2mbd</link>
      <guid>https://dev.to/jer_catallo/how-business-logic-flaws-let-anyone-skip-payment-and-get-pro-access-for-free-2mbd</guid>
      <description>&lt;p&gt;Business logic flaws are vulnerabilities that exist not because of a coding mistake, but because the application trusts its own workflow too much. Instead of exploiting a buffer overflow or injecting code, attackers simply skip steps, repeat actions, or change the order of requests to get outcomes the app never intended. This kind of flaw is common in multi-step processes like account upgrades, order flows, approval chains, and access control checks. Payment bypass is one well-known example, but the same root cause appears across many different features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only test systems you own or have written permission to test&lt;/li&gt;
&lt;li&gt;Use local labs or bug bounty programs with clear scope&lt;/li&gt;
&lt;li&gt;Do not use these techniques against production systems without authorization&lt;/li&gt;
&lt;li&gt;Report findings responsibly to the application owner&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understanding Logic Flaw Workflows
&lt;/h3&gt;

&lt;p&gt;Logic flaws happen when applications trust the order of steps instead of verifying each action on the server side. Attackers can skip, repeat, or reorder requests to reach states the application assumed were impossible to reach.&lt;/p&gt;

&lt;p&gt;Common scenarios where this shows up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payment bypass&lt;/strong&gt;: skip a payment step to get paid features for free&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privilege escalation&lt;/strong&gt;: access an admin endpoint by navigating to it directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order manipulation&lt;/strong&gt;: change item quantities or prices in a multi-step checkout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval skipping&lt;/strong&gt;: jump past a review or verification step in a workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example below uses a payment bypass to show how this works. The application has a three-step upgrade process: select a plan, submit payment, then confirm activation. The confirmation endpoint does not verify that payment was actually completed before granting Pro status.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18d4zu9kz7mgn0ls84tr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18d4zu9kz7mgn0ls84tr.png" alt=" " width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram shows how an attacker can skip step 2 by requesting the confirmation endpoint directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploiting the Missing Payment Check
&lt;/h3&gt;

&lt;p&gt;The vulnerable code assumes that if a user reaches the confirmation page, they must have paid. It grants Pro status without checking for a valid transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /upgrade/confirmed.php&lt;/span&gt;

&lt;span class="c1"&gt;// Logic flaw: The code assumes if you are here, you must have paid.&lt;/span&gt;
&lt;span class="nv"&gt;$user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Directly updating the database to 'pro' status&lt;/span&gt;
&lt;span class="nv"&gt;$sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"UPDATE users SET membership = 'pro' WHERE id = '&lt;/span&gt;&lt;span class="nv"&gt;$user_id&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Congratulations! You are now a Pro member."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An attacker can bypass the payment step by sending a direct request to the confirmation endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://&amp;lt;target-ip&amp;gt;/upgrade/confirmed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server processes the request and updates the user's membership to &lt;code&gt;pro&lt;/code&gt; without any payment verification. The database now shows the user has Pro status even though no transaction occurred.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remediation
&lt;/h3&gt;

&lt;p&gt;Verify payment completion on the server side before granting access. Check for a valid transaction ID or payment status in the database before updating membership.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /upgrade/confirmed.php (fixed)&lt;/span&gt;

&lt;span class="nv"&gt;$user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SESSION&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Check for a completed payment transaction using a prepared statement&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"SELECT id FROM transactions WHERE user_id = ? AND status = 'completed' ORDER BY created_at DESC LIMIT 1"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get_result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fetch_assoc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;http_response_code&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Payment required."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Only grant Pro status after verifying payment&lt;/span&gt;
&lt;span class="nv"&gt;$update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"UPDATE users SET membership = 'pro' WHERE id = ?"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$update&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bind_param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$update&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Congratulations! You are now a Pro member."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fixed version checks for a completed transaction before updating membership. Direct requests without payment now return a 403 error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Business logic flaws are hard to catch with automated scanners because they require understanding what the application is supposed to do. The attacker does not break the code, they just use it in an unintended order. Always validate state on the server side for every critical action, not just at the final step. Test your systems by trying to skip steps, replay requests out of order, or change values between steps to find these gaps before attackers do.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>tutorial</category>
      <category>todayilearned</category>
      <category>security</category>
    </item>
    <item>
      <title>User Enumeration: How One Leaky Error Message Lets Attackers Find Valid Usernames and Crack Your Passwords</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Sat, 16 May 2026 11:10:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-to-brute-force-usernames-and-passwords-597h</link>
      <guid>https://dev.to/jer_catallo/how-to-brute-force-usernames-and-passwords-597h</guid>
      <description>&lt;p&gt;Username enumeration and password brute-force are two of the most common techniques attackers use against web applications. They work best when a site gives away too much information through its error messages. This blog walks you through how an attacker can use a tool like &lt;code&gt;ffuf&lt;/code&gt; to discover valid usernames from a signup form, then crack the matching password with a common wordlist. Along the way, you will see the commands, the output, and what fixes to apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;These techniques are for educational purposes only. All tests were performed in a controlled lab environment with explicit permission. Unauthorized use of these methods against real systems is illegal. Always get written authorization before testing any system you do not own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Enumerate Valid Usernames
&lt;/h3&gt;

&lt;p&gt;The signup form returns a different error message when a username already exists. You can use this difference to find valid accounts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/SecLists/Usernames/Names/names.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=FUZZ&amp;amp;email=x&amp;amp;password=x&amp;amp;cpassword=x"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/customers/signup &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mr&lt;/span&gt; &lt;span class="s2"&gt;"username already exists"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command sends POST requests to the signup endpoint. The &lt;code&gt;FUZZ&lt;/code&gt; keyword is replaced with each name from the wordlist. The &lt;code&gt;-mr&lt;/code&gt; flag matches responses that contain "username already exists", which confirms the username is taken.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3kmyblm791hg24jv9bx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3kmyblm791hg24jv9bx.png" alt=" " width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four valid usernames were found: &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;robert&lt;/code&gt;, &lt;code&gt;simon&lt;/code&gt;, and &lt;code&gt;steve&lt;/code&gt;. Each returned HTTP 200 with the matching error string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Use a generic error message like "If the username is available, the account will be created" for both success and duplicate cases. This prevents attackers from distinguishing valid accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create Username List
&lt;/h3&gt;

&lt;p&gt;Save the confirmed usernames into a file for the next phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano valid_usernames.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;File contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;admin
robert
simon
steve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frodf9mi6t572dgjfqq0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frodf9mi6t572dgjfqq0c.png" alt=" " width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The file &lt;code&gt;valid_usernames.txt&lt;/code&gt; now contains the four confirmed usernames. This file is used as the first wordlist in the credential brute-force step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Brute-Force Username and Password
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;ffuf&lt;/code&gt; in cluster bomb mode to test every username and password combination. Failed logins return HTTP 200, while successful logins return a redirect (302).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; valid_usernames.txt:W1,&lt;span class="se"&gt;\&lt;/span&gt;
/usr/share/wordlists/SecLists/Passwords/Common-Credentials/10-million-password-list-top-100.txt:W2 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=W1&amp;amp;password=W2"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/customers/login &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-fc&lt;/span&gt; 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command uses two wordlists. &lt;code&gt;W1&lt;/code&gt; is the valid username list. &lt;code&gt;W2&lt;/code&gt; is the top-100 common passwords. The &lt;code&gt;-fc 200&lt;/code&gt; flag filters out HTTP 200 responses, so only successful logins (non-200 status codes) are shown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9wovzvejdsl62s7waca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9wovzvejdsl62s7waca.png" alt=" " width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One valid credential pair was found: username &lt;code&gt;steve&lt;/code&gt; with password &lt;code&gt;thunder&lt;/code&gt;. The response was HTTP 302, which indicates a redirect to the authenticated dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Add rate limiting, account lockout after failed attempts, and CAPTCHA challenges. Use generic login error messages that do not reveal whether the username or password was incorrect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;These two attacks chain together well: the signup form leaks which usernames exist, and the login form has no rate limiting to stop repeated attempts. Combined, they let an attacker go from zero knowledge to a valid session in minutes using only free, open-source tools and a common wordlist.&lt;/p&gt;

&lt;p&gt;The fixes are straightforward. Use generic error messages that do not reveal whether a username is taken or a password is wrong. Add rate limiting and account lockout on both the signup and login endpoints. Enforce a strong password policy so that common passwords like &lt;code&gt;thunder&lt;/code&gt; are rejected at registration. These controls break each step of the attack chain and make automated tools far less effective.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>tutorial</category>
      <category>todayilearned</category>
      <category>security</category>
    </item>
    <item>
      <title>Automated Web Content Discovery: How Attackers Find Hidden Paths on Your Web Server in Minutes Using Free Tools</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Fri, 08 May 2026 12:08:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-to-find-hidden-web-directories-and-sensitive-files-using-automated-discovery-44od</link>
      <guid>https://dev.to/jer_catallo/how-to-find-hidden-web-directories-and-sensitive-files-using-automated-discovery-44od</guid>
      <description>&lt;p&gt;Web applications often have directories and files that are not linked from the main pages. These paths can expose admin panels, backup files, logs, and config data. Automated content discovery tools like Gobuster use wordlists to test hundreds or thousands of paths quickly, and finding these before attackers do is a key part of web application security testing.&lt;/p&gt;

&lt;p&gt;Using the Acme IT Support practice target on TryHackMe, you can see exactly how an attacker builds up knowledge of a target step by step, starting from a small fast scan and moving to deeper coverage with file extension checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Only scan systems you own or have written permission to test.&lt;/li&gt;
&lt;li&gt;Set clear scope limits before scanning, including target hosts, paths, time windows, and allowed methods.&lt;/li&gt;
&lt;li&gt;Start with safe scan settings to avoid breaking services.&lt;/li&gt;
&lt;li&gt;Handle found data with care. Do not take, share, or publish sensitive content from logs, backups, or archives.&lt;/li&gt;
&lt;li&gt;Remove IPs, tokens, credentials, usernames, and other sensitive details before sharing findings publicly.&lt;/li&gt;
&lt;li&gt;Report high-risk findings through proper disclosure channels.&lt;/li&gt;
&lt;li&gt;Follow all applicable laws, platform rules, and company policies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Run a Baseline Scan with a Small Wordlist
&lt;/h2&gt;

&lt;p&gt;You can start with a small and fast wordlist to find common directories and files. This gives you quick results without waiting too long.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gobuster &lt;span class="nb"&gt;dir&lt;/span&gt; &lt;span class="nt"&gt;--url&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dir&lt;/code&gt; mode looks for directories. The &lt;code&gt;--url&lt;/code&gt; flag sets the target. The &lt;code&gt;-w&lt;/code&gt; flag points to the wordlist file. Gobuster uses 10 threads by default and treats 404 responses as negative results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzlh6oh2h4sqwmsc7v54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzlh6oh2h4sqwmsc7v54.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scan found 9 paths:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/assets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Redirect, static resources directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/contact&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Contact page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/customers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;302&lt;/td&gt;
&lt;td&gt;Redirect, possible user area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/development.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Sensitive, exposed development log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/monthly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Monthly content endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/news&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;News section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Redirect, restricted area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/robots.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Crawler exclusion file, useful for recon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/sitemap.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Sitemap, reveals additional paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Finding &lt;code&gt;/development.log&lt;/code&gt; at status 200 shows a high-risk misconfiguration. Development logs can contain stack traces, database queries, and sometimes credentials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Remove development logs from production servers. Use proper logging systems that store logs outside the web root. Add access controls if logs must be kept on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Expand Coverage with a Larger Wordlist
&lt;/h2&gt;

&lt;p&gt;You can use a bigger wordlist to find less common paths. Adding more threads and filtering noise makes the scan faster and the output cleaner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gobuster &lt;span class="nb"&gt;dir&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/dirb/big.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; 50 &lt;span class="nt"&gt;-b&lt;/span&gt; 404,403 &lt;span class="nt"&gt;--no-error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-u&lt;/code&gt; flag sets the target URL. The &lt;code&gt;-w&lt;/code&gt; flag points to the larger dirb wordlist with over 20,000 entries. The &lt;code&gt;-t 50&lt;/code&gt; flag increases threads for faster scanning. The &lt;code&gt;-b 404,403&lt;/code&gt; flag hides not-found and forbidden responses. The &lt;code&gt;--no-error&lt;/code&gt; flag removes error output for cleaner results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftu2iv8o4ty280lvvy4ek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftu2iv8o4ty280lvvy4ek.png" alt=" " width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This scan found 2 new paths not seen in Step 1:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/cookie-test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;New, cookie testing endpoint exposed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/sitemap_xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;New, alternate sitemap path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;-b 404,403&lt;/code&gt; removes noise and shows only useful results. Setting threads to 50 makes the scan much faster on stable targets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Remove internal test endpoints like &lt;code&gt;/cookie-test&lt;/code&gt; from production. Use a single sitemap path and redirect alternates to avoid confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Deep Scan with File Extension Checking
&lt;/h2&gt;

&lt;p&gt;You can add file extension checking to find backup files, config files, and other sensitive file types. This multiplies your test cases and gives wider coverage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gobuster &lt;span class="nb"&gt;dir&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; http://&amp;lt;target-ip&amp;gt;/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-x&lt;/span&gt; txt,json,bak,zip,md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-x&lt;/code&gt; flag adds each extension to every wordlist entry. For example, &lt;code&gt;backup&lt;/code&gt; becomes &lt;code&gt;backup.txt&lt;/code&gt;, &lt;code&gt;backup.json&lt;/code&gt;, &lt;code&gt;backup.bak&lt;/code&gt;, and so on. This helps you find files that directory-only scans will miss.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipbtptnp3qe7i9bdzd28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipbtptnp3qe7i9bdzd28.png" alt=" " width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This scan found 1 critical path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/tmp.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Critical, archive file exposed on web root&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Adding &lt;code&gt;-x&lt;/code&gt; tests each wordlist entry with every extension, which gives much wider coverage. Finding &lt;code&gt;/tmp.zip&lt;/code&gt; shows why extension scanning is important. Backup and temp files left in web-accessible paths are a common issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Remove all backup and archive files from the web root. Use deployment scripts that clean up temp files. Store backups in secure locations outside the web server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Findings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/development.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;High, may contain credentials or stack traces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/tmp.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;High, archive with unknown contents exposed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301&lt;/td&gt;
&lt;td&gt;Medium, restricted area worth investigating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/customers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;302&lt;/td&gt;
&lt;td&gt;Medium, potential user data area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/cookie-test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Low-Medium, exposes internal test endpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/robots.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Informational, reveals disallowed paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/sitemap.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;Informational, additional path disclosure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Small wordlists are fast but miss many paths. You should layer scans with bigger wordlists to get better coverage.&lt;/li&gt;
&lt;li&gt;File extension scanning with &lt;code&gt;-x&lt;/code&gt; is needed to find backup files like &lt;code&gt;.bak&lt;/code&gt; and &lt;code&gt;.zip&lt;/code&gt;, and config leaks.&lt;/li&gt;
&lt;li&gt;Filtering noise with &lt;code&gt;-b&lt;/code&gt; for block status codes gives cleaner output for faster review.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;200&lt;/code&gt; response means the content is accessible. &lt;code&gt;301&lt;/code&gt; and &lt;code&gt;302&lt;/code&gt; mean redirects worth following. Even &lt;code&gt;403&lt;/code&gt; confirms a path exists.&lt;/li&gt;
&lt;li&gt;Exposed files like &lt;code&gt;development.log&lt;/code&gt; and &lt;code&gt;tmp.zip&lt;/code&gt; are real-world issues you will often see in penetration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use these steps on your own assessments to find hidden content and improve your security posture. Always stay within scope and handle any sensitive data you find with care.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>tutorial</category>
      <category>todayilearned</category>
      <category>security</category>
    </item>
    <item>
      <title>OSINT Content Discovery: Why You Need to Know What's Publicly Exposed About Your Web Assets</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Thu, 07 May 2026 12:15:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-to-find-hidden-web-contents-using-passive-osint-techniques-3kf1</link>
      <guid>https://dev.to/jer_catallo/how-to-find-hidden-web-contents-using-passive-osint-techniques-3kf1</guid>
      <description>&lt;p&gt;Passive content discovery helps you map attack surfaces without touching target systems. You can use public search engines, browser extensions, web archives, code repositories, and cloud storage references to find exposed assets. This guide covers five methods you can apply in your own authorized security assessments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;Only use these methods on assets you own or have clear written permission to test.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get written permission before you target any domain, repo, or cloud resource.&lt;/li&gt;
&lt;li&gt;Follow all laws, platform terms, and bug bounty scope rules.&lt;/li&gt;
&lt;li&gt;Do not try to access accounts, use found credentials, steal data, or leave backdoors.&lt;/li&gt;
&lt;li&gt;Stop and report right away if you find sensitive data.&lt;/li&gt;
&lt;li&gt;Do not proceed if you are not sure about your authorization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Google Dorking
&lt;/h2&gt;

&lt;p&gt;Google search operators let you filter results to specific domains, file types, URL paths, and page titles. These operators are passive and use only public indexed data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Use &lt;code&gt;site:&lt;/code&gt; to Scope Your Search
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;site:&lt;/code&gt; operator limits results to one domain or hosting platform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site:&amp;lt;target-domain&amp;gt; "&amp;lt;keyword&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query shows only pages from the target domain that contain your keyword. You can use it to find public pages hosted on a specific platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtdpl1it6ckfwdnf7f68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtdpl1it6ckfwdnf7f68.png" alt=" " width="800" height="1191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see indexed GitHub Pages sites that match the keyword. This shows how &lt;code&gt;site:&lt;/code&gt; limits search to one hosting domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Use &lt;code&gt;filetype:&lt;/code&gt; to Find Exposed Documents
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;filetype:&lt;/code&gt; operator filters results by file extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"&amp;lt;target-phrase&amp;gt;" filetype:&amp;lt;extension&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query finds indexed files of a specific type that contain your target phrase. You can use it to map exposed documents and artifacts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13i2tw7h8meeibthqi7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13i2tw7h8meeibthqi7v.png" alt=" " width="800" height="1255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see public Jupyter notebooks that may hold code, data samples, or analysis work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Treat found documents as sensitive even if they are public. Do not copy or share private content. Report exposure through approved channels only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Use &lt;code&gt;inurl:&lt;/code&gt; for Path-Based Discovery
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;inurl:&lt;/code&gt; operator targets pages with specific words in the URL path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inurl:&amp;lt;path-keyword&amp;gt; "&amp;lt;target-phrase&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query finds pages with your keyword in the URL path. You can use it to find specific page types.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6dhdat20hyn0yrczo43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs6dhdat20hyn0yrczo43.png" alt=" " width="800" height="1107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see personal or professional about pages that give more context about the target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Avoid personal targeting, doxxing, or profiling. Collect only the data you need for your security task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Use &lt;code&gt;intitle:&lt;/code&gt; for Title-Based Discovery
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;intitle:&lt;/code&gt; operator matches pages with specific text in the HTML title tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;intitle:"&amp;lt;title-text&amp;gt;" "&amp;lt;keyword1&amp;gt;" "&amp;lt;keyword2&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query finds pages with your text in the title plus extra keywords. You can use it to find project pages tied to certain technologies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk116nxfvl7bt0cptp3hi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk116nxfvl7bt0cptp3hi.png" alt=" " width="800" height="1160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see developer portfolio pages that list their technology stack in the page title.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Keep searches within approved scope. Do not use findings to target hobby or student projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wappalyzer Technology Fingerprinting
&lt;/h2&gt;

&lt;p&gt;Wappalyzer detects web technologies from the browser. It reads HTTP headers, HTML, JavaScript files, and loaded resources to identify frameworks, CDNs, and services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Fingerprint OWASP Juice Shop Stack
&lt;/h3&gt;

&lt;p&gt;Open the target URL in a browser with Wappalyzer installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://juice-shop.github.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extension scans the page and shows detected technologies in its panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh8w4v5x4bv91jy8o5pv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh8w4v5x4bv91jy8o5pv.png" alt=" " width="800" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wappalyzer found front-end libraries, CDN providers, and hosting indicators on &lt;code&gt;juice-shop.github.io&lt;/code&gt;. You can use this stack data to plan your next assessment steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Only fingerprint where recon is allowed. Do not assume you can attack just because you see stack details. Use this data for defensive testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Analyze GitHub Technology Profile
&lt;/h3&gt;

&lt;p&gt;Apply Wappalyzer to large platforms to see their technology footprint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extension finds frameworks, analytics tools, CDN providers, and cloud services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfrmnegdj1kq5d6855tg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flfrmnegdj1kq5d6855tg.png" alt=" " width="800" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wappalyzer found React, React Router, GSAP, AWS-related services, and more on &lt;code&gt;github.com&lt;/code&gt;. This shows how fingerprinting works on large applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Follow platform terms and rate limits. Do not scrape data in an abusive way. Use collected data only for authorized tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wayback Machine Archive Analysis
&lt;/h2&gt;

&lt;p&gt;The Wayback Machine stores historical snapshots of web pages. You can use it to find old URLs, retired endpoints, and content versions no longer on the live site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Search for Historical Snapshots
&lt;/h3&gt;

&lt;p&gt;Enter the target domain into the Wayback Machine search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://web.archive.org/web/*/&amp;lt;target-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse the calendar timeline to see archived snapshots from different dates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpemit6u1bdcg4xgr8vks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpemit6u1bdcg4xgr8vks.png" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use this as your entry point for historical content analysis. You can find old URLs, retired endpoints, and content versions that are no longer on the live site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Just because content is archived does not mean you can test current systems. Do not use archived findings to access restricted areas without approval. Check ownership and scope before you test any found endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub OSINT
&lt;/h2&gt;

&lt;p&gt;GitHub search helps you find code references, config files, and metadata. Public repos often contain clues about infrastructure, dependencies, and potential misconfigurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Search GitHub for Target Artifacts
&lt;/h3&gt;

&lt;p&gt;Use the GitHub search page with targeted queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/search?q=&amp;lt;target-query&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find repos, code snippets, and config files in your assessment scope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6lmxc6imgfwxnu0wpk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6lmxc6imgfwxnu0wpk3.png" alt=" " width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Use GitHub Dork Patterns for Credential Discovery
&lt;/h3&gt;

&lt;p&gt;Organization-scoped searches limit results to one company's public repos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org:&amp;lt;company-name&amp;gt; &amp;lt;secret-keyword&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add keywords like "password", "token", "api_key", or "secret" to check for credential exposure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;=========================================================&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;GITHUB&lt;/span&gt; &lt;span class="nx"&gt;OSINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HIGH&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;VALUE&lt;/span&gt; &lt;span class="nx"&gt;TARGET&lt;/span&gt; &lt;span class="nx"&gt;DORKS&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;=========================================================&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="nx"&gt;Cloud&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Infrastructure&lt;/span&gt; &lt;span class="nx"&gt;Secrets&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Searches&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt; &lt;span class="nx"&gt;IDs&lt;/span&gt; &lt;span class="nx"&gt;within&lt;/span&gt; &lt;span class="nx"&gt;PEM&lt;/span&gt; &lt;span class="nx"&gt;certificate&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AKIA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;pem&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Locates&lt;/span&gt; &lt;span class="nx"&gt;exposed&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="nx"&gt;credential&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;unprotected&lt;/span&gt; &lt;span class="nx"&gt;SSH&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BEGIN OPENSSH PRIVATE KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;id_rsa&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Discovers&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt; &lt;span class="nx"&gt;Cloud&lt;/span&gt; &lt;span class="nc"&gt;Platform &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GCP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;
&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google_application_credentials&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="nx"&gt;Database&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Authentication&lt;/span&gt; &lt;span class="nx"&gt;Leaks&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;hardcoded&lt;/span&gt; &lt;span class="nx"&gt;MongoDB&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;JavaScript&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mongodb+srv://&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Searches&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;Java&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;MySQL&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;passwords&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jdbc:mysql://&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Locates&lt;/span&gt; &lt;span class="nx"&gt;WordPress&lt;/span&gt; &lt;span class="nx"&gt;configuration&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="nx"&gt;containing&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="nx"&gt;passwords&lt;/span&gt;
&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;wp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;PostgreSQL&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="nx"&gt;instances&lt;/span&gt;
&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:.&lt;/span&gt;&lt;span class="nx"&gt;pgpass&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost:5432&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="nx"&gt;Keys&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Tokens&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Hunts&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;hardcoded&lt;/span&gt; &lt;span class="nx"&gt;Bearer&lt;/span&gt; &lt;span class="nx"&gt;authentication&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;Python&lt;/span&gt; &lt;span class="nx"&gt;scripts&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization: bearer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Locates&lt;/span&gt; &lt;span class="nx"&gt;exposed&lt;/span&gt; &lt;span class="nx"&gt;Django&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt; &lt;span class="nx"&gt;web&lt;/span&gt; &lt;span class="nx"&gt;framework&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;
&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SECRET_KEY=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;live&lt;/span&gt; &lt;span class="nx"&gt;Stripe&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="nx"&gt;processing&lt;/span&gt; &lt;span class="nx"&gt;API&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api.stripe.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sk_live_&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Discovers&lt;/span&gt; &lt;span class="nx"&gt;exposed&lt;/span&gt; &lt;span class="nx"&gt;Slack&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt; &lt;span class="nx"&gt;URLs&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hooks.slack.com/services/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="nx"&gt;Targeted&lt;/span&gt; &lt;span class="nx"&gt;Corporate&lt;/span&gt; &lt;span class="nx"&gt;Recon&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Replace&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;companyname&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Searches&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;specific&lt;/span&gt; &lt;span class="nx"&gt;organization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s repos for internal Jira passwords
org:companyname "jira_password"

# Finds Atlassian/Confluence access tokens for a specific target domain
"companyname.atlassian.net" "token"

# Locates terminal history files showing SSH connections to a target
filename:.bash_history "ssh user@companyname"

# Discovers internal corporate network routing or configuration files
"corp.companyname.internal" extension:conf
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see high-value search patterns for cloud credentials, database leaks, and token discovery. The sheet includes org-scoped searches like &lt;code&gt;org:companyname&lt;/code&gt; for focused recon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Only use this in authorized training, internal audits, or approved bug bounty scopes. Never use found secrets or credentials. Report exposed credentials through approved incident channels right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  S3 Bucket Discovery
&lt;/h2&gt;

&lt;p&gt;Amazon S3 buckets often show up in public references through naming patterns, source code, and config files. You can find them using search operators and verify access with AWS CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 10: Find S3 Buckets Through Public References
&lt;/h3&gt;

&lt;p&gt;Search for public S3 bucket references with Google dorking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site:s3.amazonaws.com "&amp;lt;target-company&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also search GitHub for bucket names in source code and config files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 11: Check Bucket Access with AWS CLI
&lt;/h3&gt;

&lt;p&gt;Use the AWS CLI to check if a bucket allows public listing without credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;ls &lt;/span&gt;s3://&amp;lt;bucket-name&amp;gt; &lt;span class="nt"&gt;--no-sign-request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get the bucket ACL to see access permissions. A successful response means the bucket allows anonymous access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api get-bucket-acl &lt;span class="nt"&gt;--bucket&lt;/span&gt; &amp;lt;bucket-name&amp;gt; &lt;span class="nt"&gt;--no-sign-request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;=========================================================&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;S3&lt;/span&gt; &lt;span class="nx"&gt;BUCKET&lt;/span&gt; &lt;span class="nx"&gt;OSINT&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;RECONNAISSANCE&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;=========================================================&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Passive&lt;/span&gt; &lt;span class="nc"&gt;Discovery &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Google&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;GitHub&lt;/span&gt; &lt;span class="nx"&gt;Dorks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt; &lt;span class="nx"&gt;Dork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;publicly&lt;/span&gt; &lt;span class="nx"&gt;indexed&lt;/span&gt; &lt;span class="nx"&gt;S3&lt;/span&gt; &lt;span class="nx"&gt;buckets&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;
&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amazonaws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt; &lt;span class="nx"&gt;intitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index of&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;companyname&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt; &lt;span class="nx"&gt;Dork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Searches&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;exposed&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="nx"&gt;URLs&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s documents
"s3.amazonaws.com" ext:pdf "companyname"

# GitHub Dork: Locates bucket URLs hardcoded in a company&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;repositories&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s3.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;org&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;companyname&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;GitHub&lt;/span&gt; &lt;span class="nx"&gt;Dork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Finds&lt;/span&gt; &lt;span class="nx"&gt;custom&lt;/span&gt; &lt;span class="nx"&gt;S3&lt;/span&gt; &lt;span class="nx"&gt;endpoints&lt;/span&gt; &lt;span class="nx"&gt;mapped&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;companyname.s3.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Active&lt;/span&gt; &lt;span class="nc"&gt;Enumeration &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Brute&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Force&lt;/span&gt; &lt;span class="nx"&gt;Naming&lt;/span&gt; &lt;span class="nx"&gt;Conventions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Common&lt;/span&gt; &lt;span class="nx"&gt;permutations&lt;/span&gt; &lt;span class="nx"&gt;used&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;automated&lt;/span&gt; &lt;span class="nf"&gt;tools &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; &lt;span class="nx"&gt;ffuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Gobuster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//{target}-{keyword}.s3.amazonaws.com&lt;/span&gt;

&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;backup&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;staging&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;prod&lt;/span&gt;
&lt;span class="nx"&gt;companyname&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;www&lt;/span&gt;


&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Access&lt;/span&gt; &lt;span class="nc"&gt;Verification &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="nx"&gt;CLI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Testing&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;insecure&lt;/span&gt; &lt;span class="nf"&gt;permissions &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Requires&lt;/span&gt; &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="nx"&gt;CLI&lt;/span&gt; &lt;span class="nx"&gt;installed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Attempt&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;contents&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="nf"&gt;anonymously &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="nx"&gt;ls&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//companyname-assets --no-sign-request&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Attempt&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;
&lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="nx"&gt;cp&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//companyname-backup/db_dump.sql . --no-sign-request&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Attempt&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;write&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;harmless&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;insecure&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;
&lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="nx"&gt;cp&lt;/span&gt; &lt;span class="nx"&gt;test_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//companyname-public/ --no-sign-request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see passive discovery patterns for S3 references using Google and GitHub queries. The image includes naming permutation examples and CLI commands to check bucket permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt; Cloud enumeration needs explicit permission from the asset owner. Do not list, download, upload, or change bucket content unless you have written authorization. If you find an exposed bucket, stop testing and report it with minimal proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You now have five passive content discovery methods you can use in authorized assessments. Google dorking helps you map indexed content with targeted operators. Wappalyzer gives fast technology stack details. The Wayback Machine reveals historical web data and retired endpoints. GitHub OSINT uncovers code references and config metadata. S3 recon shows cloud storage discovery patterns. All methods are passive and you should only use them within authorized scopes with clear permission.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>tutorial</category>
      <category>todayilearned</category>
      <category>security</category>
    </item>
    <item>
      <title>Manual Web Content Discovery: How You Can Find Hidden Paths Before Attackers Do</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Mon, 04 May 2026 12:05:00 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-to-find-hidden-web-content-using-manual-reconnaissance-5bm</link>
      <guid>https://dev.to/jer_catallo/how-to-find-hidden-web-content-using-manual-reconnaissance-5bm</guid>
      <description>&lt;p&gt;Manual content discovery is a core skill in application security testing. Instead of relying only on automated scanners, you can use simple HTTP requests and browser tools to find exposed files, hidden paths, and technology fingerprints. This covers techniques like checking &lt;code&gt;robots.txt&lt;/code&gt;, fingerprinting favicons, reading &lt;code&gt;sitemap.xml&lt;/code&gt;, inspecting HTTP headers, and spotting framework markers in HTML source.&lt;/p&gt;

&lt;p&gt;These methods help you understand a target's structure and find information disclosure issues early, before running heavy scanning tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ethical Considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only test systems you own or have explicit written permission to assess.&lt;/li&gt;
&lt;li&gt;Follow the defined scope, timing, and rules of engagement set by the owner.&lt;/li&gt;
&lt;li&gt;Stop immediately if you find data outside scope and report it through approved channels.&lt;/li&gt;
&lt;li&gt;Use findings for defense and remediation, not exploitation.&lt;/li&gt;
&lt;li&gt;Treat discovered paths like admin or staff portals as sensitive data. Do not brute-force or abuse them.&lt;/li&gt;
&lt;li&gt;Do not publish sensitive headers, tokens, or internal values outside approved reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Robots.txt Analysis
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;robots.txt&lt;/code&gt; file tells web crawlers which paths to avoid. It can accidentally reveal sensitive routes like admin panels or staff portals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://&amp;lt;target-domain&amp;gt;/robots.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command fetches the robots.txt file so you can check &lt;code&gt;Disallow&lt;/code&gt; and &lt;code&gt;Allow&lt;/code&gt; directives for hidden paths.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyws6ueo9s63wtusulc3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyws6ueo9s63wtusulc3n.png" alt=" " width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response shows a &lt;code&gt;Disallow: /staff-portal&lt;/code&gt; directive under &lt;code&gt;User-agent: *&lt;/code&gt;. This means the site owner does not want crawlers to index the staff portal, but the path is still visible to anyone who checks this file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The &lt;code&gt;/staff-portal&lt;/code&gt; route is exposed through robots.txt. While this does not mean the path is vulnerable, it gives you a starting point for further authorized testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Remove sensitive paths from &lt;code&gt;robots.txt&lt;/code&gt;. Use proper authentication and authorization controls to protect those routes instead. Security through obscurity is not a reliable protection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Favicon Fingerprinting
&lt;/h3&gt;

&lt;p&gt;Favicons are small icons that browsers display in tabs. Different frameworks and products use unique favicon files, so you can calculate a hash and match it against known databases to identify the technology.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://&amp;lt;target-domain&amp;gt;/favicon.ico | &lt;span class="nb"&gt;md5sum&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads the favicon and calculates its MD5 hash for comparison.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskpvahzuvet828lv7gym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskpvahzuvet828lv7gym.png" alt=" " width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser network tab confirms a successful HTTP 200 response for &lt;code&gt;favicon.ico&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09m4a6mtifse579al0oo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F09m4a6mtifse579al0oo.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The calculated MD5 hash is &lt;code&gt;f276b19aabcb4ae8cda4d22625c6735f&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2zwqwgmkhq6tn16j6wu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2zwqwgmkhq6tn16j6wu.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Searching this hash in the OWASP favicon database returns a match for &lt;code&gt;cgiirc (0.5.9)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The favicon hash maps to &lt;code&gt;cgiirc (0.5.9)&lt;/code&gt;, an IRC web client. This suggests the target may reuse assets from this product or run it in the background. You can use this information to check for known issues with this version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Replace default framework or third-party favicons with a custom one. This prevents passive technology identification through favicon hashing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sitemap.xml Enumeration
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;sitemap.xml&lt;/code&gt; file lists pages that the site wants search engines to index. It often reveals old routes, API endpoints, or parameterized URLs you might not find through normal browsing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://&amp;lt;target-domain&amp;gt;/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This retrieves the sitemap to find discoverable paths and endpoints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cr3vf2jlwnq9y4h1iwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cr3vf2jlwnq9y4h1iwv.png" alt=" " width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sitemap contains multiple URL entries including &lt;code&gt;/news/&lt;/code&gt;, &lt;code&gt;/contact&lt;/code&gt;, and parameterized article paths with sequential IDs like &lt;code&gt;news/article?id=1&lt;/code&gt;, &lt;code&gt;news/article?id=2&lt;/code&gt;, and &lt;code&gt;news/article?id=3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The sitemap exposes several routes and a pattern for article IDs. You can use this to map out the content structure and check for IDOR or other parameter-based issues on these endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Avoid listing sensitive or internal endpoints in &lt;code&gt;sitemap.xml&lt;/code&gt;. Only include public-facing, intended content. For parameterized URLs, validate and authorize each request server-side.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Header Inspection
&lt;/h3&gt;

&lt;p&gt;HTTP response headers contain metadata about the server, security configuration, and sometimes version information. Missing security headers or verbose server details can reveal weaknesses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://&amp;lt;target-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a HEAD request to get only the response headers without the full page body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpuxs14wl9e39fcj597t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpuxs14wl9e39fcj597t.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The headers show &lt;code&gt;Server: nginx/1.18.0 (Ubuntu)&lt;/code&gt; and a custom &lt;code&gt;X-FLAG: THM{HEADER_FLAG}&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The &lt;code&gt;Server&lt;/code&gt; header leaks the exact web server version and operating system. This helps you narrow down potential version-specific issues. The response also lacks important security headers like &lt;code&gt;Content-Security-Policy&lt;/code&gt; and &lt;code&gt;Strict-Transport-Security&lt;/code&gt;, which means the site may be vulnerable to clickjacking or downgrade attacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Configure your web server to suppress or mask the &lt;code&gt;Server&lt;/code&gt; header. Add security headers like &lt;code&gt;Content-Security-Policy&lt;/code&gt;, &lt;code&gt;Strict-Transport-Security&lt;/code&gt;, &lt;code&gt;X-Frame-Options&lt;/code&gt;, and &lt;code&gt;X-Content-Type-Options&lt;/code&gt;. You can use tools like &lt;a href="https://securityheaders.com" rel="noopener noreferrer"&gt;securityheaders.com&lt;/a&gt; to check your current header posture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework Stack Identification
&lt;/h3&gt;

&lt;p&gt;Web frameworks often leave markers in HTML source code, such as generator comments or meta tags. These markers reveal the technology stack and sometimes the exact version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://&amp;lt;target-domain&amp;gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"generated&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;framework"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fetches the homepage HTML and filters for framework-related comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrqr66g9fbfjrc4u3gno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrqr66g9fbfjrc4u3gno.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The HTML source contains a comment showing the page was generated using the THM Framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef870cqn6ga9wa4zdoxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fef870cqn6ga9wa4zdoxr.png" alt=" " width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visiting the framework reference URL confirms it is the THM Web Framework with visible version details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;: The source comment reveals &lt;code&gt;THM Framework v1.2&lt;/code&gt; as the underlying technology. You can now research this framework for known misconfigurations, default paths, or version-specific vulnerabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation&lt;/strong&gt;: Strip generator comments and version markers from production HTML before deployment. Configure your build pipeline or template engine to exclude debug and version metadata from rendered output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Manual content discovery gives you a clear picture of a target without heavy tooling. You can see how &lt;code&gt;robots.txt&lt;/code&gt; can leak sensitive paths, favicon hashes can identify technologies, &lt;code&gt;sitemap.xml&lt;/code&gt; can map out hidden routes, HTTP headers can expose server versions and missing security controls, and HTML source comments can reveal framework details. These techniques work well as a first step before running automated scanners and help build a stronger picture of the target's attack surface.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Subdomain Enumeration: How Attackers Find What You Forgot to Hide</title>
      <dc:creator>Jer Catallo</dc:creator>
      <pubDate>Sat, 02 May 2026 11:41:50 +0000</pubDate>
      <link>https://dev.to/jer_catallo/how-to-find-hidden-subdomains-from-passive-osint-to-active-brute-force-hf2</link>
      <guid>https://dev.to/jer_catallo/how-to-find-hidden-subdomains-from-passive-osint-to-active-brute-force-hf2</guid>
      <description>&lt;p&gt;Subdomain enumeration is the process of finding all subdomains that belong to a target domain. Each subdomain is a potential entry point, making this a key step in external reconnaissance. In this write-up, we walk through the subdomain enumeration techniques tested in a hands-on lab, so you can see the tools, commands, and results along the way.&lt;/p&gt;

&lt;p&gt;There are two main approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Passive enumeration&lt;/strong&gt;: Uses public data sources like search engines, certificate transparency logs, and third-party APIs. This method does not send direct requests to the target, so it has low risk of detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active enumeration&lt;/strong&gt;: Sends direct requests to DNS servers or web servers using wordlists. This method finds more results but creates network traffic that can be logged or detected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We demonstrate both approaches below, so you can see how they complement each other and why relying on only one method leaves blind spots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethical Considerations
&lt;/h2&gt;

&lt;p&gt;Subdomain enumeration is a reconnaissance activity. It has legal and operational impact. You should follow these rules in every engagement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization is mandatory. Active DNS brute forcing or host-header fuzzing without written permission can break laws such as CFAA and local statutes.&lt;/li&gt;
&lt;li&gt;Passive does not always mean harmless. Passive OSINT can still show sensitive information, so handle it with care.&lt;/li&gt;
&lt;li&gt;Rate limiting matters. Use low thread counts (for example, &lt;code&gt;-t 1&lt;/code&gt;) and wait between requests to avoid service disruption.&lt;/li&gt;
&lt;li&gt;Scope validation is required. A discovered host is not automatically in scope. Always check every asset against the approved scope list.&lt;/li&gt;
&lt;li&gt;Responsible disclosure applies. Report unintended exposed infrastructure through proper authorized channels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All activity in this write-up ran against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Personal domain (&lt;code&gt;jercarlocatallo.com&lt;/code&gt;), owned by the author.&lt;/li&gt;
&lt;li&gt;TryHackMe lab environments, authorized training infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demonstration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Search Engine Dorking
&lt;/h3&gt;

&lt;p&gt;Search engine dorking is the simplest passive technique. By using the &lt;code&gt;site:&lt;/code&gt; operator, you can ask Google which subdomains it has indexed for a domain. No traffic reaches the target, and results are available in seconds.&lt;/p&gt;

&lt;p&gt;We use HackTheBox below so you can see how this technique works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HackTheBox query:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site:*.hackthebox.com -site:www.hackthebox.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mg801fh76p8zbezke0f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6mg801fh76p8zbezke0f.png" alt=" " width="800" height="1037"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Indexed results include subdomains such as &lt;code&gt;roadmap&lt;/code&gt;, &lt;code&gt;jobs&lt;/code&gt;, &lt;code&gt;ctf&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, and &lt;code&gt;trust&lt;/code&gt;. This confirms indexing-based visibility and helps you prioritize deeper checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Search engines know only what they have crawled. Results are quick to get but limited to indexed assets. This technique alone will miss subdomains that search engines never discovered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificate Transparency Lookup
&lt;/h3&gt;

&lt;p&gt;Certificate Transparency (CT) logs record every SSL/TLS certificate issued by participating authorities. crt.sh is a public search interface for these logs. Unlike search engines, CT logs can reveal hostnames that were never indexed or are no longer active.&lt;/p&gt;

&lt;p&gt;We check tryhackme.com below so you can see how CT logs compare to search engine results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tryhackme.com:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://crt.sh/?q=tryhackme.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fak5jkag22b8gnuvlzyeb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fak5jkag22b8gnuvlzyeb.png" alt=" " width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Certificate entries show wildcard and service-specific names. You can see that CT logs include historical names not currently indexed by search engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; CT logs provide deeper hostname visibility through certificate history. They often reveal names that search engines miss, including wildcard entries and expired certificates. However, CT logs cannot find subdomains that never had a certificate issued.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passive Aggregation with Sublist3r
&lt;/h3&gt;

&lt;p&gt;Sublist3r automates passive enumeration by querying multiple third-party sources (search engines, DNS aggregators, VirusTotal, and more) in a single run. We run it against a personal domain so you can see what public data sources know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sublist3r &lt;span class="nt"&gt;-d&lt;/span&gt; jercarlocatallo.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijhvx6ryzuatpvm1szkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijhvx6ryzuatpvm1szkl.png" alt=" " width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see 3 unique subdomains were discovered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;www.jercarlocatallo.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;m.jercarlocatallo.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wwww.jercarlocatallo.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that &lt;code&gt;wwww.jercarlocatallo.com&lt;/code&gt; (four w's) is a misspelled typo domain, which is interesting because it shows up in third-party data sources, possibly from a crawl or a misconfigured link somewhere.&lt;/p&gt;

&lt;p&gt;Two sources failed during collection: DNSDumpster (CSRF token error) and VirusTotal (blocking requests). Passive output quality depends on source availability and rate limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor certificate transparency logs for your domains to detect unauthorized subdomain creation.&lt;/li&gt;
&lt;li&gt;Remove unused or forgotten subdomains from DNS to reduce attack surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Sublist3r is efficient for gathering what third-party sources already know, but it cannot discover subdomains that no source has indexed. Infrastructure names like dev, admin, and vpn often do not appear in passive data because no one has publicly referenced them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Host Discovery with ffuf
&lt;/h3&gt;

&lt;p&gt;Not all subdomains resolve in public DNS. Some exist only as virtual hosts behind a single IP address. ffuf can find these by fuzzing the &lt;code&gt;Host&lt;/code&gt; header in HTTP requests and filtering out default responses.&lt;/p&gt;

&lt;p&gt;We run this in a TryHackMe lab environment so you can see the technique in action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffuf &lt;span class="nt"&gt;-w&lt;/span&gt; /usr/share/wordlists/SecLists/Discovery/DNS/namelist.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Host: FUZZ.acmeitsupport.thm"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; http://10.48.142.81 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-fs&lt;/span&gt; 2395
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek571w6imgmytrps2vmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek571w6imgmytrps2vmj.png" alt=" " width="799" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see two virtual hosts were found: &lt;code&gt;delta&lt;/code&gt; and &lt;code&gt;yellow&lt;/code&gt;. This confirms that HTTP-layer discovery can expose hosts not visible in public OSINT datasets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure web servers to return consistent responses for unknown Host headers.&lt;/li&gt;
&lt;li&gt;Use a default catch-all virtual host that does not leak information about other hosted services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Virtual host fuzzing reveals assets that DNS and passive sources cannot find. It is an active technique that requires sending real HTTP requests, so it is more detectable but more complete for web-hosted infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Active DNS Brute Force with Gobuster
&lt;/h3&gt;

&lt;p&gt;Gobuster sends direct DNS queries for each word in a wordlist, resolving subdomains that passive sources never collected. We run this against a personal domain to find subdomains that Sublist3r missed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gobuster dns &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; jercarlocatallo.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-w&lt;/span&gt; subdomains-top1million-5000.txt &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resolver&lt;/span&gt; 8.8.8.8 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--protocol&lt;/span&gt; tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard DNS typically uses UDP, which can drop packets under load. TCP reduces timeout noise and improves consistency for wordlist-based enumeration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdus9p85az6jiqejh8jpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdus9p85az6jiqejh8jpg.png" alt=" " width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see 8 subdomains resolved:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subdomain&lt;/th&gt;
&lt;th&gt;IP Resolved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;www.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;64.29.17.65, 216.198.79.65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mail.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dev.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;admin.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vpn.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;staging.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uat.jercarlocatallo.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;www&lt;/code&gt; returned two public IPs (64.29.17.65 and 216.198.79.65), which suggests a round-robin or load-balanced setup. Some words (autos, soap, chemie) produced i/o timeouts, which is expected resolver noise. The scan completed all 4,989 words. Loopback results indicate locally configured DNS entries that may map to non-public infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use split-horizon DNS to separate internal and external DNS records.&lt;/li&gt;
&lt;li&gt;Avoid exposing internal infrastructure names (dev, staging, uat, admin) in public DNS.&lt;/li&gt;
&lt;li&gt;Implement DNSSEC to prevent DNS spoofing and cache poisoning attacks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt; Active brute forcing found 8 subdomains where Sublist3r found only 3. The 5 additional names (mail, dev, admin, vpn, api, staging, uat) were not indexed by any passive source. As you can see, this gap shows why active enumeration is necessary for full coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Passive techniques show what public data sources know. Active techniques show what DNS and application behavior expose. When you combine both approaches, you improve attack surface visibility and reduce blind spots.&lt;/p&gt;

&lt;p&gt;Sublist3r found 3 publicly known subdomains from third-party data, including a typo domain (&lt;code&gt;wwww.jercarlocatallo.com&lt;/code&gt;). Gobuster found 8 subdomains through direct DNS resolution, adding infrastructure names like dev, admin, vpn, api, staging, uat, and mail that no passive source had indexed. That gap between passive and active results is why using both methods together matters.&lt;/p&gt;

&lt;p&gt;For defenders, the takeaway is clear: monitor certificate logs, clean up unused DNS records, and use split-horizon DNS to keep internal names out of public resolvers.&lt;/p&gt;




&lt;p&gt;If you found this helpful, drop a like and share it with someone learning security. If you have questions, ran into something different in your own lab, or want to share your results, leave a comment below. Always happy to connect and talk about security, recon techniques, or anything AppSec related.&lt;/p&gt;

&lt;p&gt;Feel free to connect with me on &lt;a href="https://www.linkedin.com/in/jer-catallo/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Always open to connecting with people in security, development, or both. Whether you are building something, breaking something, or just getting started, feel free to reach out.&lt;/p&gt;

</description>
      <category>security</category>
      <category>todayilearned</category>
      <category>tutorial</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
