210 lines
6.8 KiB
Lua
210 lines
6.8 KiB
Lua
--[[
|
|
Eris Default Script
|
|
|
|
This script demonstrates how to use the Eris Lua API to customize
|
|
the tarpit's behavior, and will be loaded by default if no other
|
|
scripts are loaded.
|
|
|
|
Available events:
|
|
- connection: When a new connection is established
|
|
- request: When a request is received
|
|
- response_gen: When generating a response
|
|
- response_chunk: Before sending each response chunk
|
|
- disconnection: When a connection is closed
|
|
- block_ip: When an IP is being considered for blocking
|
|
- startup: When the application starts
|
|
- shutdown: When the application is shutting down
|
|
- periodic: Called periodically
|
|
|
|
API Functions:
|
|
- eris.debug(message): Log a debug message
|
|
- eris.info(message): Log an info message
|
|
- eris.warn(message): Log a warning message
|
|
- eris.error(message): Log an error message
|
|
- eris.set_state(key, value): Store persistent state
|
|
- eris.get_state(key): Retrieve persistent state
|
|
- eris.inc_counter(key, [amount]): Increment a counter
|
|
- eris.get_counter(key): Get a counter value
|
|
- eris.gen_token([prefix]): Generate a unique token
|
|
- eris.timestamp(): Get current Unix timestamp
|
|
--]]
|
|
|
|
-- Called when the application starts
|
|
eris.on("startup", function(ctx)
|
|
eris.info("Initializing default script")
|
|
|
|
-- Initialize counters
|
|
eris.inc_counter("total_connections", 0)
|
|
eris.inc_counter("total_responses", 0)
|
|
eris.inc_counter("blocked_ips", 0)
|
|
|
|
-- Initialize banned keywords
|
|
eris.set_state("banned_keywords", "eval,exec,system,shell,<?php,/bin/bash")
|
|
end)
|
|
|
|
-- Called for each new connection
|
|
eris.on("connection", function(ctx)
|
|
eris.inc_counter("total_connections")
|
|
eris.debug("New connection from " .. ctx.ip)
|
|
|
|
-- You can reject connections by returning false
|
|
-- This example checks a blocklist
|
|
local blocklist = eris.get_state("manual_blocklist") or ""
|
|
if blocklist:find(ctx.ip) then
|
|
eris.info("Rejecting connection from manually blocked IP: " .. ctx.ip)
|
|
return false
|
|
end
|
|
|
|
return true -- accept the connection
|
|
end)
|
|
|
|
-- Called when generating a response
|
|
eris.on("response_gen", function(ctx)
|
|
eris.inc_counter("total_responses")
|
|
|
|
-- Generate a unique traceable token for this request
|
|
local token = eris.gen_token("ERIS-")
|
|
|
|
-- Add some believable but fake honeytokens based on the request path
|
|
local enhanced_content = ctx.content
|
|
|
|
if ctx.path:find("wp%-") then
|
|
-- For WordPress paths
|
|
enhanced_content = enhanced_content
|
|
.. "\n<!-- WordPress Debug: "
|
|
.. token
|
|
.. " -->"
|
|
.. "\n<!-- WP_HOME: http://stop.crawlingmysite.com/wordpress -->"
|
|
.. "\n<!-- DB_USER: wp_user_"
|
|
.. math.random(1000, 9999)
|
|
.. " -->"
|
|
elseif ctx.path:find("phpunit") or ctx.path:find("eval") then
|
|
-- For PHP exploit attempts
|
|
-- Turns out you can just google "PHP error log" and search random online forums where people
|
|
-- dump their service logs in full.
|
|
enhanced_content = enhanced_content
|
|
.. "\nPHP Notice: Undefined variable: _SESSION in /var/www/html/includes/core.php on line 58\n"
|
|
.. "Warning: file_get_contents(): Filename cannot be empty in /var/www/html/vendor/autoload.php on line 23\n"
|
|
.. "Token: "
|
|
.. token
|
|
.. "\n"
|
|
elseif ctx.path:find("api") then
|
|
-- For API requests
|
|
local fake_api_key =
|
|
string.format("ak_%x%x%x", math.random(1000, 9999), math.random(1000, 9999), math.random(1000, 9999))
|
|
|
|
enhanced_content = enhanced_content
|
|
.. "{\n"
|
|
.. ' "status": "warning",\n'
|
|
.. ' "message": "Test API environment detected",\n'
|
|
.. ' "debug_token": "'
|
|
.. token
|
|
.. '",\n'
|
|
.. ' "api_key": "'
|
|
.. fake_api_key
|
|
.. '"\n'
|
|
.. "}\n"
|
|
else
|
|
-- For other requests
|
|
enhanced_content = enhanced_content
|
|
.. "\n<!-- Server: Apache/2.4.41 (Ubuntu) -->"
|
|
.. "\n<!-- Debug-Token: "
|
|
.. token
|
|
.. " -->"
|
|
.. "\n<!-- Environment: staging -->"
|
|
end
|
|
|
|
-- Track which honeytokens were sent to which IP
|
|
local honeytokens = eris.get_state("honeytokens") or "{}"
|
|
local ht_table = {}
|
|
|
|
-- This is a simplistic approach - in a real script, you'd want to use
|
|
-- a proper JSON library to handle this correctly
|
|
if honeytokens ~= "{}" then
|
|
-- Simple parsing of the stored data
|
|
for ip, tok in honeytokens:gmatch('"([^"]+)":"([^"]+)"') do
|
|
ht_table[ip] = tok
|
|
end
|
|
end
|
|
|
|
ht_table[ctx.ip] = token
|
|
|
|
-- Convert back to a simple JSON-like string
|
|
local new_tokens = "{"
|
|
for ip, tok in pairs(ht_table) do
|
|
if new_tokens ~= "{" then
|
|
new_tokens = new_tokens .. ","
|
|
end
|
|
new_tokens = new_tokens .. '"' .. ip .. '":"' .. tok .. '"'
|
|
end
|
|
new_tokens = new_tokens .. "}"
|
|
|
|
eris.set_state("honeytokens", new_tokens)
|
|
|
|
return enhanced_content
|
|
end)
|
|
|
|
-- Called before sending each chunk of a response
|
|
eris.on("response_chunk", function(ctx)
|
|
-- This can be used to alter individual chunks for more deceptive behavior
|
|
-- For example, to simulate a slow, unreliable server
|
|
|
|
-- 5% chance of "corrupting" a chunk to confuse scanners
|
|
if math.random(1, 100) <= 5 then
|
|
local chunk = ctx.content
|
|
if #chunk > 10 then
|
|
local pos = math.random(1, #chunk - 5)
|
|
chunk = chunk:sub(1, pos) .. string.char(math.random(32, 126)) .. chunk:sub(pos + 2)
|
|
end
|
|
return chunk
|
|
end
|
|
|
|
return ctx.content
|
|
end)
|
|
|
|
-- Called when deciding whether to block an IP
|
|
eris.on("block_ip", function(ctx)
|
|
-- You can override the default blocking logic
|
|
|
|
-- Check for potential attackers using specific patterns
|
|
local banned_keywords = eris.get_state("banned_keywords") or ""
|
|
local user_agent = ctx.user_agent or ""
|
|
|
|
-- Check if user agent contains highly suspicious patterns
|
|
for keyword in banned_keywords:gmatch("[^,]+") do
|
|
if user_agent:lower():find(keyword:lower()) then
|
|
eris.info("Blocking IP " .. ctx.ip .. " due to suspicious user agent: " .. keyword)
|
|
eris.inc_counter("blocked_ips")
|
|
return true -- Force block
|
|
end
|
|
end
|
|
|
|
-- For demonstration, we'll be more lenient with 10.x IPs
|
|
if ctx.ip:match("^10%.") then
|
|
-- Only block if they've hit us many times
|
|
return ctx.hit_count >= 5
|
|
end
|
|
|
|
-- Default to the system's threshold-based decision
|
|
return nil
|
|
end)
|
|
|
|
-- The enhance_response is now legacy, and I never liked it anyway. Though let's add it here
|
|
-- for the sake of backwards compatibility.
|
|
function enhance_response(text, response_type, path, token)
|
|
local enhanced = text
|
|
|
|
-- Add token as a comment
|
|
if response_type == "php_exploit" then
|
|
enhanced = enhanced .. "\n/* Token: " .. token .. " */\n"
|
|
elseif response_type == "wordpress" then
|
|
enhanced = enhanced .. "\n<!-- WordPress Debug Token: " .. token .. " -->\n"
|
|
elseif response_type == "api" then
|
|
enhanced = enhanced:gsub('"status": "[^"]+"', '"status": "warning"')
|
|
enhanced = enhanced:gsub('"message": "[^"]+"', '"message": "API token: ' .. token .. '"')
|
|
else
|
|
enhanced = enhanced .. "\n<!-- Debug token: " .. token .. " -->\n"
|
|
end
|
|
|
|
return enhanced
|
|
end
|