Bitcoin Forum
October 28, 2026, 07:04:40 PM *
News: Latest Bitcoin Core release: 31.1 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: [Flag Captured] CTF for Bitcoin that I found: stealmybitcoin.com  (Read 187 times)
TheButterZone (OP)
Legendary
*
Offline

Activity: 3304
Merit: 1156


RIP Mommy


View Profile WWW
October 25, 2026, 01:50:11 AM
Last edit: October 26, 2026, 06:14:14 AM by TheButterZone
 #1

Midway through learning how to CTF for the first time, I'm getting 504 Gateway Time-out, don't have time to sit around waiting for the DoS to resolve.

Server: nginx

Flag address to capture: https://mempool.space/address/bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy

If you're successful, I'd appreciate a tip for bringing this CTF to your attention...

Code: (stealmybitcoin.com)
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="theme-color" content="#edf4fb">
  <meta name="description" content="A PHP security challenge. Find the hidden bitcoin key. First person to get the wallet wins.">
  <title>Steal My Bitcoin</title>
  <link rel="icon" href="/favicon.svg" type="image/svg+xml">
  <style>
    :root { color-scheme: light; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #243b53; background: #edf4fb; }
    * { box-sizing: border-box; }
    body { margin: 0; min-height: 100vh; min-height: 100svh; display: grid; place-items: center; padding: 32px 20px; background: radial-gradient(ellipse at 15% 15%, #dcecfb 0, transparent 55%), radial-gradient(ellipse at 90% 90%, #e0eafa 0, transparent 50%), #edf4fb; }
    main { width: 100%; max-width: 1000px; }
    .card { background: #fff; border: 1px solid #dbe6f0; border-radius: 22px; padding: clamp(28px, 6vw, 52px); box-shadow: 0 20px 70px -30px #486f9838; }
    h1 { color: #183957; font-size: clamp(30px, 6vw, 40px); letter-spacing: -.045em; line-height: 1.15; margin: 0 0 24px; font-weight: 650; }
    p { font-size: 16px; line-height: 1.8; margin: 0 0 18px; }
    strong { font-weight: 650; color: #1f568b; }
    code { background: #eef4fa; color: #245886; border: 1px solid #e0eaf4; border-radius: 5px; padding: 2px 6px; font-size: .88em; overflow-wrap: anywhere; }
    .finish { margin: 28px 0 0; padding-top: 23px; border-top: 1px solid #e8eef5; color: #235a8d; font-weight: 600; }
    footer { margin-top: 22px; text-align: center; font-size: 12px; letter-spacing: .025em; color: #58738d; }
    .notice { margin-top: 24px; padding: 18px 20px; background: #f0f5fb; border: 1px solid #dce7f2; border-radius: 10px; }
    .notice p { margin: 0; font-size: 13px; line-height: 1.75; color: #45617b; }
    .layout { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 24px; align-items: start; }
    .wallet { margin: 0; padding: 24px; min-width: 0; border-radius: 18px; }
    .wallet-heading { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px; align-items: baseline; font-size: 14px; }
    .wallet a { color: #235a8d; text-underline-offset: 3px; font-size: 13px; }
    .wallet-address { overflow-wrap: anywhere; font-family: monospace; font-size: 12px; color: #58738d; margin: 8px 0 0; }
    #wallet-balance { font-size: 13px; margin: 18px 0 4px; line-height: 1.5; font-variant-numeric: tabular-nums; color: #58738d; }
    #wallet-usd { font-size: 30px; font-weight: 600; letter-spacing: -.04em; line-height: 1.2; color: #183957; margin: 0 0 18px; font-variant-numeric: tabular-nums; }
    #wallet-refresh { font: inherit; font-size: 12px; color: #346595; background: transparent; border: 0; border-top: 1px solid #e8eef5; border-radius: 0; padding: 14px 0 0; width: 100%; text-align: left; cursor: pointer; }
    #wallet-refresh:disabled { opacity: .6; cursor: wait; }
    .wallet-address a { font-family: monospace; font-size: 13px; text-decoration: none; }
    a:hover { text-decoration: underline; }
    @media (max-width: 780px) { .layout { grid-template-columns: 1fr; } main { max-width: 620px; } .wallet { padding: 24px; } }
    .challenge-clock { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; margin: -8px 0 24px; color: #58738d; }
    #challenge-clock { font-size: 13px; font-weight: 400; font-variant-numeric: tabular-nums; color: #58738d; }
    article.challenge-solved { border-color: #e3b0b6; background: #fffafa; }
    .challenge-solved .challenge-clock { background: #fcecef; border-radius: 6px; padding: 8px 12px; margin-top: -8px; }
    .challenge-solved #challenge-clock { color: #a52f3b; font-weight: 600; }
    .challenge-solved #prize-copy strong { color: #a52f3b; }
  </style>
<!-- SITE-AUTHORSHIP-PROOF
{
  "algorithm": "Ed25519",
  "public_key_spki_base64": "MCowBQYDK2VwAyEAEXObSx+XQvc/5vOeAVqcHpY5Gu0BN67Fv0726iGJ0nc=",
  "message_utf8_base64": "U2l0ZSBhdXRob3JzaGlwIGNsYWltCkRvbWFpbjogc3RlYWxteWJpdGNvaW4uY29tClN0YXRlbWVudDogVGhlIGhvbGRlciBvZiB0aGUgY29ycmVzcG9uZGluZyBwcml2YXRlIGtleSBjcmVhdGVkIGFuZCBvcGVyYXRlcyB0aGlzIGNoYWxsZW5nZSB3ZWJzaXRlLgpJc3N1ZWQ6IDIwMjYtMTAtMjRUMTg6NTA6MDFaCk5vbmNlOiBjYjE2NzEyMjA2YmE4NjY1OGYyMWExYmI2YTlhMWNjMWY5MDliNmM2N2ZjYmRhNDgK",
  "signature_base64": "S6N38mGD/S8Hx07SfAtw1iEZ21q0KaC7IkzOrWDdmHfHMsHQcdfBs4ehU9y9EfKnD11KBqdJVIqabWn82uIACw=="
}
END-SITE-AUTHORSHIP-PROOF -->
</head>
<body>
  <main>
    <div class="layout">
    <article class="card" aria-labelledby="challenge-title">
      <h1 id="challenge-title">Steal my Bitcoin</h1>
      <div class="challenge-clock" aria-label="Challenge duration"><span id="challenge-clock">0.00 hours without a successful hack</span></div>
      <p id="prize-copy"><span id="prize-tense">There is</span> a bitcoin key hidden on this server worth <strong id="prize-value">about $500</strong>.</p>
      <p>It is accessible to the PHP user account through the <code>print-flag</code> binary.</p>
      <p>This PHP version was selected from before the PHP2SHELL CVEs were released.</p>
      <p class="finish">First person to get the wallet wins. If you don’t need the money, consider donating it to those who do: <a href="https://www.givewell.org/" target="_blank" rel="noopener noreferrer" style="color: inherit; text-underline-offset: 3px;">givewell.org</a>.</p>

      <aside class="notice" aria-label="Security and monitoring notice">
        <p>All traffic is logged. This is a known vulnerable machine, so we cannot guarantee privacy. Other attackers may access or monitor activity and data beyond our control. Do not submit anything you want to keep private.</p>
      </aside>
    </article>
      <section class="card wallet" aria-label="Challenge wallet">
        <div class="wallet-heading"><strong>Challenge wallet</strong><a href="https://mempool.space/address/bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy" target="_blank" rel="noopener noreferrer">Explorer &nearr;</a></div>
        <p class="wallet-address"><a href="https://mempool.space/address/bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy" target="_blank" rel="noopener noreferrer" title="bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy" aria-label="View wallet bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy on mempool.space">bc1qlnu5&hellip;t3cxqy</a></p>
        <p id="wallet-balance" role="status" aria-live="polite">Checking balance&hellip;</p>
        <p id="wallet-usd" role="status" aria-live="polite"></p>
        <button id="wallet-refresh" type="button">Refresh &circlearrowright;</button>
        <noscript><p>JavaScript is off. Use the explorer link to check the balance.</p></noscript>
      </section>
    </div>
    <footer>stealmybitcoin.com &middot; <a href="/terms.html" style="color: inherit; text-underline-offset: 3px;">Terms of participation</a></footer>
  </main>
<script>
(() => {
  const address = 'bc1qlnu50y4zm0nwxkckxu09thn5xdcz8aw6t3cxqy';
  const api = 'https://mempool.space/api';
  const start = Date.parse('2026-10-24T20:30:00Z');
  const output = document.getElementById('wallet-balance');
  const button = document.getElementById('wallet-refresh');
  const usdOutput = document.getElementById('wallet-usd');
  const clock = document.getElementById('challenge-clock');
  const timing = document.querySelector('.challenge-clock');
  let finish = null;
  let prizeDollars = null;
  const btc = sats => (sats / 100000000).toFixed(8) + ' BTC';
  function tick() {
    document.getElementById('prize-value').textContent = finish !== null || !(prizeDollars > 0)
      ? 'about $500'
      : 'about ' + new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD', maximumFractionDigits: 0}).format(prizeDollars);
    document.getElementById('prize-tense').textContent = finish !== null ? 'There was' : 'There is';
    document.querySelector('article.card').classList.toggle('challenge-solved', finish !== null);
    const hours = Math.max(0, ((finish ?? Date.now()) - start) / 3600000).toFixed(2);
    clock.textContent = finish !== null
      ? (finish < start ? 'Wallet claimed before the challenge started' : 'Successfully hacked after ' + hours + ' hours')
      : hours + ' hours without a successful hack';
  }
  async function get(path, signal) {
    const response = await fetch(api + path, {
      signal, credentials: 'omit', referrerPolicy: 'no-referrer', cache: 'no-store'
    });
    if (!response.ok) throw new Error('Explorer unavailable');
    return response.json();
  }
  // Walk confirmed history backwards to find the spend that crossed below $10.
  // The finish time comes from that transaction's block, never the visitor's clock.
  async function findFinish(confirmed, price, signal) {
    let balance = confirmed;
    let cursor = '';
    const seen = new Set();
    while (true) {
      const batch = await get('/address/' + address + '/txs/chain' + (cursor ? '/' + cursor : ''), signal);
      if (!Array.isArray(batch)) throw new Error('Invalid transaction history');
      for (const tx of batch) {
        if (seen.has(tx.txid)) throw new Error('Repeated transaction history');
        seen.add(tx.txid);
        const received = tx.vout.filter(v => v.scriptpubkey_address === address).reduce((sum, v) => sum + v.value, 0);
        const spent = tx.vin.filter(v => v.prevout?.scriptpubkey_address === address).reduce((sum, v) => sum + v.prevout.value, 0);
        const before = balance - received + spent;
        if (spent > received && before * price / 1e8 >= 10 && balance * price / 1e8 < 10 &&
            tx.status?.confirmed && Number.isFinite(tx.status.block_time)) {
          return {time: tx.status.block_time * 1000, txid: tx.txid};
        }
        balance = before;
      }
      if (batch.length < 25) return null;
      cursor = batch[batch.length - 1].txid;
    }
  }
  async function refresh() {
    if (button.disabled) return;
    button.disabled = true;
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 20000);
    try {
      const data = await get('/address/' + address, controller.signal);
      const values = [data.chain_stats?.funded_txo_sum, data.chain_stats?.spent_txo_sum,
                      data.mempool_stats?.funded_txo_sum, data.mempool_stats?.spent_txo_sum];
      if (!values.every(v => Number.isSafeInteger(v) && v >= 0)) throw new Error('Invalid balance');
      const confirmed = values[0] - values[1];
      const pending = values[2] - values[3];
      output.textContent = btc(confirmed) + (pending ? ' · Pending: ' + (pending > 0 ? '+' : '') + btc(pending) : '');
      output.title = 'Confirmed balance · Checked ' + new Date().toLocaleTimeString();
      let prices;
      try {
        prices = await get('/v1/prices', controller.signal);
        if (!Number.isFinite(prices.USD) || prices.USD <= 0) throw new Error('Invalid price');
      } catch (error) {
        usdOutput.textContent = 'USD estimate unavailable';
        return;
      }
      const dollars = confirmed / 1e8 * prices.USD;
      prizeDollars = dollars;
      usdOutput.textContent = '≈ ' + new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD'}).format(dollars) + ' USD';
      usdOutput.title = 'Approximate confirmed balance; excludes pending changes and fees.';
      if (dollars < 10 && values[1] > 0) {
        const result = await findFinish(confirmed, prices.USD, controller.signal);
        finish = result?.time ?? null;
        timing.title = result ? 'Finish based on the spending transaction’s block timestamp: ' + new Date(finish).toUTCString() : 'No qualifying confirmed spend found.';
        if (result && finish < start) {
          tick();
        } else tick();
      } else {
        finish = null;
        timing.title = pending < 0 ? 'Outgoing payment pending; finish time awaits confirmation.' : 'Total time since the scheduled challenge start.';
        tick();
      }
    } catch (error) {
      output.textContent = 'Update unavailable. Check the explorer.';
      timing.title = 'Wallet verification unavailable; showing the last verified timer state.';
    } finally {
      clearTimeout(timeout);
      button.disabled = false;
    }
  }
  button.addEventListener('click', refresh);
  tick();
  setInterval(() => {
    if (finish !== null && finish < start) {
      tick();
    } else tick();
  }, 1000);
  setInterval(() => { if (!document.hidden) refresh(); }, 60000);
  document.addEventListener('visibilitychange', () => { if (!document.hidden) refresh(); });
  refresh();
})();
</script>
</body>
</html>

Code: (https://stealmybitcoin.com/terms.html)
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="theme-color" content="#edf4fb"><title>Terms of participation | The Bitcoin Challenge</title><style>
    :root { color-scheme: light; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: #243b53; background: #edf4fb; }
    * { box-sizing: border-box; }
    body { margin: 0; min-height: 100vh; min-height: 100svh; display: grid; place-items: center; padding: 32px 20px; background: radial-gradient(ellipse at 15% 15%, #dcecfb 0, transparent 55%), radial-gradient(ellipse at 90% 90%, #e0eafa 0, transparent 50%), #edf4fb; }
    main { width: 100%; max-width: 620px; }
    .card { background: #fff; border: 1px solid #dbe6f0; border-radius: 22px; padding: clamp(28px, 6vw, 52px); box-shadow: 0 20px 70px -30px #486f9838; }
    .eyebrow { display: flex; align-items: center; gap: 9px; margin: 0 0 24px; color: #346595; font-size: 12px; font-weight: 650; letter-spacing: .13em; text-transform: uppercase; }
    .dot { width: 7px; height: 7px; background: #4988c6; border-radius: 50%; }
    h1 { color: #183957; font-size: clamp(30px, 6vw, 40px); letter-spacing: -.045em; line-height: 1.15; margin: 0 0 24px; font-weight: 650; }
    p { font-size: 16px; line-height: 1.8; margin: 0 0 18px; }
    strong { font-weight: 650; color: #1f568b; }
    code { background: #eef4fa; color: #245886; border: 1px solid #e0eaf4; border-radius: 5px; padding: 2px 6px; font-size: .88em; overflow-wrap: anywhere; }
    .finish { margin: 28px 0 0; padding-top: 23px; border-top: 1px solid #e8eef5; color: #235a8d; font-weight: 600; }
    footer { margin-top: 22px; text-align: center; font-size: 12px; letter-spacing: .025em; color: #58738d; }
  
    main { max-width: 800px; }
    body { display: block; }
    main { margin: 32px auto; }
    h2 { font-size: 20px; color: #183957; margin: 30px 0 10px; letter-spacing: -.02em; }
    p, li { font-size: 15px; line-height: 1.8; }
    ul { padding-left: 22px; }
    li { margin-bottom: 8px; }
    a { color: #235a8d; text-underline-offset: 3px; }
    .updated { font-size: 13px; color: #58738d; }
</style><link rel="icon" href="/favicon.svg" type="image/svg+xml"></head><body><main><article class="card"><p><a href="/">&larr; Back to the challenge</a></p><h1>Terms of participation</h1><p>This is a free challenge of technical skill, not a random drawing or sweepstakes. No purchase or payment is required. Only participate if it is legal for you to do so.</p>
<h2>The wallet</h2>
<p>First person to get the wallet wins. Someone else may empty it before you do, even if you find the key. The displayed dollar value is approximate; bitcoin prices and fees change. There is no guaranteed payout or replacement of funds already claimed. Do not send your own money to the wallet.</p>
<h2>Play within the challenge</h2>
<p>Test the web application and run <code>print-flag</code> inside the PHP container. Do not attack the host, SSH, Docker, Nginx, other people, or third-party services. No container escapes, denial of service, resource exhaustion, persistence, or sabotage.</p>
<h2>Use at your own risk</h2>
<p>This server is intentionally insecure. Use an isolated testing environment and do not submit passwords, private keys, or personal information.</p>
<h2>Privacy</h2>
<p>All traffic is logged. This is a known vulnerable machine, so we cannot guarantee privacy. Other attackers may access or monitor activity and data beyond our control. Do not submit anything you want to keep private.</p>
<p>The challenge may go offline, change, or end at any time. Uptime, solvability, saved progress, and recovery are not guaranteed.</p>
<p>To the extent the law allows, we provide this challenge as is and accept no liability for losses or damage from taking part.</p>
<p>By participating, you agree to these rules.</p></article><footer>stealmybitcoin.com</footer></main></body></html>

TheButterZone (OP)
Legendary
*
Offline

Activity: 3304
Merit: 1156


RIP Mommy


View Profile WWW
October 25, 2026, 11:45:40 AM
 #2

Just used up my full Claude quota, made me use Sonnet 4.6 Medium, can't CTF for crap.

Pmalek
Legendary
*
Offline

Activity: 3654
Merit: 9804



View Profile
October 25, 2026, 05:07:52 PM
 #3

Won't Claude refuse to do such actions as trying to breach a server and take bitcoin from a wallet? Perhaps it's a question of prompting it properly.

▄▄███████████████████▄▄
▄███████████████████████▄
███████████████████▄█████
█████████████████████████
███████████▀█████████████
█████████▀███▀██████▀████
██████████████████▄██████
█████████▄▄█▄▄███████████
██████████▄▄▄████████████
███████████████████▀█████
████████████████▀▀███████
▀███████████████████████▀
▀▀███████████████████▀▀
 
 EARNBET 
|⚽ 🏀
 
🏈 🏓
 
🎯 🥊
 
⚾ 🎾
 
⛳ 🏐
 
🏏 🏎️
|


███████▄▄███████████
████▄██████████████████
██▄▀▀███████████████▀▀███
█▄████████████████████████
▄▄████████▀▀▀▀▀████████▄▄██
███████████████████████████
█████████▌████▀████████████
███████████████████████████
▀▀███████▄▄▄▄▄█████████▀▀██
█▀█████████████████████▀██
██▀▄▄███████████████▄▄███
████▀██████████████████
███████▀▀███████████

....HIGHEST....
VIP REWARDS

 ✔ G U A R A N T E E D   
| 
 🜲 
KING OF
THE CASTLE

$200K in prizes
| 
..PLAY NOW..
TheButterZone (OP)
Legendary
*
Offline

Activity: 3304
Merit: 1156


RIP Mommy


View Profile WWW
October 25, 2026, 05:23:51 PM
 #4

It was presented verbatim, recognized as a CTF & titled "Winning a CTF challenge"

But I'm done with Claude piecemealing my quota to death yet again, on round 2.

Quote from: Claude's summary before euthanasia:
The server has session.upload_progress disabled, which blocks the only publicly available PoC. Winning now requires either finding a memory address leak nobody has found yet, or brute-forcing ASLR — both of which require writing original exploit code at a level that assumes deep knowledge of PHP internals, heap layouts, and memory corruption exploitation. That’s not a beginner gap, that’s years of specialist work.




TheButterZone (OP)
Legendary
*
Offline

Activity: 3304
Merit: 1156


RIP Mommy


View Profile WWW
October 26, 2026, 06:13:40 AM
 #5

Someone got it.

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!