<!DOCTYPE html>
<html id="_top" lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><meta name="color-scheme" content="light dark"/><meta name="language" content="zh-CN"/><title>Defcon 34 Quals Pwn WriteUp | 花盆</title><meta name="author" content="LittFlower"/><meta name="description" content="2026 年 Defcon 34 参赛游记"/><meta property="og:type" content="article"/><meta property="og:title" content="Defcon 34 Quals Pwn WriteUp | 花盆"/><meta property="og:url" content="https://blog.littflower.top/writeups/defcon-34-quals-pwn-writeup/README.mdx"/><meta property="og:description" content="2026 年 Defcon 34 参赛游记"/><meta property="og:site_name" content="花盆"/><meta property="og:locale" content="zh-CN"/><meta name="twitter:card" content="summary"/><meta name="twitter:title" content="Defcon 34 Quals Pwn WriteUp | 花盆"/><meta name="twitter:description" content="2026 年 Defcon 34 参赛游记"/><link rel="canonical" href="https://blog.littflower.top/writeups/defcon-34-quals-pwn-writeup/README.mdx"/><link rel="alternate" type="application/rss+xml" title="花盆" href="/rss.xml"/><script>(function () {
    var storageKey = "theme";
    var root = document.documentElement;
    var theme = "light";
    try {
      var stored = window.localStorage.getItem(storageKey);
      if (stored === "light" || stored === "dark") {
        theme = stored;
      } else if (
        typeof window.matchMedia === "function" &&
        window.matchMedia("(prefers-color-scheme: dark)").matches
      ) {
        theme = "dark";
      }
    } catch (_) {}
    root.setAttribute("data-theme", theme);
  })();</script><script defer src="/theme.js?v=d3b88eea84"></script><link rel="preload" as="style" href="/style.css" type="text/css"/><link rel="preload" as="style" href="/fonts.css" type="text/css"/><link rel="stylesheet" href="/style.css" type="text/css"/><link rel="stylesheet" href="/fonts.css" type="text/css"/></head><body><nav aria-label="页面导航" class="top-bar fullwidth"><div class="top-bar-inner fullwidth"><div class="top-bar-group"><a href="/" aria-label="root" class="file-link"><img src="/icons/dir.png" alt width="20" height="22" aria-hidden="true" decoding="async" loading="eager" class="icon icon--dir"/><span>/</span></a><a href=".." aria-label="parent" class="file-link"><img src="/icons/dir.png" alt width="20" height="22" aria-hidden="true" decoding="async" loading="eager" class="icon icon--dir"/><span>..</span></a></div><div class="top-bar-group"><a href="#_content" class="top-bar-link"><code class="hash inline-code">#</code>内容</a><a href="#_top" class="top-bar-link"><code class="hash inline-code">#</code>回到顶部</a><button type="button" data-theme-toggle="true" aria-pressed="false" title="切换为深色主题" class="theme-toggle"><code aria-hidden="true" class="hash inline-code">#</code>  <span data-theme-toggle-label="true" class="theme-toggle-label">浅色主题</span></button></div></div></nav><div class="page-shell"><nav aria-label="当前目录文件" class="page-files"></nav><main class="page-main page-main--toc"><article id="_content" style="scroll-margin-top:48px;" class="page-article"><h1 id="2026-年-defcon-34-参赛游记" class="scroll-target"><a href="#2026-%E5%B9%B4-defcon-34-%E5%8F%82%E8%B5%9B%E6%B8%B8%E8%AE%B0"><code class="hash inline-code">#</code>2026 年 Defcon 34 参赛游记</a></h1>
<p>qwq 先放结果</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a15078d72662027ad970f5b.png" alt="进决赛啦"/></p>
<h2 id="day-2" class="scroll-target"><a href="#day-2"><code class="hash inline-code">#</code>Day-2</a></h2>
<p>一开始以为比赛是晚上周五晚上九点开始，所以为了给队长省点机票钱，买了最便宜的周四晚上那一班飞机去北京。。飞机一路都有点颠簸，没睡着，想看个电视剧什么的发现没提前下，嗯睡睡到落地。</p>
<p>12 点刚过半个小时落地，很爽的是在大兴机场外面那个出租车平台有拒载的神人，理由是我去的地方太近了。。有点不爽 hia hia 直接举报了（</p>
<h2 id="day-1" class="scroll-target"><a href="#day-1"><code class="hash inline-code">#</code>Day -1</a></h2>
<p>第二天早早到酒店，我和 stackc00kie 师傅一间，我一个人在房间玩到他来，和他聊天才发现“我靠原来比赛是早上五点开始”，原来是 UTC+0 啊，哈哈那无敌了（</p>
<p>然后等人来的差不多，队长和 tplus 拉着我们一起去酒店旁边找了个饭店吃饭，听说前几年也是在这边吃的，还不赖（</p>
<p>stackc00kie 师傅拍了点照片：</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a1571b37e05763db3cdf3d3.png" alt/></p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a15719d7e05763db3cdf3cf.png" alt/></p>
<p>谔谔... stackc00kie 师傅晚上 11 点早早就睡了，而我的作息根本睡不着，依旧 1 点睡 4 点起.. 阎王夸我好身体</p>
<h2 id="day-0" class="scroll-target"><a href="#day-0"><code class="hash inline-code">#</code>Day 0</a></h2>
<p>ok 早上五点也是早早起来把电脑搬到 tplus 房间准备开打了，，然后遇到了很经典的</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a150afb72662027ad971101.png" alt="延迟半小时"/></p>
<p>很难绷...</p>
<p>第一天第一波放的 Pwn 题是一个 lua vm，输入 lua 字节码，然后解释运行。</p>
<p>一开始注意到🕳其实是在 <code class="inline-code">ipairs</code> 里存在对 <code class="inline-code">shared_prt</code> 生命周期的错误管理，可以实现 UAF 一个 0x20 的 chunk，而且后续研究通过堆喷也可以实现踩到这个 chunk</p>
<p>中间还闹了个乌龙，题目的 docker 的 glibc 版本是 2.39，chroot 的是 2.43，一开始堆喷拿 2.39 打的，后面发现其实得按 2.43 打。。。但这都不是大问题</p>
<p>问题是没有 leak... 在这个地方一直卡着，事已至此 先想想吃什么，tplus 师傅点了 kfc，吃东西的时候挂着 agent 再让 ai 看看有没有办法 leak，0 进展。。</p>
<p>这个时候上了一道 kernel pwn，出于好奇打开附件看了眼：</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a150da572662027ad97304e.png" alt/></p>
<p>我靠怎么是 Ubuntu 2404，那岂不是可以打这个 <a href="https://kqx.io/post/qemu-nday/%EF%BC%8C%E8%BF%9B" title="https://kqx.io/post/qemu-nday/，进" target="_blank" rel="noopener">https://kqx.io/post/qemu-nday/，进</a> docker 确认了下 qemu 版本，果然是 v8.2，看了眼 <code class="inline-code">run.sh</code>，是 TCG。。那感觉可以打啊。。</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a150e5372662027ad9730e2.png" alt/></p>
<p>思索...然后</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a1144c66211f5ac19ea4f9b.png" alt/></p>
<p>是的我已经一分钟没有跟人提起过 Defcon 34 内核题 1 血了，虽然这没什么好炫耀的，但是正如我所说这没什么好炫耀的 OvO</p>
<p>其实想不通这题为什么没人打，因为其实一开始在看这题的师傅在看预期解，后面想了想可能这个点外国人还在歇（</p>
<p>然后三点钟上了另一道 gitvfs，也是同样的环境，如法炮制两道一血到手了（</p>
<p>不过没敢交 gitvfs 的 flag2，因为怕交太快又上新题，我们前面的题还没怎么做（tplus 的策略)，感觉是对的</p>
<p>然后上了 shelldiet 这道 KoH，具体就是实现一个读 flag 的 shellcode，要求 shellcode 的所有字节和相加尽可能小。没开任何沙箱限制。</p>
<p>先插一个回旋镖 <img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a15118872662027ad973269.png" alt/></p>
<p>先当 speedrun 打，第一反应：</p>
<ol>
<li>肯定直接拿 shell 优于 orw</li>
<li>double read 应该是最好的方向</li>
</ol>
<p>然后一开始写了个 260 字节的先交上去，顺带提一嘴，这场比赛的 PoW 计算量都好大，每次都能给我电脑 cpu 卡住...可能该换电脑了，后续这题都是用 tplus 借我的服务器打的</p>
<p>double read 的思路一开始就是最常规的 <code class="inline-code">pop rdx; xchg eax, esi; syscall</code>，然后通过 <code class="inline-code">add byte ptr [rax], al</code> 这种低 cost 汇编实现自修改。这种一直卷到 24.. 歇了一会让 ai 继续研究，去看上午没看完的 stork，这个时候这个题已经有很多解了。感觉就是 ai 做的，另外 cscat 师傅实现了一个 gc 的 poc。。</p>
<p>daimi 把 poc 给 ai，leak 尝试爆破，然后就出了（</p>
<p>给 ai 大人👻了</p>
<h3 id="livectf" class="scroll-target"><a href="#livectf"><code class="hash inline-code">#</code>livectf</a></h3>
<p>这个时候 pwn 基本上做的差不多了，开始帮 tplus 看 livectf，此时 livectf 刚进行到 phase2</p>
<p>livectf 很有意思，题目有点像坦克大战，每个队伍可以维护一个 bot，bot 根据队伍自定义的策略在地图上战斗，击杀越多得分越多。题目下发了一个 vm 程序，bot 需要实现为一个 bin，vm 负责解析 bot，每个 tick 可以执行一个 syscall，每个 syscall 之间允许执行 10000 条指令</p>
<p>场上一共 1000 个 bot，70 个队伍，剩下全是 npc</p>
<p>一个大轮 4 h，一个小轮 3 min，一共 6 个大轮</p>
<p>具体信息（例如武器池、武器信息、行动冷什么的）得靠每轮逆向那个 vm 程序。然后得实现一个编译 bot 的编译器（</p>
<p>我上手的时候 phase1 已经 gg 了，40 多名，一开始理想的策略是先本地 agent 养蛐蛐，然后拿出最优化的交到平台上，解析一下这类 bot 发现 ai 喜欢行动之前先用 scan map 的 syscall 拿敌人站位，但是这个动作是需要花费 tick 的，也就是开局大概率站着不动，死亡率很高，所以分数很低。。</p>
<p>最粗暴的策略反而是最有效的，开局先随机动两步，然后朝着一个方向旋转射击杀 bot（主要是杀 npc）就能拿到不错的分数，至少在 phase2 是这样（第三名）。</p>
<p>后面阶段三就不太行了，我们这种策略很大的问题是开局随机动两步很多时候没什么用，有很大概率和 npc 在一条线上然后被它秒了，，，或者是活了很久但是因为移动很低效，在中后期的地图上找不到人杀，导致活了很久得分还没一些没活多久的队伍分数高。</p>
<p>这个时候 Misty 师傅来了，一个很对的洞见是“开局随机移动是无意义的，倒不如选一个方向开一枪，开枪之后一定不会被那个方向的 npc 打了”，</p>
<p>基于这个思想，把 bot 策略改成</p>
<ol>
<li>开局随机开一枪</li>
<li>随机移动，一边移动一边朝一个方向开枪</li>
<li>如果长期杀不到人（中后期）就提高移动效率找人杀。</li>
</ol>
<p>然后我就想把这个策略实现了交到 Nu1L 上，but 这个时候 tplus 不让我上，我说我本地 benchmark 分数还可以，他让我自己开个小号交，他要感觉多改多错（</p>
<p>很难绷啊 我小号分数很快比大号高了（</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a11de896211f5ac19ed736d.png" alt/></p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a11de596211f5ac19ed736a.png" alt/></p>
<p>那这个时候 tplus 肯定想让我赶紧把 bot 交到大号上，为了防止被 ban 我特意重新编译了一份，然后最搞笑的来了，同样的逻辑交到 xingmengctf 上每轮都能吃一两百份，交到 Nu1L 上每轮不超过 100 份（（</p>
<blockquote>
<p>tplus：许哥你是演员吧 😡</p>
<p>花：我靠不到啊（（</p>
<p>花：我靠小号分数又涨了</p>
</blockquote>
<p>反正很难绷得住，tplus 要去睡觉了，于是第三轮后面的 livectf 就暂时交给了 Misty、Mr.R。</p>
<p>我们反思了一下第三轮 bot 逻辑的缺陷，由于获取地图信息的代价太大，bot 的移动都是完全随机/一条路走到黑的，很容易卡在地图的某个角落然后一直卡到该轮结束</p>
<p>Misty 说既然地图是已知的，其实可以先把地图压缩一下存下来，然后用类似于 A* 的算法给 bot 设计一个比较好的寻路算法，但这个肯定在 10000 条指令里是不那么好做的，于是 Misty 大哥开始实现了一个基于 C 的编译器（之前是 python 的</p>
<p>这个时候距离开赛已经 24h 了，我实在扛不住，挂了一些 agent 就去睡觉了（（</p>
<p>哦对，睡之前发现又上了两道内核的 revenge，感觉天塌了（</p>
<h2 id="day1" class="scroll-target"><a href="#day1"><code class="hash inline-code">#</code>day1</a></h2>
<p>睡醒起来，misty 师傅还在奋战，，寻路算法效果不是特别好，有个恶性 bug 是 bot 在寻路算法之后会待在原地不动（不执行），然后一边修一边照着 replay 抄了第一名队伍的策略：</p>
<ol>
<li>开局站着不动，朝四个方向一边转向一边射击（此时单向枪无 cd）</li>
<li>杀到 500 tick 左右开始用 dash syscall 游走，如果撞墙则反射到另一个方向</li>
</ol>
<p>感觉还不赖（</p>
<p>这个策略在 phase 4 寻路算法恶性 bug 的时候救老命了hh</p>
<p>中间有个插曲，我们在交 bot 之前会先在小号上测试一下，结果经常出现同样的 bot 在小号上效果很好，换到大号就 gg...</p>
<blockquote>
<p>花：是不是 Nu1L 这个号风水不好...</p>
<p>tplus：号风水的来了...</p>
</blockquote>
<p>然后到了 phase 5，题目模式改成了占点模式，之前写的寻路算法发挥了很大作用，由于之前写的早，这一轮成功干到了第一名。misty tql</p>
<p>livectf 没什么好看的了，然后去看那两个内核的 revenge，第一个 revenge 之后预期解直接被 ai 秒了，第二个有两个 flag，flag1 被 ai 秒了，flag2 被大哥们用<a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=48f6a5356a33dd78e7144ae1faef95ffc990aae0" title="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=48f6a5356a33dd78e7144ae1faef95ffc990aae0" target="_blank" rel="noopener">这个</a>没在 7.0.9 上修复且出题人忘了 patch 的 🕳 又非了....</p>
<p>去帮 misc 看了一下一个 ocr 题目，里面全是这种逆天 hex</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a1574cc7e05763db3cdf47f.png" alt/></p>
<p>我作为 agent 的 harness 把这些全肉眼 ocr 了一遍，大概是个 tcp 数据包，里面有个 gzip。。由于我计网一点没听，所以 ocr 体力活干完就跑路了（</p>
<p>还是回来继续卷 shellcode koh，想了想发现其实没必要死磕 <code class="inline-code">pop rdx; xchg esi, eax; syscall</code>，这个的优化看起来已经到瓶颈了，于是我尝试了另外两条路：</p>
<ol>
<li><code class="inline-code">jmp &lt;main_addr></code></li>
<li><code class="inline-code">ret xxx</code> 调栈从而控制返回地址</li>
</ol>
<p>第一个大概优化到 30 左右就没做了，感觉 <code class="inline-code">jmp</code> 还是太复杂了，自修改很麻烦，考虑第二个，看了看栈发现 <code class="inline-code">ret 0x20</code> 可以让程序再次 ret2main，效果还不错，可以做到 20</p>
<p>后面又优化了一下，可以做到 11。但是在排行榜上这个策略只能排到 16。。</p>
<p>调了一下 docker，发现远程栈里 <code class="inline-code">ret 0xd8</code> 也是一样的效果（那里是 <code class="inline-code">_start</code>），成功做到 10。这个时候大概猜到前两名的队伍是非预期了，赛后果然非预期就是那个打那个 pipe 设备，通过 race 先把程序原本写到 buffer 里的 shellcode_sum 读出来，再写个自己的进去，可以做到 0...</p>
<p>哎，✅8️⃣我当时看了眼这玩意不是只读挂载吗。。。怎么可以写啊！！</p>
<p><img loading="lazy" decoding="async" fetchpriority="auto" src="https://pic1.imgdb.cn/item/6a156f017e05763db3cdf23b.png" alt/></p>
<p>（上面这个图是赛后截的，没想到 blue water 居然在不非预期的情况下进一步压缩到了 8。。。）</p>
<p>当时就感觉再卷下去没有性价比了（因为要归一化），就去帮忙看 nodefs 这道题</p>
<p>这个题得靠 race 做 leak，然后网速超级慢，最后师傅租了个美国原生服务器解决了。。</p>
<p>到这大概是 5.30 晚上 10 点左右，我们把 pwn 都 ak 了。</p>
<p>后面就是看着 tplus 开 20+ 个 agent 围殴谜语 Misc 题，哈哈。其实今天早上还以为题要做不完了，感觉要进不了 defcon final 非常焦虑。。最后结果是好的，还不赖。</p>
<h2 id="day2" class="scroll-target"><a href="#day2"><code class="hash inline-code">#</code>Day2</a></h2>
<p>pwn ak 之后我和 stackc00kie 就打算洗洗睡了</p>
<p>本来说比赛结束前 两个小时再看看有没有要帮忙的，然后根本起不来，我和 stackc00kie 一觉睡到 8 点，，，他 9 点的高铁，说起来，stackc00kie 师傅两天可能睡了不超过 2h，这个更是超人</p>
<h2 id="一些思考" class="scroll-target"><a href="#%E4%B8%80%E4%BA%9B%E6%80%9D%E8%80%83"><code class="hash inline-code">#</code>一些思考</a></h2>
<p>这场 defcon 的 ctf 题目其实前几名的队伍都 ak 了，主要差距就在 koh 和 livectf 上。shellcode 这道 koh 其实就是因为我们人手不太够，导致这种纯 trick 的 speedrun 交的很晚，不过好在第一名非预期给大家都卷4️⃣了，归一化之后分差并不大</p>
<p>再就是感觉内核 1day 的积累很重要，虽然一开始放的两道内核题我很快就反应出可以打去年的 qemu nday，但是后续 revenge 之后就没什么好办法了，但是大哥们依然可以靠手里存的很多 cve 把这个打完。。</p>
<p>还有就是 agent 工作流的问题吧，感觉我现在的工作流还是不是很高效，应该好好思考一下怎么优化。</p>
<p>不过确实是很开心的一次比赛，这半年来体验最好的一场 ctf，play for fun and win~</p></article><nav aria-label="文章目录" id="article-toc" class="toc"><div class="toc-title">目录</div><ul class="toc-list"><li class="toc-item toc-l2"><a href="#day-2">Day-2</a></li><li class="toc-item toc-l2"><a href="#day-1">Day -1</a></li><li class="toc-item toc-l2"><a href="#day-0">Day 0</a></li><li class="toc-item toc-l3"><a href="#livectf">livectf</a></li><li class="toc-item toc-l2"><a href="#day1">day1</a></li><li class="toc-item toc-l2"><a href="#day2">Day2</a></li><li class="toc-item toc-l2"><a href="#一些思考">一些思考</a></li></ul></nav></main></div><footer class="footer"><div class="footer-row footer-links"><span class="footer-label">保持联系</span><a href="mailto:xzy1476569428@163.com" class="footer-link">xzy1476569428@163.com</a><span aria-hidden="true" class="footer-separator">·</span><a href="/rss.xml" class="footer-link">RSS</a><span aria-hidden="true" class="footer-separator">·</span><a href="https://github.com/LittFlower/my_blog" class="footer-link">GitHub</a></div><div class="footer-row footer-meta"><span>本站基于 <a href="https://github.com/unvariant/blog">unvariant/blog</a> 改造，除特别说明外内容以 CC BY 4.0 协议共享。</span></div></footer></body></html>