<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Curious4AI</title>
    <description>The latest articles on DEV Community by Curious4AI (@curious4ai).</description>
    <link>https://dev.to/curious4ai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4156811%2F3ef6d8c3-909d-481e-b593-8840571a648b.png</url>
      <title>DEV Community: Curious4AI</title>
      <link>https://dev.to/curious4ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/curious4ai"/>
    <language>en</language>
    <item>
      <title>Finding a flag hidden in a .wav file with a spectrogram</title>
      <dc:creator>Curious4AI</dc:creator>
      <pubDate>Fri, 02 Oct 2026 09:44:57 +0000</pubDate>
      <link>https://dev.to/curious4ai/finding-a-flag-hidden-in-a-wav-file-with-a-spectrogram-44g8</link>
      <guid>https://dev.to/curious4ai/finding-a-flag-hidden-in-a-wav-file-with-a-spectrogram-44g8</guid>
      <description>&lt;p&gt;Today I worked through a beginner forensics CTF challenge. The description was one line: here is a .wav file, find the flag. The hint said to try tools like Audacity or Sonic and look at the spectrogram.&lt;/p&gt;

&lt;p&gt;I'll skip the flag so I don't spoil it for anyone. Here's what I did and what I got wrong first.&lt;/p&gt;

&lt;h2&gt;
  
  
  First look
&lt;/h2&gt;

&lt;p&gt;The file is 10 seconds, 44.1 kHz, stereo. Both channels turned out to be identical, so I only used the left one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;scipy.io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wavfile&lt;/span&gt;

&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wavfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio.wav&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# 44100 (441000, 2) 10.0 seconds
&lt;/span&gt;
&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you play it, it's noise. That's the clue. When a challenge says "hidden in noise" and the hint says spectrogram, the thing hiding in there is probably an image drawn in frequency over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a spectrogram shows
&lt;/h2&gt;

&lt;p&gt;A normal waveform plot shows loudness over time. A spectrogram splits the audio into short chunks, runs an FFT on each chunk, and plots which frequencies are loud at which moment. Time goes left to right, frequency goes bottom to top, brightness is energy.&lt;/p&gt;

&lt;p&gt;So if someone generates sound that is loud at exactly the right frequencies at exactly the right times, the picture they want shows up. Text works fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  First attempt: matplotlib defaults
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;matplotlib.pyplot&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;

&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subplots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;specgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;savefig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spec.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Matplotlib's &lt;code&gt;specgram&lt;/code&gt; defaults to &lt;code&gt;NFFT=256&lt;/code&gt;. That already showed text, but it looked rough. There were thin white vertical gaps, and I got a &lt;code&gt;divide by zero encountered in log10&lt;/code&gt; warning.&lt;/p&gt;

&lt;p&gt;My guess about the gaps: the plot takes a log of the energy, and a chunk that is pure silence has zero energy. I checked the samples and there are 34 runs of 256 or more zeros in a row, which fits. That's a guess about why, I didn't dig further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second attempt: bigger window, more overlap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;specgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NFFT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;noverlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viridis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_ylim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things changed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NFFT=2048&lt;/code&gt; gives finer frequency detail, so the letters get sharper vertically. The trade-off is that you lose time detail, since each chunk is longer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noverlap=1536&lt;/code&gt; means each chunk shares 75% of its samples with the previous one. That smooths the picture horizontally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I cut the y-axis at 8 kHz. Everything interesting sat below about 7 kHz. Zooming in made the text tall enough to read comfortably.&lt;/p&gt;

&lt;p&gt;Now the whole line of text was clear, a short label followed by a long string of characters. Reading it out was just squinting at the picture. If two characters look alike, zoom in more.&lt;/p&gt;

&lt;p&gt;There was also a faint line rising along the bottom edge between roughly 1 and 2 seconds. I don't know what it is. It might just be part of how the file was made.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Audacity route
&lt;/h2&gt;

&lt;p&gt;The hint mentioned Audacity. I did this one in Python and haven't tried that route, but the Audacity manual says you switch a track to spectrogram view from the track's dropdown menu. You can then adjust the view settings there if the text looks blurry. It's the same idea as changing &lt;code&gt;NFFT&lt;/code&gt; above.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take from it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Noise in an audio challenge plus a spectrogram hint usually means a picture hidden in the frequencies.&lt;/li&gt;
&lt;li&gt;Window size and overlap are the two knobs. If the picture is blurry, change them before you assume it's a different trick.&lt;/li&gt;
&lt;li&gt;Crop the frequency range to where the signal is.&lt;/li&gt;
&lt;li&gt;Always check both channels. Here they matched, but in other challenges they may not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Matplotlib &lt;code&gt;specgram&lt;/code&gt; docs (&lt;code&gt;NFFT&lt;/code&gt; default 256, &lt;code&gt;noverlap&lt;/code&gt;): &lt;a href="https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.specgram.html" rel="noopener noreferrer"&gt;https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.specgram.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Audacity manual, Spectrogram View: &lt;a href="https://manual.audacityteam.org/man/spectrogram_view.html" rel="noopener noreferrer"&gt;https://manual.audacityteam.org/man/spectrogram_view.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>ctf</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I would start a security audit of a Solidity contract (as someone new to this)</title>
      <dc:creator>Curious4AI</dc:creator>
      <pubDate>Fri, 02 Oct 2026 08:41:34 +0000</pubDate>
      <link>https://dev.to/curious4ai/how-i-would-start-a-security-audit-of-a-solidity-contract-as-someone-new-to-this-4b6o</link>
      <guid>https://dev.to/curious4ai/how-i-would-start-a-security-audit-of-a-solidity-contract-as-someone-new-to-this-4b6o</guid>
      <description>&lt;p&gt;I come from web app security and bug bounty hunting. Smart contracts are new to me, so I read a few audit guides and tried to boil them down to a routine I could actually follow.&lt;/p&gt;

&lt;p&gt;This is that routine. It's a learning guide, not a pro's playbook. If you audit contracts for a living and I got something wrong, tell me in the comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Read the docs before the code
&lt;/h2&gt;

&lt;p&gt;Every guide I read says the same thing first: understand what the protocol is supposed to do. Then bugs show up as places where the code does something else.&lt;/p&gt;

&lt;p&gt;Cyfrin's guide puts it well: the more you know about what a protocol should do, the easier it is to notice when it does something different. Sounds obvious. It's also the step people skip when they open 10,000 lines of Solidity.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Run it and look at the tests
&lt;/h2&gt;

&lt;p&gt;Clone the repo, compile it, call the main functions. Then open the tests and see what they cover. If you use Foundry, &lt;code&gt;forge coverage&lt;/code&gt; shows which parts have tests and which don't. The parts nobody tested are a good place to look first.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check the Solidity version
&lt;/h2&gt;

&lt;p&gt;Check the &lt;code&gt;pragma&lt;/code&gt;. Each compiler version has its own known bugs, and the version tells you a bit about how old the code is. One thing to know: since 0.8, arithmetic reverts on overflow by default. The Solidity docs say you can turn that off with &lt;code&gt;unchecked { ... }&lt;/code&gt;, so any &lt;code&gt;unchecked&lt;/code&gt; block deserves a second look.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Map the risky spots
&lt;/h2&gt;

&lt;p&gt;Cyfrin lists what to hunt for first: roles, access controls, function parameters, public and external functions, payable functions, state changes, and dependencies.&lt;/p&gt;

&lt;p&gt;Coming from web security this clicks. It's the same idea as listing endpoints, checking who can call them, and noting which ones move money.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Run a static analyzer, but don't trust it blindly
&lt;/h2&gt;

&lt;p&gt;Slither is a free static analyzer for Solidity. Install is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;slither-analyzer
slither &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It runs a bunch of detectors. Some I'd want to see for any contract: &lt;code&gt;reentrancy-eth&lt;/code&gt;, &lt;code&gt;tx-origin&lt;/code&gt;, &lt;code&gt;arbitrary-send-eth&lt;/code&gt;, &lt;code&gt;unprotected-upgrade&lt;/code&gt;. It also has printers like &lt;code&gt;human-summary&lt;/code&gt; and &lt;code&gt;contract-summary&lt;/code&gt; that give you a quick overview of a codebase.&lt;/p&gt;

&lt;p&gt;The guides agree on how to treat the output. It points you at weak spots. It doesn't replace reading the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Read it line by line
&lt;/h2&gt;

&lt;p&gt;This is the slow part. For each function, ask what it's meant to do, who can call it, and what it changes.&lt;/p&gt;

&lt;p&gt;Here's a small example. This contract lets people deposit ETH and withdraw it. I compiled it with solc 0.8.26 and it builds fine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract Vault {
    mapping(address =&amp;gt; uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount &amp;gt; 0, "nothing to withdraw");
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "send failed");
        balances[msg.sender] = 0;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the order in &lt;code&gt;withdraw&lt;/code&gt;. It sends the ETH first and sets the balance to zero after. If &lt;code&gt;msg.sender&lt;/code&gt; is a contract, its receive function runs during that &lt;code&gt;call&lt;/code&gt;, and it can call &lt;code&gt;withdraw&lt;/code&gt; again before the balance is cleared. That's reentrancy.&lt;/p&gt;

&lt;p&gt;The Solidity docs recommend the Checks-Effects-Interactions pattern: do your checks, update your state, and only then talk to other contracts. The fix is to move one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount &amp;gt; 0, "nothing to withdraw");
    balances[msg.sender] = 0;
    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok, "send failed");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more point from the docs that I didn't know: reentrancy isn't only about sending ether. Any call to another contract can hand control over.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Know what usually goes wrong
&lt;/h2&gt;

&lt;p&gt;OWASP has a Smart Contract Top 10 for 2026. The list, in order: access control, business logic, price oracle manipulation, flash loan attacks, missing input validation, unchecked external calls, arithmetic errors, reentrancy, integer overflow and underflow, proxy and upgrade bugs. OWASP notes the ordering is built from 2025 incident and survey data.&lt;/p&gt;

&lt;p&gt;Interesting to me that access control and business logic come before reentrancy. That matches how it feels in web apps, where broken access control beats clever exploits.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Write it up
&lt;/h2&gt;

&lt;p&gt;A finding that nobody understands doesn't get fixed. The guides say to show a proof of concept, give the bug a clear title, and explain the impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I'd practice
&lt;/h2&gt;

&lt;p&gt;Cyfrin points to CodeHawks First Flights as a way to practice on real code, and to Solodit, a database of past findings you can read for similar protocols.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OWASP Smart Contract Top 10 (2026): &lt;a href="https://scs.owasp.org/sctop10/" rel="noopener noreferrer"&gt;https://scs.owasp.org/sctop10/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Solidity docs, security considerations: &lt;a href="https://docs.soliditylang.org/en/latest/security-considerations.html" rel="noopener noreferrer"&gt;https://docs.soliditylang.org/en/latest/security-considerations.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cyfrin, 10 steps to approach a smart contract audit: &lt;a href="https://www.cyfrin.io/blog/10-steps-to-systematically-approach-a-smart-contract-audit" rel="noopener noreferrer"&gt;https://www.cyfrin.io/blog/10-steps-to-systematically-approach-a-smart-contract-audit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Slither (Trail of Bits / crytic): &lt;a href="https://github.com/crytic/slither" rel="noopener noreferrer"&gt;https://github.com/crytic/slither&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ethereum.org, how to use Slither: &lt;a href="https://ethereum.org/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/" rel="noopener noreferrer"&gt;https://ethereum.org/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>solidity</category>
      <category>web3</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
