惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
Arctic Wolf
T
Tenable Blog
T
Troy Hunt's Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy & Cybersecurity Law Blog
NISL@THU
NISL@THU
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hacker News: Front Page
S
Secure Thoughts
AWS News Blog
AWS News Blog
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
M
MIT News - Artificial intelligence
T
Tor Project blog
S
Schneier on Security
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
美团技术团队
Google DeepMind News
Google DeepMind News
V
Visual Studio Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
罗磊的独立博客
T
Threat Research - Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V
V2EX
C
CXSECURITY Database RSS Feed - CXSecurity.com
Stack Overflow Blog
Stack Overflow Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
D
Docker
J
Java Code Geeks
GbyAI
GbyAI
H
Heimdal Security Blog
The Hacker News
The Hacker News
MongoDB | Blog
MongoDB | Blog
V
Vulnerabilities – Threatpost
T
Tailwind CSS Blog
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
C
CERT Recently Published Vulnerability Notes
Y
Y Combinator Blog
Recorded Future
Recorded Future
Cisco Talos Blog
Cisco Talos Blog
T
Threatpost
The Register - Security
The Register - Security
Hacker News - Newest:
Hacker News - Newest: "LLM"

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog NAT 类型初探 | Yesterday17's Blog
[JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog
Yesterday17 · 2020-10-11 · via Yesterday17's Blog

这篇文章记录的是吉林大学 2020 CTF 校赛的 babywasm 题解。用到的工具有 Chrome Developer Toolwabt

ToC

  • 记录基本偏移信息
  • 入口
  • 进入 func9
    • IF 线:如果你会使用搜索引擎
  • 发挥作用的偏移
  • 关键逻辑分析
  • 修改 WASM
  • 结果

记录基本偏移信息

上来先观察 data 段。

我们发现了一些有趣的东西。首先是 flag 必备的 Spirit{},然后是弹出对话框中的文本,中间夹杂了一些不明所以的 ASCII 字符。

我们把这些东西的偏移都记录下来。从前面的 i32 const 1048576 可以知道,基础偏移是 1048576a995 的偏移是 1048613Spirit 的偏移是 1048713

目前我们还不知道这些东西到底有什么用,带着准备好的偏移数据,我们进入正式的分析环节。

入口

首先我们需要定位目标函数。通过 JavaScript,我们知道最终调用的函数是 greet,于是就在 wasm 里寻找 greet

在分析过程中,最需要注意的就是访存指令。想要生成 flag 就一定需要访问内存。并且由于我们 greet 传入的参数是字符串,因此获得这个字符串本身也需要经过内存。于是我们需要重点关注的就是访存指令:i32.load

greet 的源码如下:

我们在 i32.load 处打断点,观察执行前后栈的情况。首先是 0x06cde 行:

执行完这一行后的栈中存储的是 1114120,和 var0 的内容一致,即输入字符串的地址。

然后看 0x06ce5 行,这行执行完后栈中存储的是 4,和 var1 的内容一致,即输入字符串的长度。

虽然上面的步骤并没有发现有用的信息,但却是必不可少的。因此这里没有省略这些失败的尝试。

而接下来就是有用的了。后面跟着的就是一个 call 9,我们不妨直接跳过:

我们发现,直接弹出了 alert,说明主要的内容就在这个函数内。call 9 之后的内容都不需要深究了

进入 func9

进入 func9 之后我们依然是在 i32.load 相关的地方打断点。在 0x048cf 处,我们有了发现。

执行完这一行之后,栈中的内容为 1114184,查看内存:

再尝试变换为 ASCII 码:

试着找到字符串末尾:

就得到了这样一个 64 位的字符串:

IF 线:如果你会使用搜索引擎

其实这时候你就可以拿这个去搜了,可以得到这样的结果:

https://hashtoolkit.com/reverse-md5-hash/f7e0b956540676a129760a3eae309294
https://hashtoolkit.com/reverse-md5-hash/f7e0b956540676a129760a3eae309294

于是你会发现中间那一团 ASCII 码字符其实是 64+36 位的。你迅速猜测 64 位代表 sha25636 位代表 UUID。但当你试图提交的时候,却发现答案错误。

于是,你又回来了。

发挥作用的偏移

与此同时,在 0x048d9,我们看到了一个熟悉的数字:

这不就是 a995 的偏移吗!而在下面,我们发现了 1048713:Spirit 的偏移。

如果你看下去,会发现到了 0x049ef 就是我们熟悉的 alert 了:

因此关键的部分就在中间这一段:

关键逻辑分析

我们把这部分代码复制出来:

block $label3 (result i32)

block $label2

block $label0

block $label1

local.get $var2

i32.load offset=28

i32.const 64

i32.eq

if

local.get $var2

i32.load offset=24

local.tee $var0

i32.const 1048613

i32.eq

br_if $label0 ;; if $var0 == 1048613 -> goto $label0

local.get $var0

call $func54 ;; $func54($var0)

local.get $var2

i32.const 192

i32.add

call $func70 ;; $func70($var2 + 192)

br_if $label1 ;; return != 0 -> goto $label1

br $label2

end

local.get $var2

i32.const 192

i32.add

call $func70

end $label1

local.get $var2 ;; if br_if $label_1

i32.const 32

i32.add

br $label3 ;; force exit

end $label0

local.get $var2

i32.const 192

i32.add

call $func70

end $label2

local.get $var2 ;; if br $label2

i32.const 336

i32.add

call $func50

local.get $var2

i32.const 32

i32.add

call $func70

local.get $var2

i32.const 40

i32.add

local.get $var2

i32.const 344

i32.add

i32.load

i32.store

local.get $var2

local.get $var2

i64.load offset=336

i64.store offset=32

local.get $var2

i32.const 32

i32.add

end $label3

经过观察,我们发现:如果我们执行了 br_if $label1,那么直接就会跳出这一块的执行,因此我们尝试阻止其运行。暴力一点,我们直接把 br_if 注释掉:

local.get $var2

i32.const 192

i32.add

call $func70 ;; $func70($var2 + 192)

;; br_if $label1 ;; return != 0 -> goto $label1

br $label2

修改 WASM

修改的步骤需要将 WASM 转换为 wat,再回编译成 WASM。用到的工具分别是 wasm2watwat2wasm

对于执行,你可以选择将整个站点都下载到本地,也可以选择使用 ChromeOverride 功能。

结果

在你修改完成时,一切就都结束了——

这就是真正的 flag 了。这段 WASM 的实际源码如下:

mod utils;

use wasm_bindgen::prelude::*;

use sha2::{Sha256, Digest};

use hex::encode;

#[cfg(feature = "wee_alloc")]

#[global_allocator]

static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]

extern {

fn alert(s: &str);

}

#[wasm_bindgen]

pub fn greet(hint: &str) {

let mut content = String::from("do_not_submit-where_is_the_real_flag?");

// hint = 'you-can-never-know-what-it-is/www'

if hash_match(hint, "a9553e6a285a1f8e24167204d1ebb273cbe9254c6d4ad681dc1a2644e929da43") {

// 6351789a-2107-4796-9f51-ba7b8cb9d92e

content = rot13("6351789n-2107-4796-9s51-on7o8po9q92r");

}

let prefix = "Spirit{";

let postfix = "}";

let result = format!("{}{}{}", prefix, content, postfix);

alert(&result);

}

fn hash_match(s: &str, exp: &str) -> bool {

let mut hasher = Sha256::new();

hasher.update(s);

let result = hasher.finalize();

let hash = hex::encode(result);

return &hash == exp;

}

fn rot13(text: &str) -> String {

text.chars().map(|c| {

match c {

'A'...'M' | 'a'...'m' => ((c as u8) + 13) as char,

'N'...'Z' | 'n'...'z' => ((c as u8) - 13) as char,

_ => c

}

}).collect()

}

也就是对 flag 进行了简单的 rot13 操作。是不是签到题(逃