Taint sources

A taint source is a place where untrusted data can enter a program, such as the network, a file or the command line. This page lists every source, what it watches and how to switch it on.

What a source is

A source is a place where data from outside enters your program. When a source is on, EyalSec marks the data arriving through it as untrusted (taint) and follows it. If that data reaches a risky operation, you get an event. A source that is off marks nothing.

Every source is off until you switch it on, so a new machine reports nothing until you choose at least one. Start with the source that matches how your program receives input: socket for a network service, stdin for a filter or pipe, file for something that reads uploaded or downloaded files.

Switching a source on

The usual way to switch a source on is the dashboard: its control in the Taint sources part of the Configure window for one machine, or of the Filters page for every machine. Each control can be On, Off or Unset (see Unset).

es-python also accepts a command-line flag and an environment variable for most sources, which is handy for a single run:

es-python --make-socket-vuln app.py
ES2_MAKE_SOCKET_VULN=1 es-python app.py

A dashboard On or Off always wins over a flag. A flag only counts while the dashboard control is Unset at both the machine and the global level. The full list of flags is on the es-python command line.

A change to a source takes effect the next time the program starts. See When a change reaches the machine.

Which products use which sources

Each product shows only the sources it can use, so the list you see depends on the machine's product. A note under the title of the Configure window or the Filters page says which controls a product does not act on yet.

Product Sources shown
es-python socket, file, stdin, foreign, env, make_vuln, argv, weak_random, hardcoded, identity, db_rows, handoff
es-chromium none: it does not fetch machine configuration

On es-python, the argv, weak_random, hardcoded and identity controls do not change what it reports today. Use the flags described under each of them below instead.

Source names in rules

A source's name in the Taint sources list is not always the name you pick in a rule's Source field. Two sources are spelled differently, and a rule that uses the wrong spelling is rejected.

In Taint sources In a rule's Source
weak_random (underscore) weak-random (hyphen)
db_rows db

Every other source uses the same word in both places. The Source dropdown in the rules table already uses the rule spelling, so this matters mostly when you use the API.

A rule's Source list also offers a few names that have no control of their own, because they are switched on by another source. See Sources that only appear in rules.

socket: network data

socket marks data your program receives from another computer: anything read from a network connection, including the plain text read out of an encrypted (TLS) connection. It is the source most network services need.

On es-python, the data returned by the socket receive calls (recv, recvfrom, recv_into, recvmsg) and by the TLS socket read methods is marked.

  • Switch on: the socket control, or --make-socket-vuln / ES2_MAKE_SOCKET_VULN=1.
  • Origins you will see: socket:fd, socket:connected, socket:unbound; TLS reads show ssl:fd, and a memory-mapped socket shows mmap-socket:fd.
import socket
s = socket.create_connection(("example.com", 80))
s.sendall(b"GET / HTTP/1.0\r\n\r\n")
data = s.recv(4096)        # untrusted, origin socket:connected

Limits. When socket is the only source switched on, data read with a raw os.read() on a socket is not marked; switching on any other source as well restores it. Older socket-only machines can use this source and no other.

file: data read from files

file marks the contents of files your program opens and reads from disk. Use it for programs that process uploaded, downloaded or user-supplied files.

On es-python, the bytes returned by open(path).read(), os.read(), os.pread() and memory-mapping a file are marked, when they come from a regular file.

  • Switch on: the file control, or --make-file-vuln / ES2_MAKE_FILE_VULN=1.
  • Origins you will see: fd:posix_read, fd:posix_pread, fileio.read(candidate), fileio.readall(candidate); a memory-mapped file shows mmap-file:fd.
ES2_MAKE_FILE_VULN=1 es-python -c 'print(open("/etc/hostname").read())'
# the file contents are untrusted, origin fileio.readall(candidate)

Limits. Checking each read adds some cost to programs that read many files.

stdin: standard input

stdin marks data fed to your program on standard input: text piped into it or typed at the keyboard. Use it for command-line tools and filters that read their input this way.

On es-python, the string returned by input() and reads from sys.stdin are marked.

  • Switch on: the stdin control, or --make-stdin-vuln / ES2_MAKE_STDIN_VULN=1.
  • Origins you will see: stdin:input (from input()) and stdin (from reads on sys.stdin).
name = input("name? ")           # untrusted, origin stdin:input
blob = sys.stdin.buffer.read()   # untrusted, origin stdin

foreign: code other users can change

foreign reports when your program loads code from a file that another user on the machine can write to. An attacker who can edit such a file can run their own code inside your program. Unlike the other sources, it reports the loading itself, even while the program is starting, not a flow of data.

On es-python this covers importing a module, running the main script, and compile, exec or eval of a file's contents, whenever that file is writable by another user.

  • Switch on: the foreign control, or --make-foreign-vuln / ES2_MAKE_FOREIGN_VULN=1. It is not included in --make-everything-vuln.
  • Event you will see: foreign-code: followed by the file's path, once per file per process, so an import loop does not flood you. Under a Raise rule the load is stopped.
  • Side effect: on es-python, switching foreign on also switches socket on, unless you set socket to Off.
  • Shadowed modules: es-python also reports a standard-library module that was replaced by a file of the same name somewhere else (event shadow import, rule source shadow-import).

To trust some locations on purpose, such as a shared tools directory your team maintains, list their absolute path prefixes in ES2_FOREIGN_CODE_ALLOW, separated by colons. Nothing is exempt automatically.

ES2_MAKE_FOREIGN_VULN=1 ES2_FOREIGN_CODE_ALLOW=/opt/team-tools:/srv/shared es-python app.py
# importing /tmp/evil.py (writable by another user) -> event foreign-code:/tmp/evil.py

env: environment variables

env marks the values of environment variables your program reads. Whoever starts a process controls its environment, so a value read from it is untrusted when something else builds that environment from user input.

On es-python, values read from os.environ or os.getenv() are marked, and so are the command-line arguments in sys.argv. Only the value is marked, never the variable name.

  • Switch on: the env control, or --make-env-vuln / ES2_MAKE_ENV_VULN=1.
  • Origins you will see: env for environment values and argv for command-line arguments.
import os, sys
token  = os.environ["API_TOKEN"]   # untrusted, origin env
target = sys.argv[1]               # untrusted, origin argv

argv: command-line arguments

argv marks the command-line arguments a program was started with. They are attacker-controlled when another program builds the command line from user input, and fully trusted when a person typed them.

On es-python, arguments are covered by the env source: switch on env to track them. The argv control has no effect on an es-python machine.

make_vuln: data you mark yourself

make_vuln lets your own code mark a value as untrusted, by calling the make_vuln() builtin on it. It is mainly a testing tool: mark a value, pass it through your code, and see which risky operations it reaches.

import os
payload = make_vuln("hello; id")
os.system("echo " + payload)     # event: untrusted data reached os system
  • Switch on: set the make_vuln control to On. There is no flag: while it is Unset or Off, make_vuln() does nothing and returns its argument unchanged.
  • Custom numbers: make_vuln(value, 42) tags the value with your own number, so a rule can target it. Choose Custom number… in a rule's Source field and enter the number. See Source.

weak_random: predictable random numbers

weak_random marks values from a random number generator that is not meant for security. Such values are fine for shuffling or jitter, but predictable to an attacker when used as a session token or a password reset link. The event is reported where the value is used, not where it was generated.

On es-python this covers random.getrandbits (which the rest of the random module uses) and uuid1. uuid4 is not included, because it uses the operating system's secure generator.

  • Switch on: --make-weakrandom-vuln or ES2_MAKE_WEAKRANDOM_VULN=1, or --make-everything-vuln. The dashboard control does not reach es-python yet.
  • In a rule: spell it weak-random (see Source names in rules).

hardcoded: credentials in source code

hardcoded finds credentials, API keys and encryption keys written directly into your source code, and reports where they are used. It is the one source with a steady cost even when it finds nothing, so leave it off unless you are looking for this.

  • Switch on: --make-secret-vuln or ES2_MAKE_SECRET_VULN=1, or --make-everything-vuln. The dashboard control does not reach es-python yet.

db_rows: data read back from a database

db_rows marks values your program reads back out of a database. Data your own application stored is still attacker data: it is how stored cross-site scripting and second-order SQL injection reach a risky operation, long after the request that planted them.

It marks a lot of data on a busy application, which is why it is a source of its own rather than part of socket.

  • Switch on: the db_rows control, or --make-db-rows-vuln / ES2_MAKE_DB_ROWS_VULN=1, or --make-everything-vuln.
  • Covers on es-python: the built-in sqlite3 module and the PostgreSQL, MySQL, MariaDB and Oracle drivers that ship with es-python.
  • In a rule: spell it db (see Source names in rules).

identity: database identity checks

identity is not a source of untrusted data. It is a switch for a check of your database queries: it compares the schema with the values bound to each query and reports a username stored in one form but looked up in another, or a check-then-insert race on a column with no unique constraint.

It is shown for es-python only, is off by default, and does not change what es-python reports today.

handoff: follow a flow into the browser

handoff is not a source either. When it is on, a monitored web service records which parts of each response came from that visitor's own input, so the EyalSec browser (es-chromium) can keep following that data in the page and report reflected and stored cross-site scripting.

It marks nothing, so no event is ever attributed to it and no rule can target it. It is shown for es-python, is off by default, and has no flag: only setting it to On switches it on. Turn it on only for a machine you are actively investigating, because it adds a little information to every response the service sends.

Turn every source on at once

es-python can switch on several sources for one run with a single flag: --make-everything-vuln, or ES2_MAKE_EVERYTHING_VULN=1. It covers socket, file, stdin, env, weak_random, hardcoded and db_rows.

It does not include foreign or make_vuln; switch those on separately. Like any flag, it only applies to sources whose dashboard control is Unset: a source set to Off stays off.

es-python --make-everything-vuln app.py

In the dashboard, the equivalent is Set all sources: All On (see Set all sources). It covers the Taint sources section only, not the switches under Other.

Sources that only appear in rules

A few names appear in a rule's Source list but have no control in Taint sources, because they are switched on by another source or by a flag. You can still target them with a rule.

Rule source Switched on by
ssl-unverified socket (data from a TLS peer whose certificate was not checked)
shadow-import foreign (es-python)
fuzzer the fuzzing flags (es-python)
db db_rows

Something unclear or missing on this page? Email support@eyalsec.com.

EyalSec Pricing Docs Security Contact Login Book a live demo