Skip to main content
  1. Posts/

Lua Programming Language: Basic Concepts and Syntax

··1496 words·8 mins·
Table of Contents

Lua doesn’t get anywhere near the attention Python gets in the security field, but it’s worth knowing. It’s small, fast, embeddable, and shows up in places you might not expect: Wireshark’s dissector scripts and Nmap’s scripting engine (NSE) are both built on it, alongside a long list of games and applications that use it for scripting and modding.

This post covers the basics, variables, operators, control structures, and functions, then moves into three practical examples: a port scanner, a password cracker, and a web crawler. It closes with an honest look at where Lua is actually a good choice for pen testing work and where it isn’t.

Basic concepts and syntax
#

Variables and data types
#

Like most programming languages, Lua lets you create variables to store data. Here’s an example:

-- create a variable named "name" and assign it the value "Alice"
name = "Alice"

Here, we’ve created a variable called “name” and assigned it the string value “Alice.” Lua doesn’t require you to declare a variable’s type; it infers the type from the value assigned to it.

Here are some examples of other data types in Lua:

-- integer
age = 25

-- floating point number
weight = 65.4

-- boolean (true/false)
is_student = true

-- table (similar to an array in other languages)
grades = {90, 85, 95, 87}

Operators
#

Lua supports the usual arithmetic, logical, and comparison operators. Here are some examples:

-- arithmetic operators
a = 10
b = 5
sum = a + b -- sum = 15
difference = a - b -- difference = 5
product = a * b -- product = 50
quotient = a / b -- quotient = 2

-- logical operators
c = true
d = false
result1 = c and d -- result1 = false
result2 = c or d -- result2 = true
result3 = not c -- result3 = false

-- comparison operators
e = 10
f = 5
result4 = e == f -- result4 = false
result5 = e ~= f -- result5 = true
result6 = e > f -- result6 = true
result7 = e <= f -- result7 = false

Control structures
#

Lua supports the usual control structures: if-then statements and loops. Here are some examples:

-- if-then statement
x = 10
if x > 5 then
  print("x is greater than 5")
else
  print("x is less than or equal to 5")
end

-- for loop
for i=1,5 do
  print(i)
end

-- while loop
j = 1
while j < 5 do
  print(j)
  j = j + 1
end

Functions
#

In Lua, you define functions to encapsulate blocks of code that can be reused throughout your program. Here’s an example:

function add(a, b)
  return a + b
end

result = add(3, 5) -- result = 8

Pen testing and red teaming with Lua
#

Lua’s small footprint and embeddability make it a reasonable choice for a handful of security tasks: network analysis, quick cracking scripts, and lightweight web scraping. Here are three worked examples.

Port scanner
#

A port scanner identifies open ports on a target host. In Lua, the LuaSocket library handles the TCP connection attempts:

-- load the socket library
local socket = require("socket")

-- define the IP address and port range to scan
local ip = "192.168.1.1"
local start_port = 1
local end_port = 1024

-- loop through each port in the range and attempt to connect
for port = start_port, end_port do
  local client = socket.tcp()
  client:settimeout(0.5)
  local ok, err = client:connect(ip, port)

  if ok then
    print("Port " .. port .. " is open")
  end
  client:close()
end

client:connect() returns a truthy value on success and nil plus an error message on failure, so if ok then is enough to detect an open port. Note that this scans sequentially and blocks for up to 0.5 seconds on every closed or filtered port, so a full 1024-port sweep can take a while; a real scanner would run connections concurrently with coroutines rather than one at a time.

Password cracker
#

Cracking a password hash means hashing candidate passwords and comparing them against the target hash until one matches. In Lua, the openssl binding provides the hashing:

-- load the openssl library
local openssl = require("openssl")

-- a deliberately short wordlist for this example; real dictionary
-- attacks use lists with millions of entries, like rockyou.txt
local wordlist = {"123456", "12345678", "qwerty", "letmein", "password", "admin"}

local user = "alice"
local target_hash = "5f4dcc3b5aa765d61d8327deb882cf99" -- MD5 of "password"

for _, candidate in ipairs(wordlist) do
  local candidate_hash = openssl.digest.digest("md5", candidate)

  if candidate_hash == target_hash then
    print("Password for user " .. user .. " is: " .. candidate)
    break
  end
end

This is a dictionary attack, not brute force: it hashes each word in the list with openssl.digest.digest() (which returns a hex string by default) and checks it against the target hash. Real password databases aren’t stored as bare MD5 anymore for exactly this reason. MD5 is fast, which is good for checksums and bad for password hashing, since a fast hash is a fast hash to crack too. Modern systems use bcrypt, scrypt, or Argon2, which are deliberately slow and salted to make this kind of attack impractical at scale.

Web crawler
#

A web crawler extracts information from web pages. In Lua, Lua-cURLv3 handles the HTTP request:

-- load the lua-curl library
local curl = require("cURL")

-- define the URL to crawl and the search term to look for
local url = "https://example.com"
local search_term = "example"

-- collect the response body as it streams in
local chunks = {}
local c = curl.easy()
c:setopt(curl.OPT_URL, url)
c:setopt(curl.OPT_FOLLOWLOCATION, true)
c:setopt(curl.OPT_WRITEFUNCTION, function(chunk)
  table.insert(chunks, chunk)
end)
c:perform()

local status = c:getinfo(curl.INFO_RESPONSE_CODE)
local html = table.concat(chunks)
c:close()

-- search the captured HTML for the search term
if status == 200 and string.find(html, search_term, 1, true) then
  print("Search term found on " .. url)
else
  print("Search term not found on " .. url)
end

The write callback fires once per chunk of the response as it arrives, so accumulating chunks into a table and joining them with table.concat() at the end is the standard way to capture the full body. c:getinfo(curl.INFO_RESPONSE_CODE) returns the HTTP status code, not the page content, which is a common mix-up when reading Lua-cURL examples for the first time.

Pros and cons of Lua for pen testers and red team members
#

Pros
#

  • Lightweight: Lua is small and fast enough to embed directly into other applications, which is exactly why tools like Wireshark and Nmap ship it as a scripting layer.
  • Easy to learn: the syntax is simple and the standard library is small, so there isn’t much to memorize before you’re productive.
  • Versatile: it covers network scripting, quick cracking tools, and lightweight scraping without much ceremony.

Cons
#

  • Limited libraries: Lua’s ecosystem is nowhere near as deep as Python’s for security-specific tooling. You’ll be writing more from scratch.
  • Small community: fewer people means fewer Stack Overflow answers and fewer maintained tools when something breaks.
  • Narrower use cases: it’s a fine choice for embedded scripting and quick utilities, but it’s not what you reach for on a large, complex assessment tool.

None of that is disqualifying. For quick scripts and automation tasks, Lua’s small footprint and simple syntax make it worth having in the toolkit alongside Python, especially anywhere you’re already working inside a Lua-scriptable tool.

Where have I seen Lua before?
#

Lua shows up in more security tooling than its low profile in the field would suggest.

The most direct case is network analysis: Wireshark uses Lua for dissector scripts that extend its packet-parsing capabilities, and Nmap’s scripting engine (NSE) is written in Lua, powering most of the vulnerability-detection and service-enumeration scripts that ship with the tool.

Lua also turns up constantly in game scripting and modding, from World of Warcraft addons to Garry’s Mod. That’s not a pen testing use case in itself, but it’s a reminder of how often Lua is the embedded scripting layer inside software you might later need to analyze or exploit.

A number of widely used applications embed Lua directly, including the VLC media player and, via the third-party ngx_lua module bundled by OpenResty, the nginx web server. You won’t necessarily interact with Lua directly in these, but knowing it’s there helps when you’re reverse engineering how the application actually works.

Beyond that, plenty of security professionals write their own one-off Lua scripts and tools, usually because they’re already working inside a Lua-scriptable environment and it’s less friction than reaching for a separate language.

Conclusion
#

This post covered Lua’s basics, variables, operators, control structures, functions, then walked through a port scanner, a password cracker, and a web crawler, and weighed where Lua fits against languages like Python and Ruby.

It’s not going to replace Python as your primary tool. But when you’re already inside Wireshark or Nmap, or you need something small enough to embed, Lua is worth having in your back pocket.

UncleSp1d3r
Author
UncleSp1d3r
As a computer security professional, I’m passionate about building secure systems and exploring new technologies to enhance threat detection and response capabilities. My experience with Rails development has enabled me to create efficient and scalable web applications. At the same time, my passion for learning Rust has allowed me to develop more secure and high-performance software. I’m also interested in Nim and love creating custom security tools.