Typing AST nodes
Due to its architecture, ending has great support for typing. This provides the following advantages:
- Fetch back results in their original type: if the column you're dumping is of type
BOOL, you get a boolean. If it is aBLOB, you get bytes. - Improve injection logic: different types mean different injection logic. Fetching a boolean value using a blind SQL injection requires 1 query, while fetching a string requires to know the length and dump character after character.
- Use statistics: if we dumped 10 rows for column
passwordand they all have a length of 32 characters, chances are the next rows are also going to be 32 characters long. - Bounds: set minimum and maximum values for an integer, charset or length for a string, etc.
Import AST nodes and types using:
from ending.ast import *
Setting a type
To associate a node with a type, either set the type in the constructor, or use the with_type() method.
# ID is a positive integer
id = Identifier("id", type=IntType(min=0))
# ID is between 0 and 100
id = id.with_type(IntType(min=0, max=100))
# Password is 32 characters long and stored as hexadecimal
password = Identifier(
"password",
type=TextType(size=32, charset="1234567890abcdef")
)
Types are automatically computed from the node if possible. This works for obvious cases, such as hardcoded values:
# TextType(charset='helo wrd', size=IntType(min=11, max=11))
Value("hello").metadata.type
# IntType(min=123, max=123)
Value(123).metadata.type
But also for more complex types: here, since password is garantied to be 32 characters long, it necessarily has a first character, and the charset is the same as the identifier's:
# TextType(
# charset='1234567890abcdef',
# size=IntType(min=1, max=1)
# )
Substring(password, 0, 1).metadata.type
Types
| Node | Description |
|---|---|
UnknownType |
type not yet determined |
BoolType |
boolean (0 / 1) |
IntType |
integer with optional min/max bounds |
TextType |
string with charset and size hints |
BlobType |
binary data with byteset and size hints |
IntType, TextType, and BlobType carry statistical hints that guide blind injection (e.g. which characters to test first). The feedback(value) method on a type instance records obtained values so the framework can adapt.
BoolType
Represents boolean types, such as BOOL, BOOLEAN.
IntType
Represents integer types, such as INT, INTEGER.
The type consists of minimum and maximum values.
# A numeric row ID
id_type = IntType(min=0)
# Timestamp from year 2000 to year 2050
timestamp_type = IntType(min=946684800, max=2524608000)
TextType
Represent text-based types, such as VARCHAR, TEXT.
The type consists of a length (size), which can be either a raw value, or bounds, and a charset.
# Type representing an MD5 hash:
md5_type = TextType(size=32, charset="1234567890abcdef")
The length is actually an IntType, and as such can be a range:
# Type representing an email column, whose size is between 3 and 256
email_type = TextType(
size=IntType(min=3, max=256),
charset="a..<cut for brievity>..zA...Z0-9@.-"
)
BlobType
Represent bytes-based types, such as BLOB or BYTEA.
The type consists of a length (size), which can be either a raw value, or bounds, and a byteset.
some_type = BlobType(size=32, byteset=b"123456789")