Skip to content

Instantly share code, notes, and snippets.

@yuvalif
Created June 10, 2022 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yuvalif/32ae6d3d57a010d6da4615d506d9c967 to your computer and use it in GitHub Desktop.
Save yuvalif/32ae6d3d57a010d6da4615d506d9c967 to your computer and use it in GitHub Desktop.
-- calculate entropy of an object
function object_entropy(full_name)
local byte_hist = {}
local byte_hist_size = 256
for i = 1,byte_hist_size do
byte_hist[i] = 0
end
local total = 0
for i, c in pairs(Data) do
local byte = c:byte() + 1
byte_hist[byte] = byte_hist[byte] + 1
total = total + 1
end
entropy = 0
for _, count in ipairs(byte_hist) do
if count ~= 0 then
local p = 1.0 * count / total
entropy = entropy - (p * math.log(p)/math.log(byte_hist_size))
end
end
return entropy
end
function detect_ransomware() [8/4770]
if Request.RGWOp ~= 'put_obj' then
-- calculate entropy only when uploading an object
RGWDebugLog("skipping entropy calculation for "..Request.RGWOp)
return
end
local full_name = Request.Bucket.Name.."\\"..Request.Object.Name
local upload_id = Request.HTTP.Parameters["uploadId"]
if upload_id ~= nil then
if RGW[full_name.."-upload-id"] == upload_id then
-- calculate entropy only on the first part of a large object
return
end
RGW[full_name.."-upload-id"] = upload_id
end
local new_entropy = object_entropy(full_name)
if new_entropy == 0 then
RGWDebugLog("no data in "..full_name)
return
end
local current_entropy = RGW[full_name.."-entropy"]
RGWDebugLog("current entropy of "..full_name.." is "..tostring(current_entropy))
if current_entropy ~= nil then
-- object with entropy already exists
RGWDebugLog("object "..Request.Object.Name.." updated. entropy changed from "..tostring(current_entropy).." to "..tostring(new_entropy))
local inc_threshold = 0.05 -- 5% increase in object entropy
local enc_threshold = 0.5 -- minimum entropy for encryption
local bucket_threshold = 0.2 -- if bucket has 20% encrypted files it is quarantines
local inc_rate = (new_entropy - current_entropy)/current_entropy
if inc_rate > inc_threshold and new_entropy > enc_threshold then
RGWDebugLog("entropy of "..full_name.." increased by "..tostring(inc_rate*100).."%")
if RGW[Request.Bucket.Name.."-enc-count"] == nil then
RGW[Request.Bucket.Name.."-enc-count"] = 1
else
RGW.increment(Request.Bucket.Name.."-enc-count")
end
enc_rate = RGW[Request.Bucket.Name.."-enc-count"]/RGW[Request.Bucket.Name.."-count"]
RGWDebugLog(tostring(enc_rate*100).."% of the objects in "..Request.Bucket.Name.." may be encrypted")
if enc_rate > bucket_threshold then
RGW[Request.Bucket.Name.."-quarantine"] = true
end
end
else
RGWDebugLog("new object "..full_name.." uploaded. entropy is "..tostring(new_entropy))
if RGW[Request.Bucket.Name.."-count"] == nil then
RGW[Request.Bucket.Name.."-count"] = 1
else
RGW.increment(Request.Bucket.Name.."-count")
end
end
RGW[full_name.."-entropy"] = new_entropy
end
detect_ransomware()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment