Skip to content

Commit 6a60b18

Browse files
author
HackTricks News Bot
committed
Add content from: FuzzMe – Reverse Engineering and Fuzzing an Android Shared L...
1 parent 74f169f commit 6a60b18

File tree

1 file changed

+133
-1
lines changed

1 file changed

+133
-1
lines changed

src/binary-exploitation/common-exploiting-problems.md

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,139 @@ In order to bypass this the **escape character `\x16` must be prepended to any `
3535

3636
**Here you can** [**find an example of this behaviour**](https://ir0nstone.gitbook.io/hackthebox/challenges/pwn/dream-diary-chapter-1/unlink-exploit)**.**
3737

38-
{{#include ../banners/hacktricks-training.md}}
38+
## Android AArch64 shared-library fuzzing & LD_PRELOAD hooking
39+
40+
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:
41+
42+
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)`.
43+
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`).
44+
45+
<details>
46+
<summary>Minimal file-driven harness</summary>
47+
48+
```c
49+
#include <fcntl.h>
50+
#include <stdint.h>
51+
#include <stdio.h>
52+
#include <stdlib.h>
53+
#include <sys/stat.h>
54+
#include <unistd.h>
55+
56+
extern int validate(const uint8_t *buf, uint64_t len);
57+
58+
int main(int argc, char **argv) {
59+
if (argc < 2) return 1;
60+
int fd = open(argv[1], O_RDONLY);
61+
if (fd < 0) return 1;
62+
struct stat st = {0};
63+
if (fstat(fd, &st) < 0) return 1;
64+
uint8_t *buffer = malloc(st.st_size + 1);
65+
read(fd, buffer, st.st_size);
66+
close(fd);
67+
int ret = validate(buffer, st.st_size);
68+
free(buffer);
69+
return ret;
70+
}
71+
```
72+
73+
</details>
74+
75+
3. **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 == 84` ⇒ `value` must be `42`). Feeding syntactically valid JSON that progressively satisfies each branch lets you map the schema without instrumentation.
76+
4. **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:
77+
78+
<details>
79+
<summary>Minimal snprintf leak hook</summary>
80+
81+
```c
82+
#define _GNU_SOURCE
83+
#include <dlfcn.h>
84+
#include <stdarg.h>
85+
#include <stdio.h>
86+
#include <string.h>
87+
88+
typedef int (*vsnprintf_t)(char *, size_t, const char *, va_list);
89+
90+
int snprintf(char *str, size_t size, const char *fmt, ...) {
91+
static vsnprintf_t real_vsnprintf;
92+
if (!real_vsnprintf)
93+
real_vsnprintf = (vsnprintf_t)dlsym(RTLD_NEXT, "vsnprintf");
94+
95+
va_list args;
96+
va_start(args, fmt);
97+
va_list args_copy;
98+
va_copy(args_copy, args);
99+
if (fmt && strstr(fmt, "MHL{")) {
100+
fprintf(stdout, "[LD_PRELOAD] flag: ");
101+
vfprintf(stdout, fmt, args);
102+
fputc('\n', stdout);
103+
}
104+
int ret = real_vsnprintf(str, size, fmt, args_copy);
105+
va_end(args_copy);
106+
va_end(args);
107+
return ret;
108+
}
109+
```
110+
111+
112+
</details>
113+
114+
`LD_PRELOAD=./hook.so ./validate_harness payload.json` exfiltrates the internal flag and confirms the crash oracle without patching the binary.
115+
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.
116+
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.
117+
118+
<details>
119+
<summary>AFL-friendly harness for structured JSON</summary>
120+
121+
```c
122+
#include <stdint.h>
123+
#include <stdio.h>
124+
#include <string.h>
125+
#include <unistd.h>
126+
127+
extern int validate(unsigned char *bytes, size_t len);
128+
129+
#define FUZZ_SIZE 9
130+
131+
int main(void) {
132+
uint8_t blob[FUZZ_SIZE];
133+
if (read(STDIN_FILENO, blob, FUZZ_SIZE) != FUZZ_SIZE) return 0;
134+
char suffix[FUZZ_SIZE + 1];
135+
memcpy(suffix, blob, FUZZ_SIZE);
136+
suffix[FUZZ_SIZE] = '\0';
137+
char json[512];
138+
int len = snprintf(json, sizeof(json),
139+
"{\"magic\":16909060,\"version\":1,\"padding\":0,\"flag\":\"MHL{827b07c%s}\"," \
140+
"\"root\":{\"type\":16,\"level\":3,\"num_children\":1,\"children\":[" \
141+
"{\"type\":32,\"level\":2,\"num_children\":1,\"subchildren\":[" \
142+
"{\"type\":48,\"level\":1,\"num_children\":1,\"leaves\":[" \
143+
"{\"type\":64,\"level\":0,\"reserved\":0,\"value\":42}]}}]}}",
144+
suffix);
145+
if (len <= 0 || (size_t)len >= sizeof(json)) return 0;
146+
validate((unsigned char *)json, len);
147+
return 0;
148+
}
149+
```
39150
151+
</details>
40152
153+
7. **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.
41154
155+
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.
156+
157+
### Related pages
158+
159+
{{#ref}}
160+
../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
161+
{{#endref}}
162+
163+
{{#ref}}
164+
../reversing/reversing-tools-basic-methods/README.md
165+
{{#endref}}
166+
167+
## References
168+
169+
- [FD duplication exploit example](https://ir0nstone.gitbook.io/notes/types/stack/exploiting-over-sockets/exploit)
170+
- [Socat delete-character behaviour](https://ir0nstone.gitbook.io/hackthebox/challenges/pwn/dream-diary-chapter-1/unlink-exploit)
171+
- [FuzzMe – Reverse Engineering and Fuzzing an Android Shared Library](https://hackmd.io/@sal/fuzzme-mobilehackinglab-ctf-writeup)
172+
173+
{{#include ../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)