penguin.config_patchers module

penguin.config_patchers

Configuration patch generation utilities for the Penguin emulation environment.

This module provides classes and helpers for generating configuration patches, handling static and dynamic pseudofiles, network devices, library injection, NVRAM defaults, and other config modifications.

class penguin.config_patchers.BasePatch(arch_info, inits, kernel_versions)[source]

Bases: PatchGenerator

Generate base config for static_files and default plugins

Parameters:
  • arch_info (str)

  • inits (list)

  • kernel_versions (dict)

UNKNOWN_INIT: str = 'UNKNOWN_FIX_ME'
generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

set_arch_info(arch_identified)[source]

Set architecture info for config patch.

Parameters:

arch_identified (str) – Identified architecture string.

Return type:

None

class penguin.config_patchers.DeleteFiles(extract_dir)[source]

Bases: PatchGenerator

Delete some files we don’t want.

Parameters:

extract_dir (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.DynamicExploration[source]

Bases: PatchGenerator

We are dynamically evaluating and refining a configuration. We need to collect data programatically. Disable root shell, enable coverage-tracking and nmap for coverage generation. Enable VPN so nmap has something to talk to.

Ideally this will also be paired with ShimBusybox to get shell-level instrumentation.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.FileHelper[source]

Bases: object

static exists(tmp_dir, target)[source]

Check if the target exists within the extracted filesystem in tmp_dir, handling symlinks correctly.

Parameters:
  • tmp_dir (str) – The root of the extracted filesystem (e.g., /tmp/extracted)

  • target (str) – The target path to check (e.g., /foo/zoo)

Returns:

True if the target exists within tmp_dir, False otherwise

Return type:

bool

static find_executables(tmp_dir, target_dirs=None)[source]
Parameters:
  • tmp_dir (str)

  • target_dirs (set[str] | None)

static find_shell_scripts(tmp_dir)[source]
Parameters:

tmp_dir (str)

static find_strings_in_file(file_path, pattern)[source]
Parameters:
  • file_path (str)

  • pattern (str)

Return type:

list[str]

class penguin.config_patchers.ForceWWW(fs_path)[source]

Bases: PatchGenerator

This is a hacky FirmAE approach to identify webservers and just start them. Unsurprisingly, it increases the rate of web servers starting. We’ll export this into our static files section so we could later decide to try it. We’ll enable this by default here.

Parameters:

fs_path (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.GenerateMissingDirs(archive_path, archive_files)[source]

Bases: PatchGenerator

Examine the fs archive to identify missing directories We ignore the extracted filesystem because we want to ensure symlinks are handled correctly

Parameters:
  • archive_path (str)

  • archive_files (list)

TARGET_DIRECTORIES: list[str] = ['/proc', '/etc_ro', '/tmp', '/var', '/run', '/sys', '/root', '/tmp/var', '/tmp/media', '/tmp/etc', '/tmp/var/run', '/tmp/home', '/tmp/home/root', '/tmp/mnt', '/tmp/opt', '/tmp/www', '/var/run', '/var/lock', '/usr/bin', '/usr/sbin']
generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.GenerateMissingFiles(extract_dir)[source]

Bases: PatchGenerator

Ensure we have /bin/sh, /etc/TZ, /var/run/nvramd.pid, and localhost in /etc/hosts.

Parameters:

extract_dir (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.GenerateReferencedDirs(extract_dir)[source]

Bases: PatchGenerator

FirmAE “Boot mitigation”: find path strings in binaries, make their directories if they don’t already exist.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.GenerateShellMounts(extract_dir, existing)[source]

Bases: PatchGenerator

Ensure we have /mnt/* directories referenced by shell scripts.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.KernelModules(extract_dir, kernel_version)[source]

Bases: PatchGenerator

Create a symlink from the guest kernel module path to our kernel’s module path. (ie.., /lib/modules/1.2.0-custom -> /lib/modules/4.10.0)

Parameters:
  • extract_dir (str)

  • kernel_version (dict)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

static is_kernel_version(name)[source]
Parameters:

name (str)

Return type:

bool

static pad_kernel_version(ver)[source]
Parameters:

ver (str)

Return type:

str

class penguin.config_patchers.LibInjectFixedAliases[source]

Bases: PatchGenerator

Set all aliases in libinject from our defaults.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.LibInjectStringIntrospection(library_info)[source]

Bases: PatchGenerator

Add LibInject aliases for string introspection (e.g., for comparison detection). For each method we see in the filesystem that’s in our list of shim targets, add the shim

Parameters:

library_info (dict)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

Bases: PatchGenerator

Detect the ABI of all libc.so files and place a symlink in the same directory to lib_inject of the same ABI.

Parameters:

filesystem_root_path (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.LibInjectTailoredAliases(library_info)[source]

Bases: PatchGenerator

Set default aliases in libinject based on library analysis. If one of the defaults is present in a library, we’ll add it to the libinject alias list

Parameters:

library_info (dict)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.LinksysHack(extract_dir)[source]

Bases: PatchGenerator

Linksys specific hack from FirmAE with pseudofile model.

Parameters:

extract_dir (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ManualInteract[source]

Bases: PatchGenerator

Interactive for manual exploration. Enable root shell, enable vpn. Do not terminate on www bind.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NetdevsDefault[source]

Bases: PatchGenerator

Add list of default network device names.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NetdevsTailored(netdevs)[source]

Bases: PatchGenerator

Add list of network device names observed in static analysis.

Parameters:

netdevs (dict)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NvramConfigRecovery(extract_dir)[source]

Bases: PatchGenerator

Search for files that contain nvram keys and values to populate NVRAM defaults

Parameters:

extract_dir (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NvramConfigRecoveryWild(extract_dir)[source]

Bases: PatchGenerator

Search for files that contain nvram keys and values to populate NVRAM defaults. This version relaxes the search to allow for basename matches instead of full path matches.

Parameters:

extract_dir (str)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NvramDefaults[source]

Bases: PatchGenerator

Add default nvram values from Firmadyne and FirmAE

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NvramFirmAEFileSpecific(fs_path)[source]

Bases: PatchGenerator

Apply FW-specific nvram patches based on presence of hardcoded strings in files from FirmAE.

Parameters:

fs_path (str)

FIRMAE_TARGETS: dict[str, list[tuple[str, str]]] = {'./lib/libacos_shared.so': [('time_zone_x', '0')], './sbin/acos_service': [('rip_enable', '0')], './sbin/rc': [('ipv6_6to4_lan_ip', '2002:7f00:0001::')], './usr/sbin/httpd': [('rip_multicast', '0'), ('bs_trustedip_enable', '0'), ('filter_rule_tbl', '')]}
generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.NvramHelper[source]

Bases: object

static nvram_config_analysis(fs_path, full_path=True)[source]
Parameters:
  • fs_path (str)

  • full_path (bool)

Return type:

dict[str, str]

static parse_nvram_file(path, f)[source]

Parse a NVRAM file and return key-value pairs.

Parameters:
  • path (str) – Path to NVRAM file.

  • f – File object.

Returns:

Dictionary of key-value pairs.

Return type:

dict

class penguin.config_patchers.NvramLibraryRecovery(library_info)[source]

Bases: PatchGenerator

During static analysis the LibrarySymbols class collected key->value mappings from libraries exporting some common nvram defaults symbols (“Nvrams”, “router_defaults”) - add these to our nvram config if we have any.

TODO: if we find multiple nvram source files here, we should generate multiple patches. Then we should consider these during search. For now we just take non-conflicting values from largest to smallest source files. More realistic might be to try each file individually.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.PatchGenerator[source]

Bases: ABC

abstract generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.PseudofilesExpert[source]

Bases: PatchGenerator

Fixed set of pseudofile models from FirmAE.

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.PseudofilesTailored(pseudofiles)[source]

Bases: PatchGenerator

For all missing pseudofiles we saw referenced during static analysis, try adding them with a default model

Parameters:

pseudofiles (dict)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.RootShell[source]

Bases: PatchGenerator

Add root shell

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ShimBinaries(files)[source]

Bases: object

Identify binaries in the guest FS that we want to shim and add symlinks to go from guest bin -> igloo bin into our config.

make_shims(shim_targets)[source]
Parameters:

shim_targets (dict[str, str])

Return type:

dict

class penguin.config_patchers.ShimBusybox(files)[source]

Bases: ShimBinaries, PatchGenerator

Parameters:

files (list)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ShimCrypto(files)[source]

Bases: ShimBinaries, PatchGenerator

Parameters:

files (list)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ShimFwEnv(files)[source]

Bases: ShimBinaries, PatchGenerator

Replace fw_printenv/getenv/setenv with hypercall based alternatives Work in progress. Needs testing

Parameters:

files (list)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ShimNoModules(files)[source]

Bases: ShimBinaries, PatchGenerator

Parameters:

files (list)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.ShimStopBins(files)[source]

Bases: ShimBinaries, PatchGenerator

Parameters:

files (list)

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.SingleShot[source]

Bases: PatchGenerator

We are doing a single-shot, automated evaluation. Disable root shell, leave coverage/nmap, but keep VPN on and use fetch_web to collect responses

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.SingleShotFICD[source]

Bases: PatchGenerator

We are doing a single-shot, automated evaluation. Disable root shell, but keep VPN on and measure FICD

generate(patches)[source]

Generate a patch dictionary.

Parameters:

patches (dict) – Existing patches dictionary.

Returns:

Patch dictionary or None.

Return type:

dict or None

class penguin.config_patchers.TarHelper[source]

Bases: object

Collection of static method to help find files in a tar archive

static get_all_members(tarfile_path)[source]
Parameters:

tarfile_path (str)

static get_directory_members(tarfile_path)[source]
Parameters:

tarfile_path (str)

Return type:

set[str]

static get_file_members(tarfile_path)[source]
Parameters:

tarfile_path (str)

Return type:

set[str]

static get_other_members(tarfile_path)[source]
Parameters:

tarfile_path (str)

Parameters:

tarfile_path (str)

Return type:

dict[str, str]