目錄
競爭型漏洞
概念
常見類型及示例
環境搭建
?編輯漏洞復現
ucenter/1/
ucenter/2/
ucenter/3/
ucenter/4/
總結
悲觀鎖
樂觀鎖
競爭型漏洞
概念
競爭型漏洞,也稱為競態條件漏洞(Race Condition Vulnerability),是由于程序在處理共享資源時,未能正確同步多個并發操作,導致執行結果依賴于這些操作的時序,從而可能被攻擊者利用的安全漏洞。
假設有一個銀行應用,處理轉賬的時候,如果兩個轉賬操作同時進行,而余額檢查沒有正確同步,可能導致余額錯誤地被處理。例如,用戶A有100元,同時發起兩筆轉賬,各轉出100元,如果系統沒有正確鎖定資源,可能兩筆轉賬都通過,導致用戶A的余額變成負數,這就是競爭條件導致的漏洞。
常見類型及示例
-
TOCTOU(Time-of-Check to Time-of-Use)
-
原理:程序在檢查資源狀態后、使用資源前,攻擊者篡改資源。
-
示例:檢查文件權限后,攻擊者替換為惡意文件,導致高權限執行。
-
場景:文件系統操作、權限驗證。
-
-
資源爭用
-
原理:并發操作修改同一資源(如余額、庫存)時未同步。
-
示例:電商系統中,兩用戶同時購買最后一件商品,導致超賣。
-
場景:金融交易、庫存管理。
-
-
異步操作漏洞
-
原理:回調或事件處理順序不當引發狀態混亂。
-
示例:JavaScript中多個異步回調修改同一變量,導致數據錯誤。
-
場景:前端應用、分布式任務處理。
-
下面介紹一個關于競爭性的漏洞
環境搭建
可以在資源中下載文件解壓
將文件上傳到linux下,我們修改文件按中的錯誤,
將.env.defaul修改為.env
修改文件中的DEBUG=True
然后進行如下操作
pip3 install -r requirements.txt python3 manage.py migratepython3 manage.py collectstaticpython3 manage.py createsuperuserpython3 manage.py runserver 0.0.0.0:8080
完成之后可以訪問網站
漏洞復現
ucenter/1/
無鎖無事務時的競爭攻擊
class WithdrawView1(BaseWithdrawView):success_url = reverse_lazy('ucenter:withdraw1')def form_valid(self, form):amount = form.cleaned_data['amount']self.request.user.money -= amountself.request.user.save()models.WithdrawLog.objects.create(user=self.request.user, amount=amount)return redirect(self.get_success_url())
利用bp抓包,當出現兩次以上的扣款成功,就說明漏洞利用成功
競爭成功
查看日志?
ucenter/2/
無鎖有事務時的競爭攻擊
class WithdrawView2(BaseWithdrawView):success_url = reverse_lazy('ucenter:withdraw2')@transaction.atomicdef form_valid(self, form):amount = form.cleaned_data['amount']self.request.user.money -= amountself.request.user.save()models.WithdrawLog.objects.create(user=self.request.user, amount=amount)return redirect(self.get_success_url())
繼續競爭
?查看日志
競爭成功。
ucenter/3/
悲觀鎖加事務防御Race Condition
class WithdrawView3(BaseWithdrawView):success_url = reverse_lazy('ucenter:withdraw3')def get_form_kwargs(self):kwargs = super().get_form_kwargs()kwargs['user'] = self.userreturn kwargs@transaction.atomicdef dispatch(self, request, *args, **kwargs):self.user = get_object_or_404(models.User.objects.select_for_update().all(), pk=self.request.user.pk)return super().dispatch(request, *args, **kwargs)def form_valid(self, form):amount = form.cleaned_data['amount']self.user.money -= amountself.user.save()models.WithdrawLog.objects.create(user=self.user, amount=amount)return redirect(self.get_success_url())
?發現只有一次提款成功
ucenter/4/
樂觀鎖加事務防御Race Condition
class WithdrawView4(BaseWithdrawView):success_url = reverse_lazy('ucenter:withdraw4')@transaction.atomicdef form_valid(self, form):amount = form.cleaned_data['amount']rows = models.User.objects.filter(pk=self.request.user, money__gte=amount).update(money=F('money')-amount)if rows > 0:models.WithdrawLog.objects.create(user=self.request.user, amount=amount)return redirect(self.get_success_url())
發現也只有一次成功
總結
悲觀鎖
概念
-
悲觀鎖假設并發沖突一定會發生,因此在訪問數據時直接加鎖,確保其他事務無法修改數據,直到當前事務完成。
-
悲觀鎖通常通過數據庫的鎖機制實現,如
SELECT ... FOR UPDATE
或SELECT ... LOCK IN SHARE MODE
。
特點
-
優點:保證數據的一致性,適合寫操作多的場景。
-
缺點:加鎖會降低并發性能,可能導致死鎖。
使用場景
-
高并發寫操作。
-
數據一致性要求高的場景。
實例
該例子需要實現一個轉賬操作,確保余額不會出現負數。
在 SELECT ... FOR UPDATE
中,賬戶A的記錄被鎖定,其他事務無法修改,直到當前事務提交或回滾。
-- 開啟事務
START TRANSACTION;-- 使用悲觀鎖鎖定賬戶A的記錄
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;-- 檢查余額是否足夠
IF (SELECT balance FROM accounts WHERE id = 1) >= 100 THEN-- 扣除賬戶A的余額UPDATE accounts SET balance = balance - 100 WHERE id = 1;-- 增加賬戶B的余額UPDATE accounts SET balance = balance + 100 WHERE id = 2;
END IF;-- 提交事務
COMMIT;
樂觀鎖
概念
-
樂觀鎖假設并發沖突很少發生,因此在訪問數據時不加鎖,而是在提交時檢查數據是否被其他事務修改過。
-
樂觀鎖通常通過版本號(Version)或時間戳(Timestamp)實現。
特點
-
優點:提高并發性能,適合讀操作多的場景。
-
缺點:如果沖突頻繁,會導致大量事務回滾。
使用場景
-
高并發讀操作。
-
沖突較少的場景。
實例
在樂觀鎖中,通過 version
字段檢查數據是否被修改。如果 version
不匹配,說明數據已被其他事務修改,當前事務需要回滾并重試。
即,當用戶A進行提款操作時,若存在兩個以上的請求進程(查詢的version為1),當某一個進程率先請求成功,version會自增1,其他的進程就無法查詢到該版本號,從而不會執行賬戶操作,事務回滾。
-- 開啟事務
START TRANSACTION;-- 查詢賬戶A的余額和版本號
SELECT balance, version FROM accounts WHERE id = 1;-- 假設查詢到的 balance = 500, version = 1
-- 檢查余額是否足夠
IF 500 >= 100 THEN-- 扣除賬戶A的余額,并更新版本號UPDATE accounts SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = 1;-- 檢查是否更新成功IF ROW_COUNT() = 0 THEN-- 版本號不匹配,說明數據已被其他事務修改,回滾事務ROLLBACK;ELSE-- 增加賬戶B的余額UPDATE accounts SET balance = balance + 100 WHERE id = 2;-- 提交事務COMMIT;END IF;
END IF;